View Full Version : Controllare se un fd e' valido
/\/\@®¢Ø
23-05-2003, 17:31
Domanda idiota :p: come controllo sotto unix se un file descriptor e' valido ?
Edit: ovviamente (:confused: :D) fd sta per file descriptor e il linguaggio e' il C
/\/\@®¢Ø
23-05-2003, 20:04
Si', ma io voglio sapere se quell'intero si riferisce effettivamente ad un fd oppure e' solo... un intero !
A me in particolare interessa stdin (fd 0)
Ad esempio nel seguente pezzo di codice (non scritto da me) il programma vuole chiudere stdin per i suoi motivi. Il problema e' che quando lo chiamo dal window manager (fvwm) stdin viene chiuso da questi e quindi quando il programma cerca di chiuderlo (e dirottare tutto su /dev/null) il programma si pianta.
/* codice originale */
/* ... */
int fd;
fd = open("/dev/null", O_RDONLY);
if (fd > 0)
{
close(0);
dup2(fd, 0);
close(fd);
}
Se le funzioni si limitassero a ritornare un errore potrei semplicemente controllare il valore di ritorno di close ed in caso evitare la chiamata a dup2. Purtroppo pero' invece il programma si pianta col messaggio "Fatal IO error 9 (bad file descriptor)".
Spero di essermi chiarito meglio ora
ilsensine
24-05-2003, 19:20
fstat
Nota che "if (fd>0)" è br0ken, in quanto se stdin è "chiuso" il sistema operativo è liberissimo di allocare il valore 0 per un descrittore.
/\/\@®¢Ø
25-05-2003, 14:26
Originally posted by "ilsensine"
fstat
Nota che "if (fd>0)" è br0ken, in quanto se stdin è "chiuso" il sistema operativo è liberissimo di allocare il valore 0 per un descrittore.
Avevo provato ma non mi sembrava che la situazione si sistemasse. Forse era dovuto proprio al cotnrollo bacato. Purtroppo non sono pratico di queste cose. Cmq intanto ho segnalato il bug. Se poi riesco a sistemare qualcosa... (poca fiducia... ma vediamo un po' :D )
Grazie.
/\/\@®¢Ø
25-05-2003, 15:15
ho trovato isatty(), forse e' anche meglio (visto che fa proprio il controllo che desidero)...
edit: forse no, sembra serva ad un'altra cosa :D
ilsensine
25-05-2003, 16:39
Originally posted by "cionci"
E ferror ?
ferror restituisce la condizione di errore di un _valido_ FILE *. Per passare da fd->FILE * occorre usare fdopen, che però fallisce se l'fd non è valido :D
ilsensine
25-05-2003, 16:47
Originally posted by "/\/\@®¢Ø"
Avevo provato ma non mi sembrava che la situazione si sistemasse.
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
int main() {
struct stat buf;
int ret;
ret = fstat(0, &buf);
fprintf(stderr, "stdin aperto: fstat ritorna %d\n", ret);
close(0);
ret = fstat(0, &buf);
fprintf(stderr, "stdin chiuso: fstat ritorna %d (%s)\n", ret, strerror(errno));
return 0;
}
Cmq intanto ho segnalato il bug.
Io mi riferivo in generale. In quel caso il bug è _non_ controllare se la open fallisce; questo codice ad es. funziona (perché? :D )
int fd;
fd = open("/dev/null", O_RDONLY);
if(fd<0) {
/* gestisci l'errore... */
} else if (fd>0) {
close(0);
dup2(fd, 0);
close(fd);
} // ...else è perfettamente ok!!
Nota che close(0) è superflua (v. man dup2)
/\/\@®¢Ø
25-05-2003, 17:11
[quote="ilsensine"]
Io mi riferivo in generale. In quel caso il bug è _non_ controllare se la open fallisce; questo codice ad es. funziona (perché? :D )
[quote]
Io in realta' pensavo che col check >0 si considerassero alla stessa stregua il caso -1 e 0. (Nel primo caso ignorando l'errore, nel secondo proseguendo perche' abbiamo gia' fd==0 e quindi non occorre duplicarlo su se stesso).
/\/\@®¢Ø
25-05-2003, 17:15
aspetta che forse ho capito...
visto che stdin mi e' stato chiuso dal chiamante (il window-manager), e' possibile che in 0 ci sia comunque un fd validissimo ma non sia stdin, e quindi non mi basta controllare se 0 e' un fd valido ! Allora in questo caso come faccio a capire che non si tratta di stdin ? (da quel che ho capito il wm lo fa apposta per farmi capire che sto operando in modalita' non interattiva).
ilsensine
25-05-2003, 18:59
Originally posted by "/\/\@®¢Ø"
aspetta che forse ho capito...
visto che stdin mi e' stato chiuso dal chiamante (il window-manager), e' possibile che in 0 ci sia comunque un fd validissimo ma non sia stdin, e quindi non mi basta controllare se 0 e' un fd valido ! Allora in questo caso come faccio a capire che non si tratta di stdin ? (da quel che ho capito il wm lo fa apposta per farmi capire che sto operando in modalita' non interattiva).
Un programma che confida che stdin è sull'fd 0 è mal scritto. Occorrerebbe usare, semmai, fileno(stdin) - che fallisce solo se stdin è chiuso.
Cmq non sapevo che il wm "chiude" stdin, sei sicuro di questo?
/\/\@®¢Ø
25-05-2003, 19:08
Quell'errore lo avevo notato anche io, pero' non era tra quelli che rompevano (penso) e quindi l'ho lasciato li'.
Penso proprio di si' che sia il window-manager, nei sorgenti ho trovato pure questo:
/* close stdin so the exec'd process knows its not
interactive */
close(STDIN_FILENO);
(il wm e' Fvwm)
Una cosa cosi' 'palese' suppongo implichi che c'e' un modo per capire se stdin ti e' stato chiuso... ma quale ? :confused:
ilsensine
25-05-2003, 20:48
Credo che stdin sia "hardcoded" sull'fd 0, quindi fstat fatto su 0 è la "soluzione" più generale...
Un pò più "complicato" capire se l'fd 0 corrisponde a stdin o a qualche file aperto dopo la chiusura di stdin, per far questo forse aiuta l'esame della struct stat riempita da fstat
Ma la fstat riporta qualche device particolare per lo stdin ?
ilsensine
26-05-2003, 11:07
Originally posted by "cionci"
Ma la fstat riporta qualche device particolare per lo stdin ?
Dipende cosa è "stdin". Può essere un file (programma < file.txt), un pipe (cat file.txt|programma), un pts (programma eseguito "senza parametri"; stdin riporta l'input da tastiera). In quest'ultimo caso ad esempio, "stdin" è in effetti /dev/stdin, che è un link a /dev/fd/0, che è a sua volta un link a /dev/pts/n dove n è il pseudoterminal slave corrente; in questo caso S_ISCHR() è true, e struct stat::st_rdev indica il device caratteri utilizzato... chiaro? :D
In tutti i casi, fstat riporta chiaramente "cosa sto usando" come stdin, oppure un errore se stdin è "chiuso".
ilsensine
26-05-2003, 11:16
/\/\@®¢Ø, posso sapere cosa stai tentando di fare? Un programma che "preleva" stdin da /dev/null presumo che non debba MAI leggere da stdin...
/\/\@®¢Ø
26-05-2003, 11:24
Solo un problema che avevo trovato utilizzando rox-filer sotto Fvwm: quando chiamo da menu Fvwm chiude l'stdin perche' alcuni programmi cercano di capire se stanno lavorando in modalita' interattiva o meno ( gmplayer ad esempio) mentre poi rox-filer cerca di dirottare stdin su /dev/null per un motivo analogo. Volevo vedere se riuscivo a capire quale era il problema, ma avendo pure segnalato la cosa su entrambe le mailing lists me l'hanno appena risolto :D :p (ovvero quelli di fvwm hanno cambiato la close nel dup2 come rox).
/\/\@®¢Ø
26-05-2003, 11:26
La cosa bizzarra comunque era che la dup2 funzionava in ogni caso... solo che se stdin era stato precedentemente chiuso il programma andava in errore. Eliminando la dup2 invece il programma funzionava in ogni caso.
:confused:
grazie mille comunque :)
ilsensine
26-05-2003, 11:32
Mah continuo a non capire dove era il problema...questo codice funziona in ogni caso, sia che stdin sia aperto o chiuso, purché fd sia DIVERSO da 0 e che su 0 non ci sia un fd aperto per altri scopi:
dup2(fd, 0);
close(fd);
/\/\@®¢Ø
26-05-2003, 12:21
Originally posted by "ilsensine"
Mah continuo a non capire dove era il problema...questo codice funziona in ogni caso, sia che stdin sia aperto o chiuso, purché fd sia DIVERSO da 0 e che su 0 non ci sia un fd aperto per altri scopi:
dup2(fd, 0);
close(fd);
Boh :confused:
E' possibile che venisse aperto un altro descriptor e che questo finisse in 0 (non l'fd di qui sopra cmq, ho controllato). E' proprio per questo che hanno consigliato di non chiudere stdin ma di reindirizzarlo.
ilsensine
26-05-2003, 12:30
Originally posted by "/\/\@®¢Ø"
Boh :confused:
E' possibile che venisse aperto un altro descriptor e che questo finisse in 0 (non l'fd di qui sopra cmq, ho controllato). E' proprio per questo che hanno consigliato di non chiudere stdin ma di reindirizzarlo.
Allora lo "sbaglio" era assumere che 0 == stdin, ovvero che stdin era "aperto" all'avvio del progranna.
Il fix più corretto è effettuare queste operazioni all'avvio del programma, prima di aprire altri fd, quando ancora si può "affermare qualcosa" sull'fd 0.
Comunque per chi non lo sapesse, in unistd.h sono dichiarate delle macro per gli standard streams che consentono di non dover utilizzare direttamente il numero del file descriptor, che sono appunto:
STDIN_FILENO (0 sotto GNU, ma non è detto altrove)
STDOUT_FILENO (1 sotto GNU, ma non è detto altrove)
STDERR_FILENO (2 sotto GNU, ma non è detto altrove)
Queste macro migliorano inoltre la portabilità del codice sotto diversi flavour di UNIX.
EDIT: Questo in merito al discorso che faceva il sensine nel dire giustamente che un programma che fa affidamento su 0 per indicare STDIN è mal concepito.
vBulletin® v3.6.4, Copyright ©2000-2025, Jelsoft Enterprises Ltd.