//executa alguns comandos como cd e pwd... int execute_int_commands(StringList arguments){ String command = get_string(arguments, 0); if(equals(command, "cd")){ if(chdir(get_string(arguments, 1))){ printf("Error: no such directory\n"); return 0; } }else if(equals(command, "pwd")){ printf("%s\n", get_current_dir_name()); }else if(equals(command, "jobs")){ print_job_list(job_list); }else if(equals(command,"fg")){ Job aux; int pid = string_to_int(get_string(arguments, 1)); kill(pid, SIGCONT); aux = get_by_pid(job_list, pid); if (aux == NULL) { //busca por jid aux = get_by_jid(job_list, pid); if (aux == NULL) { printf("Error: job not found\n"); return 0; } } aux->status = RUNNING; background = 0; wait(NULL); }else if(equals(command,"bg")){ Job aux; int pid = string_to_int(get_string(arguments, 1)); kill(pid ,SIGCONT); aux = get_by_pid(job_list, pid); if (aux == NULL) { //busca por jid aux = get_by_jid(job_list, pid); if (aux == NULL) { printf("Error: job not found\n"); return 0; } } aux->status = RUNNING; background = 1; }else{ return 0; } return 1; }
int main(int argc, char *argv[]){ jlist = create_job_list(); char *tok; TOKENIZER *tokenizer; int ispipe, redirect_out_flag, redirect_in_flag, not_exec_cmd, is_bg_flag; //read-in buffer char data[BUFFER_MAX]; char extra[1]; //linked list to add argument arrays to execvp: 1 for process 1, 2 for piped process list_elt *argv2_elt = NULL; list_elt *argv1_elt = NULL; message_q = NULL; int argv1size = 0; int argv2size = 0; int status; //set shell pgid sh_pgid = getpgid(0); //register sigaction for SIGCHLD, SIGINT, SIGTSTP struct sigaction handler_action; sigaction(SIGCHLD, NULL, &handler_action); //save previous action for SIGCHLD to handler_action //change what handler_action does handler_action.sa_flags |= SA_SIGINFO; handler_action.sa_flags |= SA_RESTART; handler_action.sa_sigaction = child_signal_handler; sigfillset(&handler_action.sa_mask); // sigaction setup sigaction(SIGCHLD, &handler_action, NULL); sigaction(SIGTSTP, &handler_action, NULL); sigaction(SIGINT, &handler_action, NULL); // igmore the sigint and sigtstp sigmals signal(SIGINT, SIG_IGN); //signal(SIGTSTP, SIG_IGN); while(1){ signal(SIGTTOU, SIG_IGN); //igmore sigttou for tcsetpgrp if (-1 == tcsetpgrp(STDIN_FILENO, sh_pgid)){ perror("tcsetpgrp read error"); } if(-1 == tcsetpgrp(STDOUT_FILENO, sh_pgid)){ perror("tcsetpgrp read error 2"); } signal(SIGTTOU, SIG_DFL); // un-ignmore sigttou print_list(message_q); // print the message_q while(message_q){ message_q = delete(message_q, message_q); //empty the message_q } write(STDOUT_FILENO, "kinda-sh# ", 10); //prompt //initialize all the flags before tokenizing argv1size = 0; argv2size = 0; is_bg_flag = 0; ispipe = 0; redirect_out_flag = 0; redirect_in_flag = 0; int new_std_in=0; // < int new_std_out = 0; // > char* fname_in; char* fname_out; int error_flag=0; not_exec_cmd = 0; fg_pid=0; int read_val; data[0] = '\0'; // null terminate data array read_val = read(0, data, BUFFER_MAX); // read into the data if(data[0] != '\n'){ //check if there is buffer overflow if((read_val == BUFFER_MAX) && (data[BUFFER_MAX - 1] != '\n')){ while(1){ argv2_elt = NULL; argv1_elt = NULL; read(0, extra, 1); // read 1 char at a time from the input stream until its empty (new line) if(extra[0] == '\n'){ //read in until the "return(\n) key is hit (similar to flush the std_in) break; } } } //error handling for inappropriate read size if(read_val < 0){ perror("Read"); //check for read error } if(read_val == 0){ kill(0, SIGKILL); } data[read_val-1] = '\0'; //null terminate dat tokenizer = init_tokenizer(data); //create tokenizer on data // start tokenizer loop while((tok = get_next_token(tokenizer)) != NULL){ if(is_bg_flag){ printf("& should be at the end of the command \n"); error_flag=1; break; } if( (*tok != '|') && (*tok != '&') && (*tok != '<') &&(*tok != '>') && (*tok != '\0')){ //check for token if(!ispipe){ if(redirect_out_flag || redirect_in_flag){ printf("invalid argument between redirection and pipe\n"); //cat < infile something | wc error_flag=1; break; } argv1_elt = add(argv1_elt, tok); // add tok to arv1 argv1size++; } else{ // after pipe - second process argv2size++; argv2_elt = add(argv2_elt, tok); } //if jobs command called if(str_equals(tok, "jobs")){ print_job_list(jlist); not_exec_cmd = 1; } // if fg command called if(str_equals(tok, "fg")){ job* new_bg_jb; char* num; num = get_next_token(tokenizer); int num_int = -1; not_exec_cmd = 1; int list_len; //if number argument is not specified, take the most recent background job if(num == NULL){ new_bg_jb = get_ith_job(jlist, 1); if(new_bg_jb == NULL){ printf("fg error: no job in job queue\n"); break; } } //take the num'th background job else{ num_int= my_atoi(num); // run atoi on input number (ie: fg 2) //reverse the number into the correct job order list_len = listlength(jlist); num_int = num_int - list_len -1; if(num_int < 0){ num_int = num_int * (-1); } new_bg_jb = get_ith_job(jlist, num_int); //get num_int job from the job list if(new_bg_jb == NULL){ printf("fg error: no [%s] job\n", num); free(num); break; } } //take the foreground job pid as the chosen bg pid fg_pid= new_bg_jb->pgid; message_q = add(message_q, "\n"); // add restarting prompt to message_q message_q = add(message_q, new_bg_jb->command); message_q = add(message_q, "Restarting: "); print_list(message_q); while(message_q){ message_q = delete(message_q, message_q); //empty the message_q } if(tcsetpgrp(STDIN_FILENO, fg_pid) == -1){ perror("tcsetpgrp error"); } //relay SIGCONT to the job, and mask it from all signals but SIGCHLD killpg(fg_pid, SIGCONT); sigset_t cont_mask; sigfillset(&cont_mask); sigdelset(&cont_mask, SIGCHLD); //now the shell should wait for the new foregrounded job to be finished while(fg_pid){ sigsuspend(&cont_mask); } } //take the specific background job to restart. If already running, do nothing. if(str_equals(tok, "bg")){ job* new_bg_jb; not_exec_cmd = 1; char* num; int num_int=-1; num = get_next_token(tokenizer); // if num is not specified, get the most recent background job (to restart it) if(num == NULL){ new_bg_jb = get_ith_job(jlist, 1); if(new_bg_jb == NULL){ printf("bg error: no job in job queue\n"); break; } } else{ num_int= my_atoi(num); // run atoi for bg //set up num_int to pass int list_len; list_len = listlength(jlist); num_int = num_int - list_len -1; if(num_int < 0){ num_int = num_int * (-1); } new_bg_jb = get_ith_job(jlist, num_int); // get ith job from job_list if(new_bg_jb == NULL){ printf("bg error: no [%s] job\n", num); free(num); break; } } if(!job_stopped(new_bg_jb)){ // tcsetpgrp(STDIN_FILENO, 0); printf("bg error: job is already running in the background\n"); break; } killpg(new_bg_jb->pgid, SIGCONT); tcsetpgrp(STDIN_FILENO, 0); } } else if(*tok=='>'){ // if redirect out token if(redirect_out_flag){ error_flag=1; printf("multiple stdout redirection is invalid\n"); //printf? break; } else{ fname_out = get_next_token(tokenizer); // get hte next token new_std_out = open(fname_out, O_WRONLY| O_TRUNC | O_CREAT, 0644); // open file if(new_std_out == -1){ perror("stdout Redir Error"); } redirect_out_flag=1; } } else if(*tok=='<'){ //if redirect in token if(ispipe){ printf("invalid stdin redirection after pipe\n"); error_flag=1; break; } else if(redirect_in_flag){ printf("multiple stdin redirection is invalid\n"); error_flag=1; break; } else{ fname_in = get_next_token(tokenizer); // get next token new_std_in = open(fname_in, O_RDONLY); // open the file if(new_std_in == -1){ perror("stdin Redir Error"); } } } else if(*tok=='|'){ //if pipe token if(ispipe){ // cant have more than 1 pipe (didnt do the extra credit) printf("invalid multiple pipes\n"); error_flag=1; break; } else if(redirect_out_flag){ printf("invalid pipe after stdout redirection\n"); error_flag=1; break; } ispipe=1; //set a pipe flag } else if(*tok == '&'){ // if background command is_bg_flag = 1; } } if(is_bg_flag){ data[read_val-2]= '\0'; // delete the '&' from the data array } argv1size++; argv2size++; char *argv1[argv1size]; char *argv2[argv2size]; if(error_flag || not_exec_cmd){ //if not elecutable command (jobs, fg, bg...) or error flag (bad command) while( argv1_elt ){ argv1_elt = delete(argv1_elt, argv1_elt); //empty the linked list argv1_elt } while( argv2_elt ){ argv2_elt = delete(argv2_elt, argv2_elt); //empty the linked list argv2_elt } continue; } //set up argv1 array list_elt *cursor = argv1_elt; list_elt *last; int tempIndex=0; //last pointer to the last element of argv1_elt linked list while(cursor != NULL){ last = cursor; cursor = cursor->next; } //move all values from argv1_elt into argv1 array. while(last != NULL){ argv1[tempIndex]=last->item; last = last->prev; tempIndex++; } argv1[argv1size-1] = NULL; //set up argv2 array if there's a pipe if(ispipe){ cursor = argv2_elt; tempIndex=0; //last pointer to the last element of argv2_elt linked list while(cursor != NULL){ last = cursor; cursor = cursor->next; } //move all values from argv2_elt into argv2 array. while(last != NULL){ argv2[tempIndex]=last->item; //put input after the pipe into an array argv2 last = last->prev; tempIndex++; } argv2[argv2size-1] = NULL; } free_tokenizer( tokenizer ); // tokenizer update done. if((pid=fork()) < 0){ perror("fork1"); } //process & job stuff job* jb; jb = create_job(); // create corresponding job subjob* sj; sj = create_subjob(); // create corresponding subjob set_command(data, jb); if(pid==0){ if(setpgid(0,0)==-1){ perror("setpgid error"); } sj->pid = getpid(); // get subjobs pid jb->pgid = getpgid(pid);//pid=0 } else{ if(setpgid(pid, pid)==-1){ perror("setpgid error"); } // set correct job and subjob pid vals sj->pid = pid; jb->pgid = pid; } set_first_subjob(sj, jb); // link subjob to job add_new_job(jb, jlist); // add the job to the job_list jlist if(pid==0){ signal(SIGINT, SIG_DFL); signal(SIGTSTP, SIG_DFL); if(setpgid(0,0)==-1){ perror("setpgid error"); } if(new_std_out != 0){ // if redirect out command, dup accordingly if(dup2(new_std_out, STDOUT_FILENO) == -1){ //dup2 for > (out) perror("stdout dup2"); _exit(0); } free(fname_out); } if(new_std_in != 0){ // if redirect in command, dup accordingly if(dup2(new_std_in, STDIN_FILENO) == -1){ //dup2 for < (in) perror("stdin dup2"); _exit(0); } free(fname_in); } if(ispipe){ // if pipe command called int filedes[2]; if(pipe(filedes)){//pipe perror("pipe error"); } scnd_pid = fork(); if(scnd_pid < 0){ //print error if the fork failed perror("Fork"); exit(-1); } //create a subjob, set its pid for both child and parent subjob* sj2; sj2 = create_subjob(); if(scnd_pid==0){ sj2->pid = getpid(); } else{ sj2->pid = scnd_pid; } jb->pgid = getpgid(scnd_pid); //add to the job group as the first (most recent) process. set_first_subjob(sj2, jb); set_next_subjob(sj2, sj); pipe_pid=sj2->pid; if(scnd_pid==0){ // process that writes to pipe (program 1) grand child if(close(filedes[0]) == -1){ // close STDIN part of pipe perror("close"); } if(dup2(filedes[1], STDOUT_FILENO) == -1){ // dip for STDOUT perror("pipe dup2 #1"); } status = execvp(argv1[0], argv1); // execute if(status == -1){ perror("execvp program1"); exit(-1); } killpg(0, SIGKILL); _exit(0); // exit the child } else{ //program 2; (first fork) process that reads from pipe sigset_t sigmask; sigfillset(&sigmask); sigdelset(&sigmask, SIGCHLD); while(pipe_pid){ sigsuspend(&sigmask); } if(close(filedes[1]) == -1){ // close STDOUT part of pipe perror("close"); } if(dup2(filedes[0], STDIN_FILENO) == -1){ // dup for the STDIN perror("dup2 (pipe #2)"); } status = execvp(argv2[0], argv2); // execute the second part of pipe if(status == -1){ perror("execvp program2"); } killpg(0, SIGKILL); _exit(0); } } else{ // if not pipe, pid. status = execvp(argv1[0], argv1); if(status == -1){ perror("execvp"); } _exit(0); } } else{ //kinda-sh if(setpgid(pid, pid) ==-1){ perror("setpgid pid"); } if(is_bg_flag){ //if background command, prompt with running printf("Running: %s\n", data); } if(!is_bg_flag){ fg_pid= pid; // if not bacground command, set the fg_pid to be the first fork pid val tcsetpgrp(STDIN_FILENO, pid); } sigset_t sigmask0; sigfillset(&sigmask0); sigdelset(&sigmask0, SIGCHLD); //suspend only if it's not bg process while(fg_pid!=0){ sigsuspend(&sigmask0); } if(new_std_in != 0){ close(new_std_in); //close stdin if used } if(new_std_out != 0){ close(new_std_out); //close stdout if used } } while(argv1_elt){ argv1_elt = delete(argv1_elt, argv1_elt); //empty the linked list argv1_elt } while(argv2_elt){ argv2_elt = delete(argv2_elt, argv2_elt); //empty the linked list argv2_elt } } //if data[0]!=\n }//while end return 0; }
int do_cmd(char *input, char *cwd, history_t history, FILE *in, FILE *out) { char *tokens[SIZE]; char *trim; int len; int num_tokens; int pid; int backgnd = 0; // trim leading whitespace trim = input; while (*trim == ' ') trim++; len = strlen(trim); if (len == 1) return 0; // remove trailing newlines if (trim[len-1] == '\n') trim[len-1] = '\0'; // check to see if the user is trying to replay history right away if (strncmp(trim, "!", 1) == 0) { if (replay_history(history, get_val(trim + sizeof(char)), input)) { fprintf(stderr, "Line '%s' not found\n", trim + sizeof(char)); return 0; } return do_cmd(input, cwd, history, in, out); } // don't store history replay's in the history append_history(history, trim); // tokenize num_tokens = tokenize(trim, tokens); // built-ins // internal exit command if (!strncmp(tokens[0], "quit", 5) || !strncmp(tokens[0], "exit", 5) ) { return EXIT_NORMAL; } // print working directory if (!strncmp(tokens[0], "pwd", 4)) { printf("%s\n", cwd); return 0; } // print all history if (!strncmp(tokens[0], "history", 8)) { print_history(history); return 0; } // print all jobs if (!strncmp(tokens[0], "jobs", 5)) { print_job_list(); return 0; } // change to a directory if (strncmp(tokens[0], "cd", 2) == 0) { if (num_tokens > 2) { if (chdir(tokens[1])) { perror("No such file or directory"); } else { // on success, reset cwd if (getcwd(cwd, sizeof(char) * SIZE) == NULL) { perror("getcwd() error"); return 1; } } } return 0; } // do some token scraping for redirection if (check_for_specials(tokens, num_tokens, &in, &out, &backgnd)) return 0; if ((pid = fork()) < 0) { perror("fork failure"); return 1; } else if (pid == 0) { // allow inpout and output redirection if (in) dup2(fileno(in), STDIN_FILENO); if (out) dup2(fileno(out), STDOUT_FILENO); // kick off this wild ride if (execvp(tokens[0], tokens) < 0) { fprintf(stderr, "No command \'%s\' found\n", tokens[0]); exit(1); } // here be unreachable } else { // add job to list of running jobs len = add_job(pid, tokens); if (!backgnd) { while(last_corpse != pid); printf("Process returned: %d\n", last_corpse_status); } else { printf("[%d] %d\n", len, pid); } return 0; } // The compiler doesn't see the exec, and thinks a // child could make it here fprintf(stderr, "Warning: child escaped!\n"); return 0; }
int main(void) { char inputBuffer[MAX_LINE]; /** buffer to hold the command entered **/ int background; /** equals 1 if a command is followed by '&' **/ int respawnable; /** igual a 1 si al comando le sigue '#' **/ char *args[MAX_LINE / 2]; /** command line (of 256) has max of 128 arguments **/ // probably useful variables: int pid_fork, pid_wait; /** pid for created and waited process **/ int status; /** status returned by wait **/ enum status status_res; /** status processed by analyze_status() **/ int info; /** info processed by analyze_status() **/ ignore_terminal_signals(); /** Ignora las señales del terminal **/ /** Asociamos el manejador a la señal SIGCHLD y creamos la lista de trabajos **/ signal(SIGCHLD, manejador); lista_trabajos = new_list("Lista trabajos"); /** Program terminates normally inside get_command() after ^D is typed **/ while(1) { printf("\nCOMMAND->"); fflush(stdout); /** get next command **/ get_command(inputBuffer, MAX_LINE, args, &background, &respawnable); // if empty command if(args[0] == NULL) { continue; } /** Comando interno CD **/ if(strcmp(args[0], "cd") == 0 && args[1] != NULL) { chdir(args[1]); continue; } /** Comando interno historial **/ if(strcmp(args[0], "historial") == 0) { continue; } /** Comando interno JOBS (HECHO) **/ if(strcmp(args[0], "jobs") == 0) { if(empty_list(lista_trabajos)) { printf("La lista de tareas está vacía\n"); } else { print_job_list(lista_trabajos); } continue; } /** Comando interno BG (HECHO) **/ if(strcmp(args[0], "bg") == 0) { if(args[1] == NULL) { printf("No le has pasado ningún argumento al comando \n"); } else { int index = atoi(args[1]); // Convertimos el indice pasado a un entero bgCommand(index); // Invocamos al comando FG } continue; } /** Comando interno FG (HECHO) **/ if(strcmp(args[0], "fg") == 0) { int index = 1; // Por defecto a la primera tarea de la lista if(args[1] != NULL) { index = atoi(args[1]); // Si no es nulo, lo aplicaremos al correspondiente } fgCommand(index); // Invocamos al comando FG continue; } // the steps are: // (1) fork a child process using fork() pid_fork = fork(); if(pid_fork < 0) { // Caso de error printf("Error. The system wasn able to spawn a new process. Exiting...\n"); exit(-1); } else if(pid_fork == 0) { // Proceso hijo /** Nuevo identificador de grupo de procesos para el hijo, asociacion del terminal y señales a default **/ pid_t mypid = getpid(); new_process_group(mypid); // Metemos al hijo en un nuevo grupo if(!background && !respawnable) { // Le asignamos el terminal set_terminal(mypid); } // Restauramos las señales que reciben del terminal restore_terminal_signals(); // (2) the child process will invoke execvp() execvp(args[0], args); // Solo llegará aquí si no se ha producido el cambio de imagen printf("Error. Command not found: %s\n", args[0]); exit(-1); // Padre } else { /** Nuevo identificador de grupo de procesos para el hijo **/ new_process_group(pid_fork); // El padre se va a un nuevo grupo de trabajo /** Si el programa está en background o es respawnable **/ if(background || respawnable) { /** BLoqueamos la señal SIGCHLD, añadimos el trabajo a la lista(background) y desbloqueamos la señal **/ block_SIGCHLD(); job *aux; // Creamos un nuevo nodo para agregarlo a la lista if(background) { aux = new_job(pid_fork, args[0], args, BACKGROUND); } else if(respawnable) { aux = new_job(pid_fork, args[0], args, RESPAWNABLE); } // Añadimos ese nodo a la lista creada anteriormente add_job(lista_trabajos, aux); unblock_SIGCHLD(); printf("Background job running... pid: %d, command: %s. \n", pid_fork, args[0]); /** Si no, es por que está en foreground **/ } else { /** * Wait con detección de suspension y recuperación del terminal * Con el WUNTRACED se comprueba también si el hijo se suspende **/ set_terminal(pid_fork); // Redundante pid_t wait_pid = waitpid(pid_fork, &status, WUNTRACED); set_terminal(getpid()); /** * pid_fork = waitpid(pid_fork, &status, 0); * esta instruccion ya no es necesaria porque hacemos el wait_pid **/ int info; // Le pasamos la variable "status" que nos acaba de pasar el wait enum status st = analyze_status(status, &info); /** * Si sale del bloqueo debido a la suspensión del estado * lo metemos en la lista de tareas para poder llevar un * seguimiento de los procesos suspendidos **/ if(st == SUSPENDED) { /** BLoqueamos la señal SIGCHLD, añadimos el trabajo a la lista (suspendido) y desbloqueamos la señal **/ block_SIGCHLD(); // Creamos un nuevo nodo para agregarlo a la lista job *aux = new_job(pid_fork, args[0], args, STOPPED); // Añadimos ese nodo a la lista creada anteriormente add_job(lista_trabajos, aux); unblock_SIGCHLD(); } // (4) Shell shows a status message for processed command printf("Foreground pid: %d, command: %s, status: %s, info: %d. \n", wait_pid, args[0], status_strings[st], info); } } // (5) loop returns to get_commnad() function } // end while }