[résolu]Pipe, fork et pthread, agrémentés de tftp...

Pipe, fork et pthread, agrémentés de tftp... [résolu] - C - Programmation

Marsh Posté le 27-01-2012 à 17:55:38    

Bonjour,
Je cherche aujourd'hui à manipuler tftp (sous linusk), en le lançant dans l'enfant d'un fork (via exec), et en lui envoyant des commandes (put, exit), depuis le père.
Cela semble fonctionner...
Cependant, lorsque j'essaie de lancer plusieurs fois ce processus en simultané, via pthread, je me heurte à un écueil. Je trouve, sur ma sortie standard, des "tftp> ?Invalid command" qui apparaissent...
Je vous copie-colle ci-dessous le code compilable (au final, c'est une simplification du code d'origine, mais le problème se pose toujours).
Pensez à utiliser l'options "-lpthread" de gcc :)

Code :
  1. #include <stdlib.h>
  2. #include <stdio.h>
  3. #include <unistd.h>
  4. #include <pthread.h>
  5. #include <string.h>
  6. void* send_patch_via_tftp(void* datab)
  7. {
  8. pid_t pid;
  9. int rv;
  10. int commpipe[2];
  11. char IP_to_patch[16];
  12. strcpy(IP_to_patch, datab);
  13.         if(pipe(commpipe))
  14. {
  15.  // En cas de problème avec la création du pipe, on coupe tout
  16.  fprintf(stderr, "Pipe error !\n" );
  17.  exit(1);
  18. }
  19. if( (pid=fork()) == -1)
  20. {
  21.  // On coupe tout si le fork échoue.
  22.  fprintf(stderr, "Fork error, Exit.\n" );
  23.  exit(1);
  24. }
  25. if(pid)
  26. {
  27.  /* Process père */
  28.  dup2(commpipe[1],1); /* Remplacement de stdout avec une des entée du pipe vers tftp */
  29.  close(commpipe[0]);  /* On ferme l'autre coté */
  30.  setvbuf(stdout,(char*)NULL,_IONBF,0); /* sortie stdout non bufferisee */
  31.  sleep(1); // Les sleeps sont là pour n'écrire que quand tftp est prêt. A voir si c'est 1) réductible 2)Suppressible
  32.  printf("put /opt/armur/patch patch\n" ); // On envoie la commande, toute simple, à tftp, via le pipe
  33.  sleep(1); // Idem ci-dessus
  34.  printf("quit\n" ); // Ah, oui, si celle-ci ne passe pas, ça laisse des zombies de partout, c'est crade.
  35.  wait(&rv);    /* On attend le retour de tftp */
  36. }
  37. else
  38. {
  39.  /* Process enfant */
  40.  dup2(commpipe[0],0); /* on remplace stdin avec l'autre coté du pipe */
  41.  close(commpipe[1]);  /* On ferme le premier coté du pipe */
  42.  execl("/usr/bin/tftp","/usr/bin/tftp",IP_to_patch,(char*) 0);
  43. }
  44. pthread_exit(NULL);
  45. }
  46. int main(int argc, char *argv[])
  47. {
  48.         char IP_1[16] = "10.0.0.201";
  49.         char IP_2[16] = "10.0.0.202";
  50.         char IP_3[16] = "10.0.0.203";
  51.         char IP_4[16] = "10.0.0.204";
  52.         pthread_t thread_1;
  53.         pthread_t thread_2;
  54.         pthread_t thread_3;
  55.         pthread_t thread_4;
  56.         pthread_create(&thread_1, NULL,send_patch_via_tftp, IP_1);
  57.         pthread_create(&thread_2, NULL,send_patch_via_tftp, IP_2);
  58.         pthread_create(&thread_3, NULL,send_patch_via_tftp, IP_3);
  59.         pthread_create(&thread_4, NULL,send_patch_via_tftp, IP_4);
  60.         pthread_join(thread_1, NULL);
  61.         pthread_join(thread_2, NULL);
  62.         pthread_join(thread_3, NULL);
  63.         pthread_join(thread_4, NULL);
  64. }


 
Ceci n'est pas une aide au devoir, mais une incompréhension de ma part, merci à tout ceux qui apporteront une piste de réflexion :)
Bien entendus, toutes les machines (10.0.0.201-204) disposent d'un serveur tftp fonctionnel, à l'écoute, sur un port standart, etc...


Message édité par l0g4n le 28-01-2012 à 00:25:14

---------------
Fort et motivé. Sauf parfois.
Reply

Marsh Posté le 27-01-2012 à 17:55:38   

Reply

Marsh Posté le 27-01-2012 à 21:49:52    

Désolé, je n'ai pas de linusque sous la main, mais il y a un truc qui me parait pas bien dans ton code (outre le fait que tu as sans doute dû oublié les lignes pour initialiser commpipe[] et pid):
 

Code :
  1. dup2(commpipe[1],1);


 
Là tu remplaces la sortie standard du processus père, processus où sont présent tous les threads qui envoient les commandes. Comme commpipe[1] est différent pour chaque thread, certains thread vont écrire dans le vide et les processus fils risquent d'attendre indéfiniment. Bref je regarderais du coté de la fonction fdopen() pour éviter de réassigner stdout.

Reply

Marsh Posté le 27-01-2012 à 22:40:07    

tpierron a écrit :

Désolé, je n'ai pas de linusque sous la main, mais il y a un truc qui me parait pas bien dans ton code (outre le fait que tu as sans doute dû oublié les lignes pour initialiser commpipe[] et pid):


Oups, oui, manque un bout du code, j'ai trop simplifié  :sweat: Si ça peut te rassurer, ils sont bien présents dans mon code original :)
J'édit le premier post :o
 

tpierron a écrit :


Code :
  1. dup2(commpipe[1],1);


 
Là tu remplaces la sortie standard du processus père, processus où sont présent tous les threads qui envoient les commandes. Comme commpipe[1] est différent pour chaque thread, certains thread vont écrire dans le vide et les processus fils risquent d'attendre indéfiniment. Bref je regarderais du coté de la fonction fdopen() pour éviter de réassigner stdout.


Ah mais c'est pas con ! J'avais juste pas du tout vu ça comme ça, je pensait un thread == un père/fils == un stdin/stdout/stderr...  :sweat:  
Je vais jeter un coup d'oeuil à fdopen... Si en plus, ça peut m'éviter de forker :)
EDIT : ai jeté un coup d'oeuil à fdopen, et la pluspart de la doc que je trouve dessus dis "fdopen associe un flux à l'identificateur de fichier obtenu par un appel préalable à creat, dup, dup2 ou open.", donc en gros, il fait la même chose que moi avec dup2 et pipe ?
Bref, je continus à creuser, mais je suis pas sur de creuser dans la bonne direction...  :hello:
 
EDIT 2 : Je suis parvenus à remplacer la modification de stdout par un flux, via fdopen et pipe et, si tout se passe comme je le pense, mes données arrivent bien de l'autre coté de mon pipe, dans mon child...
Sauf que du coup, maintenant, je voudrais faire en sorte que execl redirige ce qui vas sortir de mon pipe vers le stdin du process qu'il lance ?!  Sans utiliser dup2 donc ?! Sauf que je n'ai qu'un stdin pour tout mes threads  :??:  
J'ai déjà passé pas mal de temps sur ce soft, et cette partie précise me donne du fil à retordre, je crois que j'ai plus les idées très claires  :sweat:


Message édité par l0g4n le 27-01-2012 à 23:44:52

---------------
Fort et motivé. Sauf parfois.
Reply

Marsh Posté le 28-01-2012 à 00:24:59    

Problème résolu, j'ai remplacé toute ma fonction send_patch_via_tftp par :
 

Code :
  1. void* send_patch_via_tftp(void* datab)
  2. {
  3.   char IP_to_patch[16];
  4.   char commande[100];
  5.   FILE* sortie;
  6.   strcpy(IP_to_patch, datab);
  7.   strcat(commande, "tftp " );
  8.   strcat(commande, IP_to_patch);
  9.   sortie = popen(commande, "w" );
  10.   sleep(1);
  11.   fputs("put /opt/armur/patch patch\n", sortie);
  12.   sleep(1);
  13.   fputs("quit\n", sortie);
  14.   pclose(sortie);
  15. }


 
Et celà fonctionne, même sur plusieurs threads...
Faut que je vois si ça fonctionne en supprimant les sleeps, mais le principale est que ça tourne, que ça facilite grandement mon code (je n'avais pas besoin du fork, mais uniquement des threads, ils étaient là car je n'ai pas su trouver solutions plus élégante, c'est chose faite.).
Merci tpierron, c'est en farfouillant dans les liens des pages de doc de fdopen que j'ai finis sur popen :)


---------------
Fort et motivé. Sauf parfois.
Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed