/* * 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 "mysh.h" #include "ipc.h" #include "execute.h" #include "color.h" /* --- Extern --- */ extern Error error; extern boolean exitsh; extern pid_t active; extern int status_cmd; extern int result_cmd; extern char base_path[]; extern pid_list pidlist; /* --- Globale --- */ int job = 1; /* --- 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[], char* envp[]) { //Declaration variables CommandTab ct; int result; char line[BUFFER_SIZE], before[BUFFER_SIZE]; sigset_t sigs_new, sigs_old, sigs_block; //Initialisation structures error_init(); ini_pid_list(&pidlist); //Recup chemain de base de l'application if (getcwd(base_path, sizeof (char) * BUFFER_SIZE) == NULL) { addperror("Impossible de récuperer le chemin actuel"); error.print("Erreur pendant l'initialisation\n"); clean_pid(&pidlist); error.exit_err(); } //Lancement ipc if(!setup_ipc(envp)){ error.print("Erreur pendant l'initialisation\n"); clean_pid(&pidlist); error.exit_err(); } //Preparation affichage sprintf(before, GREEN "%s:" CYAN, 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 == ERR){ 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 == -ERR){ addperror("Impossible de bloquer les signaux de l'ensemble"); error.print("Erreur pendant l'initialisation\n"); clean_pid(&pidlist); error.exit_err(); } //Gestion interuption signal(SIGINT, handler); //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, ">" RESET " "); //Lecture ligne if(get_line(line) == SHELL_ERR){ //error.print("Impossible de lire les commandes\n"); continue; } //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 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_cmd = run(ct, &status_cmd); //Vide le resultat du parse de la ligne de commande clean_command(&ct); } //Nettoyage if(!end_ipc()){ adderror("Impossible de terminer correctement les IPC"); } 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 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; //Ignore les sigint signal(SIGINT, SIG_IGN); //Lance commande 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++, ct.line); //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; } void handler(int sig){ char reponse = ' '; pid_node* pn; //Repositionne le gestionnaire (Ne marche plus apres 1 utilisation) signal(SIGINT, handler); //Si il y a un process actif on le coupe if(active != -1){ if(kill(active, SIGINT) == ERR){ addperror("Impossible de tuer le processus en cours"); } active = -1; printf("\n"); return; } //Sinon demande comfirmation pour finir le programme printf("\n"); while(reponse != 'o' && reponse != 'O' && reponse != 'n' && reponse != 'N'){ //Recup la valeur printf("Voulez vous vraiment quitter ? [O/N] "); if((reponse = getchar()) == EOF){ reponse = ' '; } //Vide l'entrée standard while(getchar() != '\n'); } //Si oui if(reponse == 'n' || reponse == 'N'){ return; } //Coupe tous les processus en fond pn = pidlist.first; while(pn != NULL){ if(kill(pn->pid, SIGINT) == ERR){ addperror("Impossible de tuer le processus en fond"); } pn = pn->next; } //Termine l'execution if(!end_ipc()){ adderror("Impossible de terminer correctement les IPC"); } clean_pid(&pidlist); error.exit(); }