/* * File: mysh.c * Author: Arthur Brandao * * Created on 31 octobre 2018, 12:43 */ #define _POSIX_C_SOURCE 2 #include #include #include #include #include #include #include #include #include "error.h" #include "str.h" #include "parser.h" #include "command.h" #include "execute.h" #include "mysh.h" /* --- Extern --- */ extern Error error; extern boolean exitsh; /* --- Global --- */ pid_list pidlist; pid_node* active = NULL; int job = 1; /* --- Fonctions privées --- */ void test_write() { char* a = "azerty\n"; int tmp = write(1, a, strlen(a)); printf("%d\n", tmp); } void test_tmp_file(){ int a; FILE* f = tmpfile(); printf("F : %d\n", f == NULL); int fd = fileno(f); printf("%d : %ld\n", fd, write(fd, "bonjour", 8)); a = lseek(fd, 0L, SEEK_SET); sleep(2); char buf[10]; memset(buf, 0 , 10); a = read(fd, buf, 10); printf("%d : %s\n", a, buf); close(fd); } void test(){ CommandTab ct; char str[BUFFER_SIZE]; int a; //Initialisation structures error_finit("mysh.log"); ini_pid_list(&pidlist); //Recup ligne //printf("%s\n", fgets(str, 500, stdin));& memset(str, 0, 500); a = read(STDIN, str, 500); printf("%s\n", str); //Separe les commandes a = parse_line(&ct, str); if(a == SHELL_ERR){ addserror("Erreur lors du parse de la ligne"); error.exit_err(); } //Parse les commandes a = parse_all_command(&ct); if(a == SHELL_FAIL){ addserror("Erreur lors du parse des commandes"); error.exit_err(); } //Affiche resultat for (int i = 0; i < ct.length; i++) { Command* c = ct.cmd[i]; printf("Commande %d (%s) : \n", i, c->name); for (int j = 0; j < c->argc; j++) { printf(" Argument %d : %s\n", j, c->argv[j]); } printf("Commande en fond : %d\n\n", ct.bck); //Si c'est une commande interne on l'execute if(is_internal_cmd(ct.cmd[i]->name)){ show_current_dir(NULL, "\n"); printf("Result : %d\n", launch_internal_command(ct.cmd[i])); show_current_dir(NULL, "\n"); } } //Nettoyage structures clean_command(&ct); clean_pid(&pidlist); error.exit(); } /* --- Fonctions utilitaires --- */ void show_current_dir(const char* before, const char* after) { char buffer[BUFFER_SIZE]; if (getcwd(buffer, sizeof (buffer)) == NULL) { addperror("Erreur getcwd()"); } else { if(before == NULL && after == NULL){ printf("%s", buffer); } else if(before == NULL){ printf("%s%s", buffer, after); } else if(after == NULL){ printf("%s%s", before, buffer); } else { printf("%s%s%s", before, buffer, after); } } fflush(stdout); } int get_line(char* buffer){ memset(buffer, 0, BUFFER_SIZE); if(read(STDIN, buffer, BUFFER_SIZE) == ERR){ addperror("Impossible de lire dans STDIN"); return SHELL_ERR; } return SHELL_OK; } int get_tmp_file(){ FILE* f = tmpfile(); if(f == NULL){ adderror("Impossible de créer un fichier temporaire"); return SHELL_ERR; } return fileno(f); } /* --- Main --- */ int main(int argc, char* argv[]) { //Declaration variables CommandTab ct; int result; char line[BUFFER_SIZE], before[BUFFER_SIZE]; sigset_t sigs_new, sigs_old, sigs_block; //Initialisation structures error_finit("mysh.log"); ini_pid_list(&pidlist); //Preparation affichage sprintf(before, "\x1b[32m%s:\x1b[36m", getlogin()); //Traitement des signeaux result = sigemptyset(&sigs_new); if(result == ERR){ addperror("Impossible de récuperer un ensemble de signaux vide"); error.print("Erreur pendant l'initialisation\n"); clean_pid(&pidlist); error.exit_err(); } //On bloque que sigchld result = sigaddset(&sigs_new, SIGCHLD); if(result == -1){ addperror("Impossible d'ajouter SIGCHLD à l'ensemble de signaux"); error.print("Erreur pendant l'initialisation\n"); clean_pid(&pidlist); error.exit_err(); } result = sigprocmask(SIG_BLOCK, &sigs_new, &sigs_old); if(result == -1){ addperror("Impossible de bloquer les signaux de l'ensemble"); error.print("Erreur pendant l'initialisation\n"); clean_pid(&pidlist); error.exit_err(); } //Boucle infini de lecture while(!exitsh){ //On regarde si un fils en fond est mort if(sigpending(&sigs_block) == ERR){ addperror("Impossible de recuperer les signaux en attentes"); } else if(sigismember(&sigs_block, SIGCHLD)){ job--; } //Affichage repertoire show_current_dir(before, ">\x1b[0m "); //Lecture ligne if(get_line(line) == SHELL_ERR){ error.print("Impossible de lire les commandes\n"); clean_pid(&pidlist); error.exit_err(); } //Parse la ligne et commandes result = parse_line(&ct, line); if(result == SHELL_ERR){ error.print("Impossible d'analyser la ligne\n"); addserror("Erreur lors du parse de la ligne"); continue; } //Si aucune commande on passe printf("Nb cmd : %d\n", ct.length); if(ct.length == 0){ clean_command(&ct); continue; } //Parse les commandes result = parse_all_command(&ct); if(result == SHELL_FAIL){ error.print("Impossible d'analyser la commande\n"); addserror("Erreur lors du parse des commandes"); continue; } //Execute result = run(ct, NULL); printf("Result : %d\n", result); //Vide le resultat du parse de la ligne de commande clean_command(&ct); } //Nettoyage clean_pid(&pidlist); error.end(); return EXIT_SUCCESS; } int run(CommandTab ct, int* status){ pid_t pid; int result = 0; //Si en fond creation d'un fork pour executer les commandes printf("bck : %d\n", ct.bck); if(ct.bck){ pid = fork(); if(pid == ERR){ addperror("Erreur lors du fork pour l'execution des commandes"); error.print("Erreur systeme, impossible de continuer\n"); return SHELL_ERR; } //Fils if(pid == 0){ int stat = 0; ct.bck = 0; result = run(ct, &stat); //Message de fin + retour if(result == SHELL_FAIL){ printf("\n%s (jobs=[%d], pid=%d) terminée avec status=-1\n", ct.line, job, getpid()); exit(EXIT_FAILURE); } printf("\n%s (jobs=[%d], pid=%d) terminée avec status=%d\n", ct.line, job, getpid(), stat); exit(EXIT_SUCCESS); } printf("[%d] %d\n", job, pid); //Ajout du fils dans la liste des pid add_pid(&pidlist, pid, job++); //Pour le pere c'est fini return SHELL_OK; } //Sinon execution de chaque commande Command* c; int tube[ct.length][2]; int tubepos = 0; int infd = -1, outfd = -1, errfd = -1; boolean bpipe = false, skippipe = false, skip = false; //Parcours les commandes for(int i = 0; i < ct.length; i++){ c = ct.cmd[i]; //Si on skip if(skip){ skip = false; continue; } //Si pipe avant if(skippipe){ //Si fin chaine pipe if(c->next != SHELL_PIPE){ skippipe = false; } //Passe la commande continue; } //Si pipe creation d'un fichier commun skippipe = c->next == SHELL_PIPE ; if(c->next == SHELL_PIPE && c->output == STDOUT){ skippipe = false; bpipe = true; //Creation tube if(pipe(tube[tubepos]) == ERR){ addperror("Impossible de créer un tube"); return SHELL_FAIL; } //Redirection c->output = tube[tubepos][TUBE_ECRITURE]; if(ct.cmd[i + 1]->input == STDIN){ ct.cmd[i + 1]->input = tube[tubepos][TUBE_LECTURE]; } } //Effectue les redirections IO if(c->input != STDIN){ infd = redirect_fd2(STDIN, c->input); if(infd == ERR){ return SHELL_FAIL; } } if(c->output != STDOUT){ outfd = redirect_fd2(STDOUT, c->output); if(outfd == ERR){ return SHELL_FAIL; } } if(c->error != STDERR){ errfd = redirect_fd2(STDERR, c->error); if(errfd == ERR){ return SHELL_FAIL; } } //Execute la commande if(is_internal_cmd(c->name)){ result = launch_internal_command(c); } else if(is_executable_file(c->name)){ result = exec_file(c->name, c->argv); } else { result = exec_shell(c->name, c->argv); } //Si on a une variable pour stocker le status de retoure if(status != NULL){ *status = result; } //Reset IO if(c->input != STDIN){ infd = redirect_fd(STDIN, infd); if(infd == ERR){ return SHELL_FAIL; } } if(c->output != STDOUT){ outfd = redirect_fd(STDOUT, outfd); if(outfd == ERR){ return SHELL_FAIL; } } if(c->error != STDERR){ errfd = redirect_fd(STDERR, errfd); if(errfd == ERR){ return SHELL_FAIL; } } //Fermeture tube if(bpipe){ bpipe = false; if(close(outfd) == ERR){ addperror("Impossible de fermer tube ecriture"); return SHELL_FAIL; } if(close(c->output) == ERR){ addperror("Impossible de fermer tube ecriture"); return SHELL_FAIL; } c->output = STDOUT; } //Agit en fonction de la jointure avec la prochaine commande if(c->next == SHELL_IF){ if(result != EXIT_SUCCESS){ skip = true; } } else if(c->next == SHELL_ELSE){ if(result == EXIT_SUCCESS){ skip = true; } } } if(result != EXIT_SUCCESS){ return SHELL_FAIL; } return SHELL_OK; }