Bonjour,
Je souhaite écrire un programme en C, qui permet d'enregistrer toutes les entrées/sorties d'un bash.
Mon idée est de créer un nouveau processus "enfant" qui va lancé le bash.
Le processus "parent" va s'occuper d'enregistrer les entrées/sorties du processus "enfant".
Pour l'instant j'essaye seulement de connecter STDIN du père a STDIN du fils et STDOUT du fils a STDOUT du père.
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#define BUFF_SIZE 1024
#define NB_PIPE 2
int main(int argc, char **argv){
char *buff;
int pid,i;
int fd[NB_PIPE][2];
/* Création des pipes */
for(i=0;i<NB_PIPE;i++){
if(pipe(fd[i]) == -1){
perror("pipe failed\n");
return 1;
}
}
/* Création du processus fils */
if((pid = fork()) == -1){
perror("fork failed\n");
return 2;
}
/* Processus fils */
if(pid == 0){
/* Connecte STDIN a la sortie du pipe 1 */
close(fd[0][1]);
dup2(fd[0][0],0);
/* Connecte STDOUT a l'entrée du pipe 2 */
close(fd[1][0]);
dup2(fd[1][1],1);
/* Lance le bash */
execve("/bin/bash",NULL,NULL);
}
/* Processus père */
else{
close(fd[0][0]);
close(fd[1][1]);
while(1){
// Allocation
buff = malloc(BUFF_SIZE*sizeof(char));
// Lecture sur STDIN (clavier utilisateur) dans buff
read(0,buff,BUFF_SIZE);
// Ecriture sur le pipe 1 de buff
write(fd[0][1],buff,BUFF_SIZE);
// Lecture sur le pipe 2 dans buff
read(fd[1][0],buff,BUFF_SIZE);
//Ecriture sur STDOUT de buff
write(1,buff,BUFF_SIZE);
// Libère la mémoire
free(buff);
}
}
return 0;
}
Voici un exemple d'exécution :
toto@ThinkPad:~/lab$ ./toto
ls
main.c
toto
toto.c
ls
main.c
toto
toto.c
bash: line 3: oto: command not found
bash: line 4: toto.c: command not found
Le programme ne fonctionne pas bien :
1) Le prompt du bash ne s'affiche pas.
2) Après la deuxième commande une erreur se produit.
J'ai aussi essayé de changer le code du processus père par :
/* Processus père */
else{
/* Connecte STDIN a l'entrée du pipe 1 */
close(fd[0][0]);
dup2(fd[0][1],0);
/* Connecte STDOUT a la sortie du pipe 2 */
close(fd[1][1]);
dup2(fd[1][0],1);
/* Attend la fin du processus fils. */
wait(0);
printf("Fin du processus fils.\n");
}
Mais le programme ne fonctionne pas du tout avec cette version.
Est-ce la bonne manière de faire ?
Si oui quel est le problème dans mon code ?
Sinon comment auriez-vous fait ?
Merci.
# Impossible
Posté par totof2000 . Évalué à 3. Dernière modification le 28 février 2020 à 19:54.
Tu connectes une entrée à une entrée …
puis une sortie à une sortie.
ça peut pas marcher.
Une sortie envoie ses données vers une entrée, et une entrée ne lit que depuis une sortie
Sinon:
https://linux.die.net/man/2/read
On success, the number of bytes read is returned (zero indicates end of file), and the file position is advanced by this number. It is not an error if this number is smaller than the number of bytes requested; this may happen for example because fewer bytes are actually available right now (maybe because we were close to end-of-file, or because we are reading from a pipe, or from a terminal), or because read() was interrupted by a signal. On error, -1 is returned, and errno is set appropriately. In this case it is left unspecified whether the file position (if any) changes.
Tu ne testes pas la valeur de retour de ton write.
Enfin, d'après le man de malloc:
Ce doit être ça la cause de ton problème: pour ne pas l'avoir, tu devrais tester le nombre de caractères récupérés sur stdin ou depuis ton pipe, et n'écrire que le contenu de ton buffer. Tu éviterais le problème de l'erreur lors de la deuxième exécution (tu utilises une zone mémoire réallouée après avoir été libérée, et comme elle est pas initialisée, elle contient des résidus de tes précédentes écritures/lectures).
[^] # Re: Impossible
Posté par totof2000 . Évalué à 2.
Pour le problème de prompt, essaie d'utiliser bash -il
Extrait de bash:
Une autre option qui pourrait t'intéresser:
[^] # Re: Impossible
Posté par Totoro . Évalué à 1.
Merci pour ta réponse, j'ai corrigé.
Voila ou j'en suis :
Exemple :
Le programme fonctionne quasiment.
Le soucis maintenant c'est l'affichage, et toujours pas de prompt.
J'ai essayé les options de bash sans succès.
Je pense que l'erreur vient d'ailleurs car quand j'exécute ce programme :
J'ai ça :
Aussi je ne comprend pas pourquoi le résultat de la commande env est différent.
[^] # Re: Impossible
Posté par totof2000 . Évalué à 2. Dernière modification le 29 février 2020 à 17:33.
Ti as quoi quand tupasses les options i et l a ta commande bash ?
Peux tu stp montrer le source et le résultat ?
[^] # Re: Impossible
Posté par Totoro . Évalué à 1.
Source :
Résultat :
[^] # Re: Impossible
Posté par totof2000 . Évalué à 2. Dernière modification le 29 février 2020 à 18:34.
Si tu regardes la manpage de execve, tu verras que argv[0] doit correspondre au nom du fichier lancé a l'exécution et les paramètres doivent commencer a argv[1].
Regarde la page man il y a un exemple et essaie de t'en inspirer: il me semble que dans l'exemple, argv[0] est a null.
Fais un retour si c ok, ce que tu fais m'intéresse.
[^] # Re: Impossible
Posté par totof2000 . Évalué à 2. Dernière modification le 01 mars 2020 à 09:27.
Pour info, chez moi ça marche (prompt affiché) sous freebsd:
[^] # Re: Impossible
Posté par Totoro . Évalué à 1. Dernière modification le 01 mars 2020 à 11:09.
Alors quand je fait sans pipe() et dup2() ça marche avec le prompt :
Quand je fait :
L'exécution donne :
Le prompt s'affiche mais quand j'exécute "ls" le bash se ferme
et exécute "ls" dans le bash qui appelle main.
L'erreur semble venir du read de STDIN dans le père.
[^] # Re: Impossible
Posté par totof2000 . Évalué à 2.
Je viens d'essayer. Le bash ne se ferme pas mais il se met en arrière plan. Quand tu fais fg et que tu passes une commanbde ça marche. Je pense que c'est parce que bash n'est pas invoqué depuis un terminal.
[^] # Re: Impossible
Posté par totof2000 . Évalué à 2.
Possible aussi …
J'ai plus trop le temps de regarder maintenant. Fais un retour si tu arrives à quelque chose.
[^] # Re: Impossible
Posté par totof2000 . Évalué à 3.
Plop!
Une piste ?
[^] # Re: Impossible
Posté par Totoro . Évalué à 1. Dernière modification le 01 mars 2020 à 17:23.
Je suis entrain de regarder pselect et le paramètre sigmask.
Je vais regarder ton lien.
[^] # Re: Impossible
Posté par David Marec . Évalué à 2.
Euh … vous utilisez le même
argv
que celui passé àmain
là.[^] # Re: Impossible
Posté par David Marec . Évalué à 3. Dernière modification le 01 mars 2020 à 18:11.
Au passage, utilisez les constantes
STDIN_FILENO
etSTDOUT_FILENO
à la place de0
et1
.vous devriez de plus vérifier que le fils ne s'est pas terminé avec waitpid, après chaque timeout sur
select
par exemple.J'ai aussi l'impression que vous allez lancer deux
bash
avec votre execve.Et votre buffer n'est pas initialisé:
[^] # Re: Impossible
Posté par Totoro . Évalué à 1.
Pour execve si j'utilise :
Le prompt ne s'affiche pas.
Je pense avoir corrigé le reste .
[^] # Re: Impossible
Posté par David Marec . Évalué à 2.
Vous n'avez pas correctement alloué
basharg
.Je ne sais pas pourquoi, mais là, j'ai la flemme de faire le tri dans tous ces
pipes
.Utilisez un
pty
comme suggéré. C'est la manière canonique de faire ce genre de chose.[^] # Re: Impossible
Posté par Totoro . Évalué à 1.
Bonjour, ça fonctionne avec un PTY.
Merci pour votre aide.
[^] # Re: Impossible
Posté par totof2000 . Évalué à 2.
Cool, merci pour ton retour ;)
[^] # Re: Impossible
Posté par David Marec . Évalué à 4.
C'est étudié pour ;)
Plusieurs remarques,
Si vous utilisez
fopen
, répondez parfclose
, il est peu probable que vosclose(f)
soient valides. Un stream et un descripteur de fichier sont deux choses différentes.Puisque vous allouez dynamiquement le buffer, faites le uniquement dans le processus père. Sinon, le processus fils va hériter d'une copie.
De même pour les bashargs, dans le fils.
Pour moi, vous lancez toujours un bash … qui lance un autre bash (vérifiez avec ps).
Sortez du
while
tout ce qui ne doit être fait qu'une fois (le timeout).Pas besoin du else, soit
execve
remplace complètement le processus, soit il échoue:si vous voulez voir quelque chose dans votre fichier,
faites des fflush.
Maintenant que vous avez tout écrit à la main, vous pouvez lire le manuel de forkpty
voire les sources.
et utiliser un poll ou un epoll.
C'est con tous ces #define _XOPEN_SOURCE et autres, hein.
Pour éviter ce bordel, passez sous FreeBSD.
[^] # Re: Impossible
Posté par totof2000 . Évalué à 2.
Hello. Ce détail m'a échappé, et je ne l'ai pas constaté dans mes essais précédents. Qu'est-ce qui vous fait dire ça ?
[^] # Re: Impossible
Posté par David Marec . Évalué à 2.
En fait, vous avez raison, j'ai confondu avec la famille des exec().
Le premier argument de execve est par convention le nom du programme.
J'ai exhumé ceci:
Je me demande si le fonctionnement n'est pas différent sous FreeBSD.
[^] # Re: Impossible
Posté par David Marec . Évalué à 3.
Pour compléter, ceci sert lorsque un programme est un lien vers un autre, plus générique.
Et que le comportement de ce dernier dépend du nom sous lequel on l'a appelé, comme les utilitaires busybox par exemple.
[^] # Re: Impossible
Posté par totof2000 . Évalué à 2.
J'ai essayé sous FreeBSD, de mémoire, le comportement est identique.
D'après la page man:
Cordialement.
Suivre le flux des commentaires
Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.