View Full Version : [C linux] fork in nuova console
magnamel
30-08-2011, 21:21
Salve a tutti
ho il seguente rompicapo. Ho fatto un programmino semplice per imparare le fork ma vorrei far in modo che il processo figlio venga eseguito in un nuovo terminale. Attenzione il processo padre e figlio non sono eseguiti in maniera concorrenziale; il padre attende la fine dell'esecuzione del figlio.
Ho provato a girare su internet e diverse soluzioni non mi hanno portato a nulla.
Ad esempio nel codice filgio ho eseguito un system("gnome-terminal"); che mi apre solo un nuovo terminale mentre il mio intento e' quello di eseguire il processo filgio nel nuovo terminale (basta anche solamente una banale printf).
Qualche ideia.
Grazie a tuti
AnonimoVeneziano
30-08-2011, 22:17
Mmm beh, quello che vuoi fare è anomalo. Purtroppo non credo ci sia un modo semplicissimo per farlo.
Comunque l'output di un terminale in genere è collegato a un file su disco , scrivendo su quel file scrivi nel terminale. Puoi sapere qual'è il file collegato ad un certo terminale usando il comando "tty"
[hades@artemis ~]$ tty
/dev/pts/1
[hades@artemis ~]$
Per il mio primo terminale è /dev/pts/1 , scrivendo in questo file ho :
[hades@artemis ~]$ echo "ciao" > /dev/pts/1
ciao
[hades@artemis ~]$
Quindi se vuoi scrivere in un altro terminale da un programma con printf basta che associ il file descriptor del file /dev/pts/1 allo standard output oppure usi una funzione per il quale puoi specificare il file, come fprintf.
Rimane il problema di come sapere il nome del file collegato al terminale aperto dal programma . O ce lo infili fisso dentro nel programma (ma visto che cambia a seconda del terminale non è una buona idea) o lo ottieni dinamicamente dal programma , ma è un po' più complicato (dovresti forkare, ridirezionare lo standard output del figlio in una pipe collegata al padre, fare execv sul programma "/usr/bin/tty" e la stringa che arriva dall'altro capo della pipe dovrebbe essere il file in questione). Forse visto che sei all'inizio ti conviene comunque infilarlo direttamente dentro nel programma :)
In ordine quello che dovresti fare è:
- forkare il programma e nel figlio fare "execv()" di gnome-terminal per aprire un nuovo terminale.
- nel padre del precedente fork apri il file relativo al nuovo terminale aperto (se hai solo due terminali aperti nel server X in genere uno è /dev/pts/1 e l'altro è /dev/pts/2 quindi il file da aprire dovrebbe essere /dev/pts/2 ).
- forki di nuovo il programma e nel figlio duplichi il file descriptor del nuovo terminale sostituendo il file descriptor dello standard output (puoi farlo con "dup2( new_terminal_fid, 1);" dove new_terminal_fid è la variabile che contiene il file descriptor aperto del file /dev/pts/2 ottenuto dalla "open()" )
- a questo punto nel figlio se chiami "printf()" dovrebbe scrivere nel nuovo terminale.
Ciao
magnamel
30-08-2011, 22:51
Grazie per la risposta
Domani a mente lucida riflettero' su quello che hai detto.
A grandi linee ho capito devo "solo" trasformarlo in codice.
Grazie ancora
magnamel
03-09-2011, 21:34
o lo ottieni dinamicamente dal programma , ma è un po' più complicato (dovresti forkare, ridirezionare lo standard output del figlio in una pipe collegata al padre, fare execv sul programma "/usr/bin/tty" e la stringa che arriva dall'altro capo della pipe dovrebbe essere il file in questione)
Sto provando in questo modo ma la stringa che mi restituisce e' lo stesso terminale in cui si trova anche il padre (/dev/pts/0).
Il file descriptor della console di gnome-terminal non va bene, perche' e' la stessa del padre (e' l'output del programma gnome-terminal, non dei programmi che quest'ultimo fa girare).
Vedo due alternative semplici, che coinvolgono l'esecuzione di un piccolo script da parte di gnome-terminal (io usero' piu' sotto xterm, ma non cambia tanto la cosa).
La prima e' quella di far partire il terminale e fargli eseguire il comando "tty", salvarlo su un file in /tmp e poi mettersi in attesa. L'altro processo aprira' il file specificato, legge il nome della console, apre il device relativo e lo imposta come stdout.
Alternativa due: si salta il passaggio di scrivere da qualche parte il nome del terminale, si crea una fifo che viene impostata da una parte come output e dall'altra come input. Impostare l'input di gnome-terminal non e' altro che chiamare uno script che fa il cat della fifo
Sono abbastanza simili, ma mi piace di piu' la seconda fondamentalmente per il fatto che in questo modo il terminale si chiude automaticamente quando il processo scrivende finisce, mentre nell'altro caso devo fare una kill del processo relativo.
Questo e' un esempio di come puo' funzionare: fa partire una shell che scrive sulla seconda finestra.
Sostituisci i programmi indicati con quelli che usi tu
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
char fifo_name[128];
int fifo_fd;
void display_proc()
{
char cmd[128];
snprintf(cmd,128,"/usr/local/bin/xterm -e \"cat %s\"",fifo_name);
system(cmd);
}
void worker_proc()
{
char cmd[128];
freopen(fifo_name, "w+", stdout);
system("/usr/local/bin/bash");
}
int main()
{
int i,j;
snprintf(fifo_name,128,"/tmp/tmpXXXX");
mktemp(fifo_name);
printf("Fifo name: %s\n", fifo_name);
fifo_fd = mkfifo(fifo_name,0700);
switch( fork())
{
case 0:
display_proc();
return;
case -1:
printf("Error while forking: %s\n", strerror(errno));
return;
default:
break;
}
worker_proc();
}
AnonimoVeneziano
04-09-2011, 02:36
Sto provando in questo modo ma la stringa che mi restituisce e' lo stesso terminale in cui si trova anche il padre (/dev/pts/0).
Ti ritorna quella perchè è così che deve essere :asd: non so cosa HO bevuto quella sera, ma è ovvio che era qualcosa di forte perchè è naturale che ritorni quella del padre visto che il processo figlio è collegato a quel terminale.
Consiglio cannato :p
AnonimoVeneziano
04-09-2011, 03:07
Il file descriptor della console di gnome-terminal non va bene, perche' e' la stessa del padre (e' l'output del programma gnome-terminal, non dei programmi che quest'ultimo fa girare).
Vedo due alternative semplici, che coinvolgono l'esecuzione di un piccolo script da parte di gnome-terminal (io usero' piu' sotto xterm, ma non cambia tanto la cosa).
La prima e' quella di far partire il terminale e fargli eseguire il comando "tty", salvarlo su un file in /tmp e poi mettersi in attesa. L'altro processo aprira' il file specificato, legge il nome della console, apre il device relativo e lo imposta come stdout.
...
Questa soluzione fa il suo dovere, solo una cosa però:
snprintf(fifo_name,128,"/tmp/tmpXXXX");
dovrebbe essere
snprintf(fifo_name,128,"/tmp/tmpXXXXXX");
in quanto mktemp vuole almeno 6 X come minimo (almeno per la mia versione delle glibc)
Comunque mktemp dovrebbe essere deprecata, quindi se magari si potesse trovare un modo di non usarla sarebbe meglio (tanto per evitare un warning).
magnamel
04-09-2011, 13:23
è naturale che ritorni quella del padre visto che il processo figlio è collegato a quel terminale.
Avevo il sospetto che accadesse cio'.....ma visto che sono neofita ho provato ugualmente
non so cosa HO bevuto quella sera, ma è ovvio che era qualcosa di forte
pensa a me succede quando sono sobrio :muro:
si salta il passaggio di scrivere da qualche parte il nome del terminale, si crea una fifo che viene impostata da una parte come output e dall'altra come input. Impostare l'input di gnome-terminal non e' altro che chiamare uno script che fa il cat della fifo
Codice a parte sto portando avanti questa soluzione. Domanda:
Posso usare una pipe invece di una fifo? Aprendo un terminale nuovo con system(....) c'e' rapporto di parentela tra il mio processo ed il nuovo terminale?
Grazie
Non basta lanciare un terminal figlio che esegue un processo predefinito, e poi termina insieme a questo?
Il processo padre del secondo terminal così saprebbe perfettamente quando il task è finito!
Altrimenti, si possono fare due processi non padre/figlio che condividono una pipe... ma allora le possibilità sono infinite dato che è un generico problema di IPC.
AnonimoVeneziano
04-09-2011, 15:45
Codice a parte sto portando avanti questa soluzione. Domanda:
Posso usare una pipe invece di una fifo? Aprendo un terminale nuovo con system(....) c'e' rapporto di parentela tra il mio processo ed il nuovo terminale?
Grazie
No, non puoi usare una pipe, perchè xterm quando si apre ridireziona stdin e out , quindi una eventuale dup sulla pipe non ha effetto e non puoi fare "cat" di una pipe dal terminale perchè non esiste fisicamente .
Per una soluzione senza fifo ho modificato la versione di marco tirando fuori una roba del genere :
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <pty.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
char name[100];
void display_proc()
{
system("/usr/bin/xterm");
}
void worker_proc()
{
char cmd[128];
int fd;
while ((fd = open(name, O_WRONLY)) == -1);
fd = dup2(fd, 1);
execl("/bin/bash", "/bin/bash", NULL);
}
int main()
{
int i,j, pid;
openpty(&i, &j, name, NULL, NULL);
close(i);
close(j);
printf("%s\n", name);
switch( pid = fork())
{
case 0:
display_proc();
return 0;
case -1:
printf("Error while forking: %s\n", strerror(errno));
return 1;
default:
break;
}
worker_proc();
return 0;
}
In pratica cerca di determinare con "openpty" in nome del file del terminale con cui XTERM si aprirà, poi appena xterm crea il file si collega e ridireziona lo stdout del padre sulla pty del figlio.
Se venisse aperto un terminale da un altra parte tra la chiamata a "openpty" e l'apertura di xterm potrebbe non funzionare (è comunque una cosa quasi impossibile visto che accade tutto molto velocemente). La stessa cosa potrebbe succedere con la fifo se un programma tra la chiamata a mktemp e mkfifo creasse una fifo con lo stesso nome.
Ciao
AnonimoVeneziano
04-09-2011, 15:49
Non basta lanciare un terminal figlio che esegue un processo predefinito, e poi termina insieme a questo?
Il processo padre del secondo terminal così saprebbe perfettamente quando il task è finito!
Altrimenti, si possono fare due processi non padre/figlio che condividono una pipe... ma allora le possibilità sono infinite dato che è un generico problema di IPC.
Lui vuole scrivere da un padre sul figlio. (cioè, vuole che il figlio generi a sua volta un figlio che è un terminale e che possa scrivere su di esso)
Tecnicamente si potrebbe scrivere un altro programma server che ad esempio usa i socket o un altro sistema di IPC che viene lanciato sul terminale "figlio" e rimane in ascolto di dati da visualizzare da parte di un processo padre. Il padre gli spedirebbe i dati da visualizzare e lui li printerebbe sul terminale aperto. In tutti i casi sono soluzioni non semplicissime :p
Questa soluzione fa il suo dovere, solo una cosa però:
snprintf(fifo_name,128,"/tmp/tmpXXXX");
dovrebbe essere
snprintf(fifo_name,128,"/tmp/tmpXXXXXX");
in quanto mktemp vuole almeno 6 X come minimo (almeno per la mia versione delle glibc)
Non tutti usano la glibc :p
Comunque mktemp dovrebbe essere deprecata, quindi se magari si potesse trovare un modo di non usarla sarebbe meglio (tanto per evitare un warning).
Non mi risulta che sia deprecata :mbe:, di sicuro e' potenzialmente pericolosa.
Questo perche' se io mi faccio generare un nome di file E poi non creo subito il file E un attaccante crea il file al posto mio (visto che il nome e' facilmente indovinabile) E poi io creo il file piu' tardi senza controllare se c'e' gia', allora c'e' un rischio di sicurezza e quindi in generale conviene mkstemp. Ma direi che nel nostro caso non e' cosi' importante, visto che la necessita' e' solo quella di visualizzare su un altro schermo mentre si fanno delle prove.
vBulletin® v3.6.4, Copyright ©2000-2026, Jelsoft Enterprises Ltd.