#include #include #include #include #include #include #include #include #include #include enum command_mode { mode_background, mode_foreground }; struct command{ int argc; char**argv; char* cmdline; enum command_mode mode; }; /* List of pending processes */ struct process { struct process *next; struct command* cmd; pid_t pid; struct timespec start_time; }; struct process* processes = NULL; /* duration in millisecond between 2 timestamps */ #define TIME_DIFF(t1, t2) (((t2).tv_sec - (t1).tv_sec)*1e3 + ((t2).tv_nsec - (t1).tv_nsec)*1e-6) /* free a command structure */ void free_cmd(struct command* cmd) { free(cmd->cmdline); free(cmd->argv); free(cmd); } /* process the completion of a background process */ void complete_process(pid_t pid) { /* search for pid in the bg_commands list */ struct process* cmd = processes; struct process* prev = NULL; while(cmd) { if(cmd->pid == pid) { /* remove cmd from the list */ if(prev) { prev->next = cmd->next; } else { /* cmd is the first of the list */ processes = cmd->next; } struct timespec stop_time; clock_gettime(CLOCK_MONOTONIC, &stop_time); if(cmd->cmd->mode == mode_background) { printf("[%d] Completed - %s (duration: %f ms)\n", pid, cmd->cmd->cmdline, TIME_DIFF(cmd->start_time, stop_time)); } else { printf("Completed in %f ms\n", TIME_DIFF(cmd->start_time, stop_time)); } free_cmd(cmd->cmd); return; } /* jump to the next token */ prev = cmd; cmd = cmd->next; } /* the process was not found in the list */ printf("Error: process %d finished, but I can't find it\n", pid); abort(); } /* create a command structure from a command line */ struct command* extract_command(char* cmdline) { struct command* c = malloc(sizeof(struct command)); c->argc = 0; c->argv = NULL; c->cmdline = malloc(sizeof(char)*(strlen(cmdline)+1)); strcpy(c->cmdline, cmdline); /* first, let's count the number of parameters */ char* token = strtok(cmdline, " "); while(token) { c->argc++; token = strtok(NULL, " "); } /* strtok modified cmdline, so let's restore it */ strcpy(cmdline, c->cmdline); /* now, extract the parameters */ c->argv = malloc(sizeof(char*) * (c->argc+1)); c->argv[0] = strtok(cmdline, " "); int i; for(i=1; iargc; i++) { c->argv[i] = strtok(NULL, " "); } if(c->argc && strcmp("&", c->argv[c->argc-1]) == 0) { c->argc--; c->mode = mode_background; } else { c->mode = mode_foreground; } c->argv[c->argc] = NULL; return c; } /* execute a command */ void execute_command(struct command* c) { if(c->argc > 0) { pid_t pid_child = fork(); if(!pid_child) { // create a process group for this particular process setpgid(0, 0); execvp(c->argv[0], c->argv); /* execvp should not exit except in case of an error */ fprintf(stderr, "Error running command %s: %s\n", c->argv[0], strerror(errno)); exit(EXIT_FAILURE); } else { /* Add the new process to the list of pending processes */ struct process* process = malloc(sizeof(struct process)); process->cmd = c; process->pid = pid_child; clock_gettime(CLOCK_MONOTONIC, &process->start_time); process->next = processes; processes = process; struct itimerval timer; timer.it_interval.tv_sec=0; timer.it_interval.tv_usec=0; timer.it_value.tv_sec=2; timer.it_value.tv_usec=500000; if(setitimer(ITIMER_REAL, &timer, NULL) < 0) perror("setitimer failed"); if(c->mode == mode_foreground) { int status; waitpid(pid_child, &status, 0); complete_process(pid_child); } else { /* background mode */ printf("(in the background: process %d)\n", pid_child); } } } int status; pid_t pid; while( (pid = waitpid(-1, &status, WNOHANG)) > 0) { complete_process(pid); } } void kill_pending_processes() { struct process* p = processes; while(p) { int pid = p->pid; printf("killing %d\n", pid); int ret = kill(pid, SIGINT); if(ret < 0) perror("kill failed"); p = p->next; int status; waitpid(pid, &status, 0); complete_process(pid); } } void kill_processes_too_long() { struct process* p = processes; printf("received sigalrm\n"); struct timespec cur_t; clock_gettime(CLOCK_MONOTONIC, &cur_t); while(p) { int pid = p->pid; double duration = TIME_DIFF(p->start_time, cur_t); p = p->next; if(duration > 2500) { printf("I should be killing %d that took too long\n", pid); int ret = kill(pid, SIGINT); if(ret < 0) perror("kill failed"); } } } void handle_signal(int signo) { printf("Received signal %d\n", signo); if(signo == SIGINT) { kill_pending_processes(); exit(0); } else if(signo == SIGALRM) { kill_processes_too_long(); } } int main(int argc, char** argv){ struct sigaction sa = {0}; sa.sa_handler = handle_signal; sigaction(SIGINT, &sa, NULL); sigaction(SIGALRM, &sa, NULL); do { char *cmdline = NULL; /* print a prompt, and return a buffer that contains the user command */ cmdline = readline("mysh $ "); /* extract the command, and execute it */ struct command* cmd = extract_command(cmdline); if(cmd->argc > 0 && strcmp(cmd->argv[0] , "exit") == 0) { kill_pending_processes(); free(cmd->cmdline); free(cmd->argv); free(cmd); free(cmdline); return EXIT_SUCCESS; } execute_command(cmd); free(cmdline); cmdline = NULL; } while(1); return EXIT_SUCCESS; }