IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

 C Discussion :

Retrouver son prompt après un exec() Shell simple personnalisé


Sujet :

C

  1. #1
    Candidat au Club
    Femme Profil pro
    employé
    Inscrit en
    Janvier 2017
    Messages
    8
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : Canada

    Informations professionnelles :
    Activité : employé

    Informations forums :
    Inscription : Janvier 2017
    Messages : 8
    Points : 4
    Points
    4
    Par défaut Retrouver son prompt après un exec() Shell simple personnalisé
    Bonjour,

    Je dois créer un Shell simple. Un prompt affiche et je peux écrire des commandes qui n'existent pas dans le vrais Bash mais qui simule leur action.

    Mais mon problème est que je sors de mon programme et j'aimerai que mon prompt s'affiche à nouveau
    je ne réussi pas à comprendre comment faire pour que suite à un execl() je puisse retrouver mon pompt
    et sortir seulement quand on écrit exit!

    j'utilise un while(1)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <signal.h>
    #include <string.h>
    #include <sys/wait.h>
     
    int main(int argc, char *argv[]){
     
        int status;
        char buffer[500];
     
        printf("\nDans MON SHELL  écrire \"exit\" pour quitter\n");
     
        int pid = fork();
     
        if(pid == -1){
            perror("Une erreur c'est produite : création du  fork");
            return EXIT_FAILURE;
     
        }
     
        if(pid == 0){
     
            while(1){
                printf("MONSHELL > : ");
     
                scanf("%s", buffer);
     
     
                if (strcmp("exit", buffer) == 0){
                    exit(0);
     
                }else if(strcmp("dir", buffer) == 0){       //je ne retrouve pas mon promp pour entrer par exemple ctr...
     
                    execl("/bin/ls", "ls", "-l", NULL);
     
                }else if(strcmp("ctr", buffer) == 0){  
     
    	    	    execl("/usr/bin/clear", "clear", NULL);
     
    	        }else if(strcmp("environ", buffer) == 0){  
     
    	    	    execl("/usr/bin/env", "env", NULL);
     
    	        }else if(strcmp("echo", buffer) == 0){    //ici si j'écris echo cmd j'aimerai que seulement le cmd affiche....
                    printf("%s\n", &buffer);
     
                }else{
     
                    printf("Commande inconnue ...\n");
                }
            }//fin wihile
     
        }//fin if =0
     
        if(pid > 0){
     
            if(wait(NULL) == -1){
                perror("erreur de wait cote parent\n");
                exit(EXIT_FAILURE);
            }
     
        }//fin if > 0
          return EXIT_SUCCESS;
    }//fin main

    merci beaucoup
      0  0

  2. #2
    Membre averti

    Homme Profil pro
    Enseignant
    Inscrit en
    Septembre 2012
    Messages
    313
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Enseignant
    Secteur : Enseignement

    Informations forums :
    Inscription : Septembre 2012
    Messages : 313
    Points : 354
    Points
    354
    Par défaut
    es-tu certain qu'on atteigne à un moment le exit() ?
    j'ai l'impression que l'usage de strcmp() ne soit pas approprié dans ce contexte...
      1  0

  3. #3
    Membre averti

    Homme Profil pro
    Enseignant
    Inscrit en
    Septembre 2012
    Messages
    313
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Enseignant
    Secteur : Enseignement

    Informations forums :
    Inscription : Septembre 2012
    Messages : 313
    Points : 354
    Points
    354
    Par défaut
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <signal.h>
    #include <string.h>
    #include <sys/wait.h>
     
    // Commentaires très bien ^ ^
     
    // Eviter les else if, essayer de concevoir son application pour éviter les branchements conditionnels
    // imbriqués et surtout ne pas utiliser le "else if/elsif"
     
    int main(int argc, char *argv[])
    {
        int status;
        char buffer[500];
     
    		memset(buffer,0,500);
        printf("\nDans MON SHELL  écrire \"exit\" pour quitter\n");
        int pid = fork();
     
        if(pid == -1)
    		{
            perror("Une erreur c'est produite : création du  fork");
            return EXIT_FAILURE;
        }
        if(pid == 0) // processus père
    		{
    			printf("[PERE]");		
             while(1)
    				 {
                printf("MONSHELL > : ");
                //scanf("%s", buffer);   
    						fgets(buffer,25,stdin);
    						printf("\n[DEBUG] -> %s (%s) %d\n",buffer,strcmp("exit",buffer)?"true":"false",strcmp("exit",buffer));
     
    						// Comme on ne sait pas (il y a des fonctions qui permettent de gérer plus facilement les "interpréteurs de commande"
    						// mais ici c'est "à la bonne franquette" ^^
     
    						// on teste les commandes "prises en charge" les unes après les autres, pas la peine
    						// de s'emmerder avec des else et des elsif/else if
     
                if (strstr(buffer,"exit") != NULL)
    						{
    							exit(0);
                }
                if(strstr(buffer,"dir") != NULL)
    						{       //je ne retrouve pas mon promp pour entrer par exemple ctr...
                   execl("/bin/ls", "ls", "-l", NULL);
    						}
    						if(strstr(buffer,"ctr") != NULL)
    						{  
    							execl("/usr/bin/clear", "clear", NULL);
    						}
    						if(strstr(buffer,"environ") != NULL)
    						{  
    							execl("/usr/bin/env", "env", NULL);
    						}
    						if(strstr(buffer,"echo") !=NULL)
    						{    //ici si j'écris echo cmd j'aimerai que seulement le cmd affiche....
    							printf("%s\n", &buffer);
     						}
    					}//fin wihile
     
        }//fin if =0
     
        if(pid > 0) // processus fils
    		{
    				printf("[FILS] ...");
            if(wait(NULL) == -1)
    				{
                perror("erreur de wait cote parent\n");
                exit(EXIT_FAILURE);
            }
            printf("[FILS] end...\n");
        }//fin if > 0
     
    		return EXIT_SUCCESS;
    }//fin main
    Les commentaires ont été ajoutés, j'ai utilisé strstr (qui cherche une sous-chaine dans une chaine)...
    ici strcmp() retourne toujours autre chose que 0... il est tard il fait chaud et je ne saurais pas te dire pourquoi strcmp retourne autre chose que 0.
    strstr() retourne un pointeur sur la première occurence de la chaîne trouvée dans le chaîne, ou NULL si elle n'est fait pas partie.

    Je ne sais pas pourquoi tu fais un fork(), vu que le fils attends le père et que le père lance un execl() qui remplace le code du père par le programme que tu appelles
    du coup automatiquement ton code dans le père (le while(1)...) sera remplacé par le programme exécuté par execl...
      0  0

  4. #4
    Membre averti

    Homme Profil pro
    Enseignant
    Inscrit en
    Septembre 2012
    Messages
    313
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Enseignant
    Secteur : Enseignement

    Informations forums :
    Inscription : Septembre 2012
    Messages : 313
    Points : 354
    Points
    354
    Par défaut
    Bon !!
    Ton projet m'a tellement passionné que j'ai fait ceci, il faudra que tu l'adaptes, je n'ai tellement plus l'habitude des fork() que je me suis un peu emmelé les pinceaux ^ ^

    Le fork retourne 0 pour indiquer qu'il s'agit du processus "fils" et fourni le pid du processus "enfant" au processus père, je pensais que c'était l'inverse enfin bon...

    (man 3p fork: fork() shall return 0 to the child process and shall return the process ID of the child process to the parent process)

    J'avais complètement oublié que dans le "contexte des forks" il valait mieux "oublier" les fonctions "bufferisées" comme fgets() que je conseille d'utiliser, en général, pour tout ce qui concerne les "saisies au clavier".
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <signal.h>
    #include <string.h>
    #include <stdbool.h>
    #include <sys/wait.h>
     
    int main(void)
    {
    	int 	pidfils,returncode;
    	bool  bCommand=true;
    	char 	*pStrCommand;
    	char 	binarypath[255]="/usr/bin/";
     
    	pStrCommand=calloc(255,sizeof(char));
     
    	fprintf(stdout,	"Programme de test pour vérifier qu'il soit possible d'exécuter des commandes"
    									" tant que \"exit\" ne soit pas encodé...\n");
     
    	while(bCommand==true)
    	{
    		fprintf(stderr,"[SHELL]>\t");
    		//fgets(pStrCommand,255,stdin); avec l'usage de execl il vaut mieux ne pas utiliser des fonctions de type "bufferisé"... 
    		int octetslus=read(STDIN_FILENO,pStrCommand,255);
    		octetslus--;
    		*(pStrCommand+octetslus)='\0'; // on remplace le '\n' par '\0' sinon ça ne marchera pas avec execl... 		
     
    		pidfils=fork();
    		if(!pidfils) // FILS
    		{
    			fprintf(stderr,"\n[DEBUG] FILS...\n");
    			fprintf(stderr,"[FILS] pid %05d\n",pidfils);
    			fprintf(stderr,"[FILS] pStrCommand %s\n",pStrCommand);
     
    			if(strstr(pStrCommand,"exit")!=NULL) exit(1);			
    			strcat(binarypath,pStrCommand);
    			fprintf(stderr,"[FILS] binarypath %s\n",binarypath);
    			// A partir d'ici le Code Segment du processus sera remplacé par "ps" (ou n'importe quel autre programme exécutable)
    			execl(binarypath,pStrCommand,NULL);	 
     
    		}
     
    		// PERE
     
    		int code;
     
    		returncode=wait(&code);
    		if(returncode!=-1) 
    		{
    			fprintf(stderr,"[PROC DEATH] id: %05d\n",returncode);
    			if(code==256) bCommand=false; // exit(1) -> 256, exit(2) -> 512, ... exit(-1) -> 255, ... c'est un peu bizarre ^^  
    		}
    		fprintf(stderr,"[PERE] bCommand %s\n",bCommand?"true":"false");
    		strcpy(binarypath,"/usr/bin/");
    	}
    }
    Concernant la partie...

    int octetslus=read(STDIN_FILENO,pStrCommand,255);
    octetslus--;
    *(pStrCommand+octetslus)='\0'; // on remplace le '\n' par '\0' sinon ça ne marchera pas avec execl...
    ...comme nous sommes en "non bufferisé", la fonction read() incorpore le '\n' qui correspond à l'appui de la touche <RETURN> et ce caractère va nous empêcher d'exécuter execl...
    Du coup: via l'arithmétique des pointeurs je m'arrange pour placer le '\0', fin de chaîne, à la place du '\n'.

    *(pStrCommand+octetslus) <-> à partir du pointeur pStrCommand (une adresse en mémoire) tu te déplaces de octetslus * la taille du type de pStrCommand (ici 1 octet) puis à cet endroit tu y places '\0'.
    pStrCommand est l'adresse de base et octetslus est l'offset (le déplacement en mémoire) dépendant de la taille du type de pStrCommand.
    La petite étoile (dé-référencement) indique que c'est à l'adresse spécifiée qu'il faut placer le '\0'.

    C'était bien cool, j'ai pu renouer avec la fonction fork(), me rappeller que récupérer le "exit code" c'était un peu bizarre comme mécanisme, ...
      0  0

  5. #5
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 689
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 689
    Points : 30 983
    Points
    30 983
    Billets dans le blog
    1
    Par défaut
    Bonjour
    Citation Envoyé par hurukan Voir le message
    Le fork retourne 0 pour indiquer qu'il s'agit du processus "fils" et fourni le pid du processus "enfant" au processus père, je pensais que c'était l'inverse enfin bon...
    Pourtant ça me semble à moi inoubliable ce genre de truc. Parce que si c'était l'inverse, le fils aurait un numéro qui lui sert à que dalle (son propre pid qu'il connait déjà via getpid()) et le père aurait un 0 qui lui sert aussi à que dalle et en plus ne pourrait pas connaitre le pid de son fils.

    Citation Envoyé par hurukan Voir le message
    if(code==256) bCommand=false; // exit(1) -> 256, exit(2) -> 512, ... exit(-1) -> 255, ... c'est un peu bizarre ^^
    C'est pas bizarre, c'est "construit mathématiquement" pour stocker plusieurs informatiions sur un int.

    Un processus peut s'arrêter de deux façons
    • via exit(n), n compris entre 0 et 255
    • via kill(m), m compris entre 1 et 255

    Chaque nombre "n" et "m" n'occupant qu'un octet, les deux informations accolées tiennent sur un int. Ainsi wait(&status) va attendre la fin d'un fils, et si ce fils s'est terminé par exit(n) va positionner le "n" dans les 8 premiers bits de "status" et si ce fils a été tué via kill(m), va positionner ce "m" dans les 8 derniers bits de "status".
    Ensuite il suffit de regarder. Si les 8 derniers bits sont à 0 c'est que c'était exit(n) et pour retrouver "n" suffit de faire un décalage, sinon c'était kill(m) (m ne pouvant pas être égal à 0) et pour retrouver "m" suffit là de faire un masque. Et en plus comme il est malsain d'écrire un code basé sur des conventions qui peuvent évoluer, il existe 4 macros qu'on peut utiliser pour gérer le truc et qui, elles, suivront l'évolution éventuelle
    • WIFEXITED(status) qui renvoie vrai si c'était exit(n)
    • WEXITSTATUS(status) qui renvoie la valeur du "n"
    • WIFSIGNALED(status) qui renvoie vrai si c'était kill(m)
    • WTERMSIG(status) qui renvoie la valeur du "m"

    Evidemment afficher directement "status" sans connaitre ces détails rend évidemment la chose bizarre (toutefois tu remarqueras que toutes les valeurs que tu montres dans ton exemple sont toutes égales à n * 256, ce qui équivaut à n décalé à gauche de 8 bits).
    Ne reste que le exit(-1) que tu dis valoir 255 à expliquer mais ça ça reste effectivement inexplicable parce que faux. -1 sur un bit vaut 0xff (255) et 0xff placé sur les 8 bits de gauche d'un int donnent 0xff00 soit 255 * 256 = 65280.

    Citation Envoyé par hurukan Voir le message
    Du coup: via l'arithmétique des pointeurs je m'arrange pour placer le '\0', fin de chaîne, à la place du '\n'.
    Dangereux. Parce que si l'utilisateur entre une commande qui fait pile poil 255 caractères, ta variable pStrCommand ne contient pas de '\n' et tu effaces donc le dernier caractère de la commande. Ok, peu probable je l'admets. Mais s'il existe une solution alternative qui évite cette probabilité qui, bien que très faible, n'en est pas moins concrète, alors autant l'utiliser. Et elle existe.
    Accessoirement j'en profite car c'est sur la même variable, pourquoi "calloc" pour une zone de taille fixe ??? D'autant plus que t'as oublié le free().
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    #define SZ_COMMAND				(255)
    ...
    	char pStrCommand[SZ_COMMAND + 1];
    	char *pt;
    	pStrCommand[SZ_COMMAND]='\0';				// Oui, je n'ai besoin que d'un seul '\0' pour avoir une string... mais l'important est qu'il soit judicieusement positionné !!!
    	...
    	int octetslus=read(STDIN_FILENO, pStrCommand, SZ_COMMAND);	// Je t'ai conservé octetslus bien que peu utile...
    	if ((pt=strchr(pStrCommand, '\n')) != NULL) *pt='\0';
    Voilà. Désolé de te casser ta belle arithmétique des pointeurs qui en fait n'est pas vraiment utile. Toutefois elle était inutile aussi dans ton code originel. En effet, on utilise l'arithmétique des pointeurs dans des boucles de traitement, pour éviter de faire "n" fois le décalage. Mais ici, sans boucle, entre *(pStrCommand+octetslus)='\0' où tu fais une fois un décalage explicite, et pStrCommand[octetslus]='\0' qui fait exactement la même chose avec un décalage implicite là aussi qui n'est fait qu'une fois, je préfère la seconde écriture. Ben oui, à deux écritures équivalentes, mieux vaut privilégier la plus lisible...

    Citation Envoyé par hurukan Voir le message
    strcpy(binarypath,"/usr/bin/");
    Inutile. La variable n'a pas changé. Toutefois puisque j'y suis, tu concatènes une chaine pouvant aller jusqu'à 255 caractères à une chaine non nulle et tout ça dans une variable ne faisant, elle aussi, que 255 caractères...?
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    #define SZ_PATH				(255)
    	char binarypath[SZ_PATH + SZ_COMMAND + 1]="/usr/bin/";
    Ok, ça fait riche d'utiliser 255 caractères pour en stocker 15 mais c'est pas la mort, on peut gaspiller 240 octets (et puis si tu veux définir à moins...). Sinon l'autre solution est de faire là de l'allocation dynamique.
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    #define PATH				("/usr/bin/")
    	char *binarypath=malloc(strlen(PATH) + SZ_COMMAND + 1) * sizeof(*binarypath));
    	...
    	sprintf(binarypath, "%s%s", PATH, pStrCommand);
    plus rajouter le test de réussite du malloc (ça reste impératif dans un projet réel) et ne pas oublier le free(binarypath) à la fin. Je préfère gaspiller 240 octets.
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]
      2  2

  6. #6
    Candidat au Club
    Femme Profil pro
    employé
    Inscrit en
    Janvier 2017
    Messages
    8
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : Canada

    Informations professionnelles :
    Activité : employé

    Informations forums :
    Inscription : Janvier 2017
    Messages : 8
    Points : 4
    Points
    4
    Par défaut
    Merci beaucoup pour toutes les réponses!!!

    je vais prendre le temps de tout lire, de comprendre et d'essayer de coder le mieux que je peux et je vous reviens le plus vite possible!

    avec toutes les lectures et ce que vous me dites, je commence à comprendre un peu mieux ... j'ai de petites lumières qui allument

    merci encore!
      0  0

  7. #7
    Candidat au Club
    Femme Profil pro
    employé
    Inscrit en
    Janvier 2017
    Messages
    8
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : Canada

    Informations professionnelles :
    Activité : employé

    Informations forums :
    Inscription : Janvier 2017
    Messages : 8
    Points : 4
    Points
    4
    Par défaut
    Bonsoir !
    Bon je dois me décider à choisir parmi tout le code 1-ce que je comprend et 2-ce qui fonctionne le mieux

    Donc, j'ai opté pour ce qui suit... par contre toujours pas réussi à sortir avec mon exit(0)...
    des qu'il y a un wait() je ne sors pas... c'est peut-être une piste... je vais essayer
    j'ai essayé avec les signaux mais je n'ai pas très bien compris leurs comportements...
    c'est le père qui lance un signal quand le fils lui transmet son état?
    Une fois attrapé ou un message de l'état du fils est rendu au père on fait quoi pour sortir du programme?
    j'ai essayé et essayé lu et lu mais je ne vois pas exactement quoi faire...

    en attendant voici le code

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
     
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <signal.h>
    #include <string.h>
    #include <sys/wait.h>
     
     
    void sortie(int n, int status);
     
    void sortie(int n, int status){
        int w = n;
        printf("dans sortie\n");
     
                    do{
                       if (WIFEXITED(status)) {
                           printf("terminé, code=%d\n", WEXITSTATUS(status));
                       } else if (WIFSIGNALED(status)) {
                           printf("tué par le signal %d\n", WTERMSIG(status));
                       } else if (WIFSTOPPED(status)) {
                           printf("arrêté par le signal %d\n", WSTOPSIG(status));
                       } else if (WIFCONTINUED(status)) {
                           printf("relancé\n");
                       }
                   } while (!WIFEXITED(status) && !WIFSIGNALED(status));
     
    }
     
    int main(int argc, char *argv[])
    {
        int status;
        char buffer[500];
     
     
        memset(buffer,0,500);   
        if( argc == 2){
        printf("argc =2 argv[1] = nom du fichier a traiter %s\n");
     
     
        }
        printf("\nDans MON SHELL écrire \"exit\" pour quitter\n");
     
            if (strstr(buffer,"exit") != NULL)
            {
                exit(0);
            }     
     
     
        while(1){
     
            printf("MONSHELL > : ");
     
            fgets(buffer,25,stdin);
            int w;
             int pid = fork();
     
            if(pid == 0){
     
                if (strstr(buffer,"exit") != NULL)
                {
                    printf("PID : %d PPID : %d\n", getpid(), getppid());
                    w = waitpid(pid, &status, WUNTRACED | WCONTINUED);
                    sortie(w, status);
     
                }
                if(strstr(buffer,"dir") != NULL)
                {
                    execl("/bin/ls", "ls", "-l", NULL);
                }
     
                if(strstr(buffer,"ctr") != NULL)
                {
                    execl("/usr/bin/clear", "clear", NULL);
                }
     
                if(strstr(buffer,"environ") != NULL)
                {
                    execl("/usr/bin/env", "env", NULL);
                }
     
                if(strstr(buffer,"echo") !=NULL)
                {
                    printf("%s\n", &buffer[5]);
                }
                else
                {
                    printf("Commande inconnue ...\n");
                }
     
            }//fin if==0
     
     
        if(pid > 0){
            wait(&pid);
        }
     
     
        if(pid == -1)
        {
            perror("erreur fork -1");     
            exit(EXIT_FAILURE);
        }
     } //fin while
     
        return EXIT_SUCCESS;
    }//fin main
      0  0

  8. #8
    Candidat au Club
    Femme Profil pro
    employé
    Inscrit en
    Janvier 2017
    Messages
    8
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : Canada

    Informations professionnelles :
    Activité : employé

    Informations forums :
    Inscription : Janvier 2017
    Messages : 8
    Points : 4
    Points
    4
    Par défaut
    Bonsoir,

    Bon, voici ce que j'ai réussi à faire... pas grand chose mais je dois me fixer sur un code que je comprends le plus possible pour pouvoir avancer
    Ce n 'est pas par manque de lectures et tests et casse tête et tout ce que vous voulez

    j'ai trouvé comment sortir avec un exit pour 3 appels sur 4 ! quand je fais le echo titu par exemple, ça affiche bien juste titu mais il faut que je fasse 2 exit pour sortir
    je sais ... je pense... que la cause et encore le \n à la fin de titu
    pensez-vous qu'il est possible de retirer le \n et afficher avec execl(cut ou expr) ?

    En plus, je ne saisie pas trop le fonctionnement des signaux

    Désolée, je me perds un peu dans les explications qu'on m'a données
    merci pour votre aide!

    voici le code

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
     
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <signal.h>
    #include <string.h>
    #include <sys/wait.h>
     
     
    int status;
     
     
    int main(int argc, char *argv[])
    {
        char buffer[500];
     
     
        memset(buffer,0,500);   
        if( argc == 2){
        printf("argc =2 argv[1] = nom du fichier a traiter %s\n", argv[1]);
     
     
        }
        printf("\nDans MON SHELL écrire \"exit\" pour quitter\n");
     
     
        while(1){
     
            printf("MONSHELL > : ");
     
            fgets(buffer,25,stdin);
     
             int pid = fork();
     
            if(pid == 0){
        		if (strstr(buffer,"exit") != NULL)
                	{
                    	printf("PID : %d PPID : %d\n", getpid(), getppid());
     
             		exit(0);
                }
     
                if(strstr(buffer,"dir") != NULL)
                {
                    execl("/bin/ls", "ls", "-l", NULL);
                }
     
                if(strstr(buffer,"ctr") != NULL)
                {
                    execl("/usr/bin/clear", "clear", NULL);
                }
     
                if(strstr(buffer,"environ") != NULL)
                {
                    execl("/usr/bin/env", "env", NULL);
                }
     
                if(strstr(buffer,"echo") !=NULL)
                {
     
    		//char *argsexpr = "\\(.*\\).$";
     
    		//execl("/usr/bin/expr", "&buffer[5]", ":", "argsexpr", NULL);
     
    		//expr "bonjour" : "\(.*\).$"
    		//execl("/usr/bin/cut", "cut", "-f3-6", "&buffer[5]", NULL);
                   printf("%s\n", &buffer[5]);
                }
                else
                {
                    printf("Commande inconnue ...\n");
                }
     
            }//fin if==0
     
     
     
    	int ret = waitpid(pid, &status, 0);
    	printf("ret = %d\n", ret);    
    	if(ret == -1){
            	perror("echec waitpid du prc enfant 1");
        	}else{
     
            	printf("le execl() est termine prc = %d\n", getpid());
    		if (strstr(buffer,"exit") != NULL){
    			exit(0);
    		}
    	}
     
     
        if(pid == -1)
        {
            perror("erreur fork -1");     
            exit(EXIT_FAILURE);
        }
     } //fin while
     
        return EXIT_SUCCESS;
    }//fin main
      0  0

  9. #9
    Membre averti

    Homme Profil pro
    Enseignant
    Inscrit en
    Septembre 2012
    Messages
    313
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Enseignant
    Secteur : Enseignement

    Informations forums :
    Inscription : Septembre 2012
    Messages : 313
    Points : 354
    Points
    354
    Par défaut
    Quand je suis tombé sur ton projet il était tard et mon cerveau n'était plus très fonctionnel, j'ai tout de même pris un peu de temps pour risquer quelque chose.
    Alors je ne prétend pas du tout avoir la maîtrise du sujet, je suis enseignant et non professionnel en programmation "système" en C.

    Le fait que tu développes sur Linux je trouve ça bien cool et cela m'a motivé ^^

    Comme la dernière fois que j'avais fait un fork() ça remontait en 2015/2016 je n'avais plus trop de "souvenirs" clairs quant à "comment cela fonctionne".
    J'ai tenté de proposer "quelque chose qui marche" fait à la "va vite", avec quelques "libertés" quant à la manière d'aborder la problématique.

    Je ne pense pas qu'un shell soit programmé de la sorte, il faudrait que j'observe le code source du bash... du coup ton projet est surtout intéressant pour
    l'aspect "processus" (création de processus), pour l'aspect "recouvrement de processus" (utilisation des fonctions exeXXX())...

    Je n'ai pas encore regardé tes essais... je me suis arrêté sur ta demande liée "aux explications".

    Cependant d'entrée de jeu "il est conseillé de ne pas utiliser fgets() quand on "joue" avec les fonctions execXXX et le fork()", je ne retrouve pas la source mais je pense
    que cela vient d'un bouquin sur "la programmation système sous Linux". Je vais retrouver cela.
    Du coup il vaut mieux utiliser la fonction read()... qui fait la même chose que fgets() sauf qu'il faille enlever le '\n'.

    Tu peux le faire simplement grâce à la fonction read() qui va retourner le nombre de caractères lus, le '\n' se trouvera toujours à la dernière position...

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    octetslus=read(...);
    buffer[octetslus-1]='\0'; // remplace le '\n' par '\0'
    oups !! sve@r va crier...
    Il est préférable d'utiliser strchr() qui va chercher la première occurence du '\n' dans ta chaîne... elle retourne un pointeur sur la position dans la chaîne de celui-ci

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    char *pChercher=strchr(buffer,'\n');
    if(pChercher!=NULL)
    {
       *pChercher='\0'; // ...remplace le '\n' par le '\0'. 
    }
    si octetslus, dans la "mauvaise" approche, est à -1, par exemple, cela risque de poser des soucis lors de l'exécution du programme.
    buffer[-2] <-- en général ça ne se passe pas bien (il y a de fortes chances que cela ne se passe pas bien par rapport aux résultats escomptés) surtout si read() indique une erreur avec -1.

    ...essaye déjà en remplaçant la fonction fgets() par read()...

    Concernant les signaux (moi je pense à SIGXXXXXX), je ne sais pas si Sve@r sera de mon avis: je ne suis pas certain du tout qu'ils soient nécessaires... ou qu'ils puissent apporter quoi que ce soit
    comme amélioration du comportement de ton programme par rapport à ce que tu veux faire, si j'ai bien compris: simuler un shell.

    Maintenant si tu parles de la récupération du code de sortie d'un exit() ou d'un kill() sve@r a été sympa de nous indiquer des macros de sys/wait.h, que je ne connaissais pas, qui permettent de
    s'en sortir un peu mieux pour récupérer le "code de sortie" d'un processus.

    Bon courage ^^
      0  0

  10. #10
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 689
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 689
    Points : 30 983
    Points
    30 983
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par billy333 Voir le message
    Bon, voici ce que j'ai réussi à faire... pas grand chose mais je dois me fixer sur un code que je comprends le plus possible pour pouvoir avancer
    Au-moins ton code est maintenant lisible. Parce que ce matin je suis venu et je suis reparti direct.

    Citation Envoyé par billy333 Voir le message
    En plus, je ne saisie pas trop le fonctionnement des signaux
    Ca on s'en bat le steak. Un signal est un évènement extérieur à un processus, un truc qu'on lui envoie dans la tronche via kill() et où le processus receveur, s'il a de la chance, peut arriver à le traiter sinon il meurt (enfin ça dépend du signal car il y a quelques signaux qui ne le font pas mourir). Ici ça n'a aucune utilité. Peut-être plus tard, tu pourras protéger ton shell contre des signaux mais c'est pas la priorité.

    Citation Envoyé par billy333 Voir le message
    voici le code
    Alors déjà tu commences par tester si buffer contient "exit" alors que 3 lignes avant tu l'as mis à 0. Fatalement il ne contient pas exit. Ensuite ta fonction "sortie()" fait une boucle en fonction de l'état d'une variable qui ne change pas. Ca donne au choix une boucle infinie ou alors pas de boucle (ou ici une seule itération vu que tu fais un do...while). Bref la boucle est totalement inutile.

    Et enfin si tu veux sortir via "exit", il te faut tester "exit" avant de faire le fork. C'est le père qui s'arrête, pas le fils.

    PS: au fait, quand tu génères un fils via fork(), il faut penser à le faire s'arrêter via exit(). Surtout si la génération se fait dans une boucle !!!

    Citation Envoyé par hurukan Voir le message
    Quand je suis tombé sur ton projet il était tard et mon cerveau n'était plus très fonctionnel, j'ai tout de même pris un peu de temps pour risquer quelque chose.
    Très bien. Je ne t'ai absolument rien reproché, juste montré tes erreurs (ou disons les maladresses et les dangers que ça amène) et la façon de les corriger.

    Citation Envoyé par hurukan Voir le message
    Du coup il vaut mieux utiliser la fonction read()... qui fait la même chose que fgets() sauf qu'il faille enlever le '\n'.
    fgets() lui aussi récupère le '\n'.

    Citation Envoyé par hurukan Voir le message
    Tu peux le faire simplement grâce à la fonction read() qui va retourner le nombre de caractères lus, le '\n' se trouvera toujours à la dernière position...
    S'il s'y trouve ce qui n'est pas garanti à 100% !!!

    Citation Envoyé par hurukan Voir le message
    Il est préférable d'utiliser strchr() qui va chercher la première occurence du '\n' dans ta chaîne... elle retourne un pointeur sur la position dans la chaîne de celui-ci
    Yes. A condition impérative que ta zone de stockage soit une chaine, donc qu'elle contienne un '\0'. D'où ma méthode où je la taille à un caractère de plus que la taille "utile" et où je mets un '\0' dans ce caractère en plus (qui ne sera, lui, jamais modifié).

    Citation Envoyé par hurukan Voir le message
    Concernant les signaux (moi je pense à SIGXXXXXX), je ne sais pas si Sve@r sera de mon avis: je ne suis pas certain du tout qu'ils soient nécessaires... ou qu'ils puissent apporter quoi que ce soit
    Totalement d'accord.
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]
      1  0

  11. #11
    Membre averti

    Homme Profil pro
    Enseignant
    Inscrit en
    Septembre 2012
    Messages
    313
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Enseignant
    Secteur : Enseignement

    Informations forums :
    Inscription : Septembre 2012
    Messages : 313
    Points : 354
    Points
    354
    Par défaut
    Très bien. Je ne t'ai absolument rien reproché, juste montré tes erreurs (ou disons les maladresses et les dangers que ça amène) et la façon de les corriger.
    J'avais bien compris, j'aime bien sortir de ma "zone de confort" ^^
    Il y a plein de choses auxquelles on ne pense pas et puis on est fâché sur soi-même parce que on "oublie" la plupart des cas de figure qui se révêlent quand ton programme bugge ^^

    On apprend ^^

    EDIT:

    fgets() lui aussi récupère le '\n'.
    My bad -> je m'en servais pour introduire des nombres convertis la plupart du temps, et pour les chaînes de caractères je passe par des fonctions de mon cru qui l'enlèvent automatiquement :{
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    while(nbreCar<maximum)
    	{
    		*pTmp=getch();								// c'est complexe je sais...
     
    		if(*pTmp==10 || *pTmp==27) break;  // enlève le '\n', ou le caractère ESC de la chaîne...
      0  0

  12. #12
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 689
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 689
    Points : 30 983
    Points
    30 983
    Billets dans le blog
    1
    Par défaut
    Ok, j'ai fait ma propre version.

    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
     
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <signal.h>
    #include <string.h>
    #include <sys/wait.h>
     
    void sortie(int);
    void saisie(const char* const, char* const, unsigned short);
     
    void sortie(int status) {
    	printf("dans sortie\n");
    	if (WIFEXITED(status))
    		printf("terminé, code=%d\n", WEXITSTATUS(status));
    	else if (WIFSIGNALED(status))
    		printf("tué par le signal %d\n", WTERMSIG(status));
    	else if (WIFSTOPPED(status))
    		printf("arrêté par le signal %d\n", WSTOPSIG(status));
    	else if (WIFCONTINUED(status))
    		printf("relancé\n");
    }
     
    void saisie(const char* const prompt, char* const buffer, unsigned short size) {
    	fputs(prompt, stdout);
    	fflush(stdout);
    	int n=read(STDIN_FILENO, buffer, size);
    	if (buffer[n-1] == '\n') buffer[n-1]='\0';
    }
     
    #define SZ_BUF		(500)
    int main(int argc, char *argv[]) {
    	printf("\nDans MON SHELL écrire \"exit\" pour quitter\n");
     
    	char buffer[SZ_BUF + 1];
    	buffer[SZ_BUF + 1]='\0';
     
    	while(1) {
    		saisie("MON SHELL >", buffer, SZ_BUF);
    		if (strstr(buffer, "exit") != NULL) break;
     
    		int pid=fork();
     
    		if (pid == 0) {
    			// Fils
    			if (strstr(buffer,"dir") != NULL)
    				execl("/bin/ls", "ls", "-l", NULL);
     
    			if (strstr(buffer,"ctr") != NULL)
    				execl("/usr/bin/clear", "clear", NULL);
     
    			if (strstr(buffer,"environ") != NULL)
    				execl("/usr/bin/env", "env", NULL);
     
    			if (strstr(buffer,"echo") != NULL)
    				printf("%s\n", &buffer[strlen("echo") + 1]);
    			else
    				printf("Commande inconnue ...\n");
    			exit(0);
    		}
     
    		// Père
     		int status;
    		wait(&status);
    		sortie(status);
    	}
     
    	return EXIT_SUCCESS;
    }
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]
      1  1

  13. #13
    Candidat au Club
    Femme Profil pro
    employé
    Inscrit en
    Janvier 2017
    Messages
    8
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : Canada

    Informations professionnelles :
    Activité : employé

    Informations forums :
    Inscription : Janvier 2017
    Messages : 8
    Points : 4
    Points
    4
    Par défaut
    Bonjour!

    Et oui, j'avais remarqué que les signaux ne me servaient pas à grand choses pour l'instant...
    encore merci pour le code!
    je suis en train de regarder attentivement pour bien comprendre
    finalement toutes les routes mènent à Rome.. il y en a des plus courtes que d'autres!

    j'ai encore pleins de questions...

    je continue et je vous reviens
    merciiii
      0  0

  14. #14
    Candidat au Club
    Femme Profil pro
    employé
    Inscrit en
    Janvier 2017
    Messages
    8
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : Canada

    Informations professionnelles :
    Activité : employé

    Informations forums :
    Inscription : Janvier 2017
    Messages : 8
    Points : 4
    Points
    4
    Par défaut
    Bon... je pense que je vais pleurer! ma parole! je tourne en rond comme un chien qui se mord la queue! je m'impressionne moi-même
    alors... après avoir passé toute la journée sur le même problème...
    j'ai besoin de votre aide!
    vraiment je ne saisi pas les signaux ni le vrais comportement d'un fork ... je pense bien que mon problème c'est ça!
    alors
    voici mon code qui ne fonctionne pas
    j'ai deux fois mon prompt qui apparaît...au moins cette fois-ci ça ne fait pas planter mon ordi...
    même avec le code ici-bas parfois il y a des processus qui restent vivant ... je le vois avec $ps
    je kill..

    une autre question:
    j'ai fait un makefile super simple pour que mon script puisse se lancer peu importe l'emplacement dans le dossier home de l'usager
    pourriez-vous SVP, me dire s'il fonctionne? je pense que oui mais bon ... on ne sait jamais
    MERCI!

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    .PHONY: all
     
    	export PATH=$PATH:/tp1/
    VPATH=~/.batshrc
     
    monshell: monshell.c
    	gcc -Wall -o  monshell monshell.c
     
     
    clean:
    	rm -rf monshell
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
     
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <signal.h>
    #include <string.h>
    #include <sys/wait.h>
     
    void sortie(int);
    void saisie(const char* const, char* const, unsigned short);
     
    void sortie(int status) {
    	printf("dans sortie\n");
    	if (WIFEXITED(status))
    		printf("terminé, code=%d\n", WEXITSTATUS(status));
    	else if (WIFSIGNALED(status))
    		printf("tué par le signal %d\n", WTERMSIG(status));
    	else if (WIFSTOPPED(status))
    		printf("arrêté par le signal %d\n", WSTOPSIG(status));
    	else if (WIFCONTINUED(status))
    		printf("relancé\n");
    }
     
    void saisie(const char* const prompt, char* const buffer, unsigned short size) {
    	fputs(prompt, stdout);
    	fflush(stdout);
    	int n=read(STDIN_FILENO, buffer, size);
    	if (buffer[n-1] == '\n') buffer[n-1]='\0';
    }
     
    #define SZ_BUF		(500)
     
    int main(int argc, char *argv[]) {
     
        printf("\nDans MON SHELL écrire \"exit\" pour quitter\n");
     
        int status; 
    	char buffer[SZ_BUF + 1];
    	buffer[SZ_BUF + 1]='\0';
     
        int pid1=fork();
    	while(1){
     
            saisie("MON SHELL >", buffer, SZ_BUF);
     
            if (strstr(buffer, "exit") != NULL) break;
     
            int pid=fork();
     		if (pid == 0) {
     
    /*---------------------------------------------------*/
     
                if (strstr(buffer,"pause") != NULL){
                    if(pid1 == -1){
                        perror("Echec du premier fork()");
                    }
     
                    if(pid1 == 0){  
                        printf("Pour continuer taper 'ENTER' au clavier.\n");
            	    }
                    pause();
    			    printf("status status %d - PID %d - ppid %d \n", status, getpid(), getppid());
                }//fin pause
    /*------------------------------------------------------------------*/
     
                // Fils
                if (strstr(buffer,"dir") != NULL)
    			    execl("/bin/ls", "ls", "-l", NULL);
     
    			if (strstr(buffer,"ctr") != NULL)
    				execl("/usr/bin/clear", "clear", NULL);
     
    			if (strstr(buffer,"environ") != NULL)
    				execl("/usr/bin/env", "env", NULL);
     
    			if (strstr(buffer,"echo") != NULL)
    				printf("%s\n", &buffer[strlen("echo") + 1]);
    			else
    				printf("Commande inconnue ...\n");
    			exit(0);
    		}//fin PID
     
            // Père
    		wait(&status);
    	//	sortie(status);
    	}
     
    	kill(pid1, SIGTERM);
    	return EXIT_SUCCESS;
    }
      0  0

  15. #15
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 689
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 689
    Points : 30 983
    Points
    30 983
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par billy333 Voir le message
    vraiment je ne saisi pas les signaux ni le vrais comportement d'un fork ... je pense bien que mon problème c'est ça!
    Exact. En dehors d'être devenu imbitable (tu fais un second fork puis ensuite tu vas tester le premier) ton code est devenu explosif. Tu fais un fork() dans une boucle !!!

    Le fork crée un nouveau procesuss qui récupère et exécute tout le code qui se trouve placé après. Exemple: fork(); printf("Hello %d\n", getpid()) et tu auras 2 fois "hello" mais avec un numéro différent (celui de chaque processus).

    Grâce à sa valeur renvoyée, tu peux savoir si tu es dans le père ou dans le fils et tu peux programmer alors des traitements différents dans un ou l'autre cas. Mais il ne faut pas oublier que même si tu détectes qui est qui, tout le code reste quand-même visible des deux processus
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    if ((pid=fork()) == 0) {
    	// Ici on est dans le fils. Le père voit ce bloc mais lui il n'y entre pas car pour lui pid ne vaut pas 0
    	printf("Fils %d de %d\n", getpid(), getppid());
    } else {
    	// Ici on est dans le père. Le fils voit ce bloc mais lui n'y entre pas car pour lui pid vaut 0
    	printf("Père %d de %d\n", getpid(), pid);
    }
     
    // Ici les deux processus voient ce code. Et comme il n'y a aucune discrimination, les deux vont l'excuter
    printf("Hello %d\n", getpid());

    Donc si tu mets ça dans une boucle, première itération 2 processus, puis le fils recommence la boucle (et son père aussi) donc seconde itération 4 processus etc etc etc. C'est ce qu'on appelle un "fork bomb".

    Donc avec un fork, tu n'as plus le droit ensuite de quitter les alternatives if/else sous peine de voir tout code situé en dehors être exécuté 2 fois... sauf si tu prends soin d'arrêter l'un des deux via exit() (et généralement c'est moins con d'arrêter le fils)
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    if ((pid=fork()) == 0) {
    	// Ici on est dans le fils. Le père voit ce bloc mais lui il n'y entre pas car pour lui pid ne vaut pas 0
    	printf("Fils %d de %d\n", getpid(), getppid());
    	exit(0);			// Le fils s'arrête
    }
    // Le "else" devient inutile
    // Ici on est dans le père. Le fils est mort et donc ne voit même pas ce code
    printf("Père %d de %d\n", getpid(), pid);		// Même si le fils est mort, pid conserve sa mémoire
    printf("Hello %d\n", getpid());
    Dans ce cas seulement (si le fils s'arrête) alors t'as le droit de mettre fork() dans une boucle.

    Pour les signaux là t'as bien compris. kill(pid, sig) enverra le signal "sig" au processus "pid". Ca ça fonctionne. Ensuite ce qui est plus chaud, c'est programmer ledit processus pour qu'il réagisse de façon particulière s'il reçoit ledit signal (une fois je me suis amusé à faire un morse entre 2 processus) mais on n'en est pas encore là.

    Sinon je ne pige absolument rien à ce que tu veux faire. Pourquoi 2 fork() ???

    Et accessoirement si tu l'indentais proprement, cela serait quand-même plus lisible. Ecrire un code c'est d'abord l'écrire pour qu'il soit facilement lu dionc facilement compris. Si je dois réfléchir 2h sur une ligne à savoir à quel bloc elle appartient parce que t'as eu la flemme d'écrire propre, ben je préfère autant ne pas avoir à réfléchir et te laisser te démerder tout seul.

    Citation Envoyé par billy333 Voir le message
    j'ai fait un makefile super simple pour que mon script puisse se lancer peu importe l'emplacement dans le dossier home de l'usager
    pourriez-vous SVP, me dire s'il fonctionne? je pense que oui mais bon ... on ne sait jamais
    Je crois que tu n'as pas saisi le but d'un Makefile. Son but n'est pas de positionner un PATH. Le but d'un Makefile est de décrire les règles de dépendances entre les extensions (comment faire pour passer d'un .i à un .q puis d'un .q à un .tutu puis d'un .tutu à un .titi puis d'un .titi à un .c puis d'un .c à un exécutable).
    Donc tu décris dans le Makefile les règles puis les actions à faire entre chaque transition. Ensuite il te suffit de demander une cible (ex make xxx.titi) et si la règle "comment faire le ".titi" est décrite et que le fichier source (ici xxx.tutu puisque la règle est de faire un .titi à partir d'un .tutu) est présent, alors l'action sera faite. S'il n'est pas présent alors make regardera d'abord s'il peut faire le "xxx.tutu" à partir du xxx.q et etc.

    Pour pouvoir appeler un exécutable situé dans un dossier "XXX" depuis n'importe où dans l'arborescence, il suffit d'intégrer le dossier "XXX" dans la variable d'environneemnt PATH et ça ça se fait soit depuis le fichier .profile (qui est lu quand tu te connectes) soit depuis le fichier .bashrc (qui est lu à chaque fois que tu ouvres une fenêtre shell)

    Sinon oui pour ce qu'il est censé faire (compiler ton code) oui il est bon.
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]
      1  0

  16. #16
    Membre expérimenté
    Avatar de sambia39
    Homme Profil pro
    No Comment
    Inscrit en
    Mai 2010
    Messages
    543
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Loiret (Centre)

    Informations professionnelles :
    Activité : No Comment
    Secteur : High Tech - Matériel informatique

    Informations forums :
    Inscription : Mai 2010
    Messages : 543
    Points : 1 745
    Points
    1 745
    Par défaut
    Bonsoir,

    Citation Envoyé par Sve@r Voir le message
    Bonjour
    .......
    C'est pas bizarre, c'est "construit mathématiquement" pour stocker plusieurs informatiions sur un int.
    ......
    Chaque nombre "n" et "m" n'occupant qu'un octet, les deux informations accolées tiennent sur un int. Ainsi wait(&status) va attendre la fin d'un fils, et si ce fils s'est terminé par exit(n) va positionner le "n" dans les 8 premiers bits de "status" et si ce fils a été tué via kill(m), va positionner ce "m" dans les 8 derniers bits de "status".
    ............
    Evidemment afficher directement "status" sans connaitre ces détails rend évidemment la chose bizarre (toutefois tu remarqueras que toutes les valeurs que tu montres dans ton exemple sont toutes égales à n * 256, ce qui équivaut à n décalé à gauche de 8 bits).
    Ne reste que le exit(-1) que tu dis valoir 255 à expliquer mais ça ça reste effectivement inexplicable parce que faux. -1 sur un bit vaut 0xff (255) et 0xff placé sur les 8 bits de gauche d'un int donnent 0xff00 soit 255 * 256 = 65280.
    Tous systèmes d'exploitation de la famille GNU/Linux ou Unix dont exit() est utilisé retourne une valeur tenant uniquement sur 8 bits donc 0 à 255. Aucune valeur de retour d'erreur ne tient sur le 16, 32, 64 ou 128 bits et la raison est simple. La valeur 255 est utilisée à la place de la valeur -1 car la valeur -1 est tronquée au bit le moins significatif et renvoyer en tant qu'octet non signé. Cela est fait exprès pour que la valeur de renvoi soit différente de 0 ( 0 qui veut dire tout c’est bien passé).
    Ainsi donc tous programme sous Linux où Unix termine donc toujours avec une valeur 0, dans le cas contraire, une valeur différente de zéro indique l’erreur qui s’est produite et donc, il ne faut pas penser qu'int permet de renvoyer une valeur de retour sur 32 bits ou qu'il s'agit de deux informations accolées tenant sur un int, c'est totalement faux. Un code retour reste un code retour et c'est indissociable et sous Linux où Unix les valeurs de retours doivent toujours tenir dans 1 octet d'ailleurs si vous essayez avec les valeurs supérieures à 255 ou -1 , elles seront toutes tronquées sans exception et utiliser pour communiquer au processus père.

    Citation Envoyé par Sve@r Voir le message
    Ok, j'ai fait ma propre version.

    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    #define SZ_BUF        (500)
    int main(int argc, char *argv[]) {
        printf("\nDans MON SHELL écrire \"exit\" pour quitter\n");
     
        char buffer[SZ_BUF + 1];
        buffer[SZ_BUF + 1]='\0';
     
      ....
    }
    ici, il y a une erreur qui cause un "stack-buffer-overflow" et cela est dû au fait que le tableau à un accès vers la fin qui dépasse. Pour faire simple, c'est-à-dire que la valeur SZ_BUF+1 soit 501 est plus grand que le tableau de 500. Et si GCC ne branche pas, c'est parce que : les bornes de tableau ne sont pas systématiquement vérifiées avec GCC contrairement à d'autres compilateurs comme Clang et d'une part, il considère qu'il y a suffisamment de mémoire à la suite du tableau sauf que ce n'est pas le cas donc UB dans le pire des cas. ici buffer[SZ_BUF] ='\0'; serait plus approprié.

    à bientôt.
    Celui qui peut, agit. Celui qui ne peut pas, enseigne.
    Il y a deux sortes de savants: les spécialistes, qui connaissent tout sur rien,
    et les philosophes, qui ne connaissent rien sur tout.
    George Bernard Shaw
      1  2

  17. #17
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 689
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 689
    Points : 30 983
    Points
    30 983
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par sambia39 Voir le message
    Citation Envoyé par Sve@r Voir le message
    Un processus peut s'arrêter de deux façons
    • via exit(n), n compris entre 0 et 255
    • via kill(m), m compris entre 1 et 255
    et donc, il ne faut pas penser qu'int permet de renvoyer une valeur de retour sur 32 bits ou qu'il s'agit de deux informations accolées tenant sur un int, c'est totalement faux.
    Oui mais comme ce n'est pas ce que j'ai dit je ne vois pas pourquoi tu viens ramener ta fraise !!! C'est juste pour faire chier les gens que tu déformes ou oblitères leur propos ou bien c'est réellement une tare qui t'empêche de comprendre les mots que tu lis ??? Je n'ai jamais dit qu'on pouvait renvoyer un code sur 32 bits, d'autant plus qu'on voit bien que j'avais explicitement écrit "entre 0 et 255", j'ai dit qu'un processus avait deux possibilités de s'arrêter et que ces deux possibilités, qui occupent chacune l'espace d'un char, sont toutes deux codées sur un unique int commun (chacune occupant 8 bits bien attitrés de l'int ce qui permet alors au parent de déterminer de quelle façon son fils s'est arrêté et avec quelle valeur). Bien évidemment (et je l'avais écrit aussi ou pour le moins fortement sous-entendu) ces valeurs ne sont pas positionnées "en même temps" puisqu'un processus ne peut s'arrêter que de l'une ou l'autre façon toutes deux mutuellement exclusives. Et comme la valeur du kill() ne peut pas être 0, c'est en regardant les bits dédiés à sa position (s'ils sont ou ne sont pas à 0) que l'on peut détecter si le processus a été arrêté par kill() ou (par opposition) par exit().

    Citation Envoyé par sambia39 Voir le message
    ici, il y a une erreur qui cause un "stack-buffer-overflow" et cela est dû au fait que le tableau à un accès vers la fin qui dépasse. Pour faire simple, c'est-à-dire que la valeur SZ_BUF+1 soit 501 est plus grand que le tableau de 500. Et si GCC ne branche pas, c'est parce qu'il : les bornes de tableau ne sont pas systématiquement vérifiées avec GCC contrairement à d'autres compilateurs comme Clang et d'une part, il considère qu'il y a suffisamment de mémoire à la suite du tableau sauf que ce n'est pas le cas donc UB dans le pire des cas. ici buffer[SZ_BUF] ='\0'; serait plus approprié.
    Ouais. Mais même quand ça t'arrive d'avoir raison t'es tellement pédant que ça en devient fatiguant. Pouvais pas simplement dire que j'avais fait une erreur sur la position du '\0' ? Tu me crois vraiment incapable de le réaliser une fois que tu précises simplement le souci ? Sais pas moi, une simple phrase style "je pense que tu t'es trompé sur la position du \0" plutôt que d'étaler toute ta science avec tes "stack-buffer-overflow" et tes "contrairement à clang, gcc considère que...". D'autant plus qu'il ne considère absolument rien. Il n'est simplement pas programmé pour contrôler ce détail puisque la philosophie du C c'est "le programmeur sait ce qu'il fait".
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]
      3  4

  18. #18
    Nouveau membre du Club
    Homme Profil pro
    Quebec
    Inscrit en
    Octobre 2016
    Messages
    10
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : Canada

    Informations professionnelles :
    Activité : Quebec
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Octobre 2016
    Messages : 10
    Points : 33
    Points
    33
    Par défaut
    Bonsoir,

    Citation Envoyé par Sve@r Voir le message
    Bonjour
    C'est pas bizarre, c'est "construit mathématiquement" pour stocker plusieurs informatiions sur un int.
    Un processus peut s'arrêter de deux façons
    • via exit(n), n compris entre 0 et 255
    • via kill(m), m compris entre 1 et 255

    Chaque nombre "n" et "m" n'occupant qu'un octet, les deux informations accolées tiennent sur un int. Ainsi wait(&status) va attendre la fin d'un fils, et si ce fils s'est terminé par exit(n) va positionner le "n" dans les 8 premiers bits de "status" et si ce fils a été tué via kill(m), va positionner ce "m" dans les 8 derniers bits de "status".
    Ensuite il suffit de regarder. Si les 8 derniers bits sont à 0 c'est que c'était exit(n) et pour retrouver "n" suffit de faire un décalage, sinon c'était kill(m) (m ne pouvant pas être égal à 0) et pour retrouver "m" suffit là de faire un masque. Et en plus comme il est malsain d'écrire un code basé sur des conventions qui peuvent évoluer, il existe 4 macros qu'on peut utiliser pour gérer le truc et qui, elles, suivront l'évolution éventuelle.

    Evidemment afficher directement "status" sans connaitre ces détails rend évidemment la chose bizarre (toutefois tu remarqueras que toutes les valeurs que tu montres dans ton exemple sont toutes égales à n * 256, ce qui équivaut à n décalé à gauche de 8 bits).
    Ne reste que le exit(-1) que tu dis valoir 255 à expliquer mais ça ça reste effectivement inexplicable parce que faux. -1 sur un bit vaut 0xff (255) et 0xff placé sur les 8 bits de gauche d'un int donnent 0xff00 soit 255 * 256 = 65280.
    @fred , je te cite parce que je ne suis pas d'accord sur la manière dont tu te justifies par la suite. Les explications @sambia39 ne sont absolument pas en contradiction avec la tienne, il est écrit que la valeur de statut d'exit transmise au père ne tient que sur 8 bits et qu'il ne faut pas croire que cette information transmise en utilisant un type "int" est une association de valeurs. Là, il t'expose clairement qu'il s'agit des valeurs renvoyées par exit et non le cas du positionnement des bits de statuts par wait ou waitpid; il a donc parfaitement raison d’écrire que tout programme sort avec un code erreur qui tient sur 8 bits et que -1 est effectivement tronqué.

    Il ne mentionne même pas le positionnement des bits que tu décris. Il faut arrêter d’être hypocrite et vouloir penser que ce qu’il dit en te citant est en contradiction. J’ajoute que dans ce que tu écris pour le cas des signaux avec kill, seuls les 6 derniers bits sont positionnés et représentent la valeur du code retourné et l’ensemble du positionnement des bits en fonction des autres signaux n’est pas comme tu le décris, mais passons je vais pas en débatre ici sur comment wait ou waitpid fornctionne...

    Tu reconnais également qu’il n’a pas tord de te signifier que tu as fait une erreur et également il précise pourquoi ton compilateur n'a pas signalé le problème sans te frustrer dans ces explications et toi, tu lui reproches de ne pas l’écrire autrement en pensant que tu n’es pas capable de le savoir ? Sans déconner, tu l’appelles comment le débordement de tampon ? Tu aurais su toi pourquoi l’index hors tableau n’est pas détecté dynamiquement par le compilateur ?
    Time doesn't make wise men, only old men...
      0  1

  19. #19
    Membre expérimenté
    Avatar de sambia39
    Homme Profil pro
    No Comment
    Inscrit en
    Mai 2010
    Messages
    543
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Loiret (Centre)

    Informations professionnelles :
    Activité : No Comment
    Secteur : High Tech - Matériel informatique

    Informations forums :
    Inscription : Mai 2010
    Messages : 543
    Points : 1 745
    Points
    1 745
    Par défaut
    Citation Envoyé par Sve@r Voir le message
    Oui mais comme ce n'est pas ce que j'ai dit je ne vois pas pourquoi tu viens ramener ta fraise !!! C'est juste pour faire chier les gens que tu déformes ou oblitères leur propos ou bien c'est réellement une tare qui t'empêche de comprendre les mots que tu lis ??? Je n'ai jamais dit qu'on pouvait renvoyer un code sur 32 bits, ".
    J'ai ramené ma fraise parce que tu écris "les deux informations accolées tiennent sur un int" et je rajoute par la suite qu'il ne faut pas penser que l'information transmise au père tiens sur un en entier ou sur 32 bits ou croire que les deux informations de kill ou exit accolé justifie le type int, car c'est totalement faux. Aussi, comme tu n'as pas l'air de comprendre pourquoi la valeur -1 est tronquée en 255, je complète en disant que c'est une valeur tronquer au bit le moins significatif, et cela est fait exprès et toi à ton habitude, tu le prends comme une agression.


    De même, tu fais une erreur, je t'explique gentiment pourquoi ton compilateur ne branche pas. Tu me parles de philosophie du C, c'est n'importe quoi. Ne va pas me faire croire que ta philosophie du C, explique ton erreur ou ton compilateur qui ne t'a pas signalé l'ambiguïté de ton [SZ_BUF +1] = '\0' et si des options de compilateur existent, c'est justement pour signaler ce genre de chose.
    Celui qui peut, agit. Celui qui ne peut pas, enseigne.
    Il y a deux sortes de savants: les spécialistes, qui connaissent tout sur rien,
    et les philosophes, qui ne connaissent rien sur tout.
    George Bernard Shaw
      2  2

  20. #20
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 689
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 689
    Points : 30 983
    Points
    30 983
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par sambia39 Voir le message
    J'ai ramené ma fraise parce que tu écris "les deux informations accolées tiennent sur un int"
    Ah super. C'est vrai qu'à lire sans faire l'effort d'essayer de comprendre un minimum c'est sûr qu'on peut entendre le verbe "accolées" comme "en même temps". Pourtant quand je disais qu'un processus peut s'arrêter de 2 façons, c'était pour moi évident que n'importe qui de correctement câblé comprendrait implicitement par "2 façons mutuellement exclusives". Ok, contre la stupidité des hommes, les dieux eux-mêmes luttent en vain (citation de Friedrich von Schiller reprise par Isaac Asimov dans son roman "les dieux eux-mêmes").

    Citation Envoyé par sambia39 Voir le message
    Aussi, comme tu n'as pas l'air de comprendre pourquoi la valeur -1 est tronquée en 255,
    Ben déjà je ne vois même pas pourquoi tu viens nous parler de -1. Moi j'ai parlé de valeurs situées entre 0 et 255 et là tu arrives avec ton -1 qui sort du chapeau. Mais sinon t'as raison, je ne pige absolument rien à la numération binaire.

    Citation Envoyé par sambia39 Voir le message
    De même, tu fais une erreur, je t'explique gentiment pourquoi ton compilateur ne branche pas.
    A ce propos, le verbe exact c'est "broncher" (un synonyme de "réagir"), pas "brancher" (synonyme argotique de "pendre"). La première fois j'ai pensé à une erreur de doigt mais puisque tu insistes... Donc ok, le compilateur ne va pas me pendre, c'est gentil de me prévenir (mais je t'avoue que je n'étais pas vraiment inquiet à ce sujet).

    Citation Envoyé par sambia39 Voir le message
    Tu me parles de philosophie du C, c'est n'importe quoi. Ne va pas me faire croire que ta philosophie du C, explique ton erreur
    Euh non, ce n'est pas ce que j'ai écrit. Et sinon ce n'est pas "ma" philosophie du C mais "la" philosophie du C. Pour aller le plus vite possible, le C ne vérifie rien. C'est son principe de base.

    Citation Envoyé par sambia39 Voir le message
    ou ton compilateur qui ne t'a pas signalé l'ambiguïté de ton [SZ_BUF +1] = '\0'
    Ah là oui c'est exactement ce que j'ai dit. Et c'est effectivement le cas. gcc ne dit absolument rien dans ce genre d'écriture.
    Ou alors (autre hypothèse), il me l'a signalé mais comme je suis trop con je me suis dit "oh non il doit se tromper". Laquelle vas-tu choisir ?

    Citation Envoyé par sambia39 Voir le message
    et si des options de compilateur existent, c'est justement pour signaler ce genre de chose.
    Tu vois, si la situation avait été inversée, moi j'aurais dit "et si l'option de compilation -X ou -Y existe c'est justement..." en donnant ladite option. Toi tu arrives, tu balances tes affirmations gratuites "... des options..." (lesquelles ? à moi de me démerder à les trouver) et tu te barres dans le style "sorcier mystérieux qui délivre le minimum". Donc désolé, je viens de refaire (volontairement, cela va sans dire mais avec toi cela va encore mieux en le disant) l'erreur et j'ai tenté toutes les options que je connais, Pas une ne m'a prévenu du souci.
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]
      2  3

Discussions similaires

  1. retrouver son image perso dans messenger
    Par matrxjean1984 dans le forum Windows XP
    Réponses: 6
    Dernier message: 12/08/2006, 13h24
  2. Compte root inaccessible après changement de shell
    Par Jpountz dans le forum Administration système
    Réponses: 1
    Dernier message: 03/05/2006, 17h24
  3. [Sécurité] retrouver son mot de passe
    Par zahiton dans le forum Langage
    Réponses: 1
    Dernier message: 09/12/2005, 11h49
  4. Réponses: 3
    Dernier message: 03/02/2004, 17h34
  5. [SERVLET] retrouver son chemin
    Par sebos63 dans le forum Servlets/JSP
    Réponses: 2
    Dernier message: 12/09/2003, 08h59

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo