diff options
Diffstat (limited to 'shell.c')
-rw-r--r-- | shell.c | 261 |
1 files changed, 154 insertions, 107 deletions
@@ -80,12 +80,10 @@ static void init_params(struct param_type *params, enum modes *current_mode) params->streams.output_stream_to_append = NULL; params->pipeline = 0; params->new_readline = 1; - params->general_pgid = -1; params->general_pipe_pgid = -1; - params->general_subshell_pgid = -1; params->subshell = 0; params->background_process = 0; - params->fd_read_for_channel = -1; + params->fd_read_for_pipeline = -1; params->fd_read_for_subshell = -1; *current_mode = word_separation; last_execution_status = 0; @@ -117,8 +115,8 @@ static void reset_params(struct param_type *params, readline_reset_array(readline); clear_filename(params); init_params(params, current_mode); - if(params->fd_read_for_channel != -1) - close(params->fd_read_for_channel); + if(params->fd_read_for_pipeline != -1) + close(params->fd_read_for_pipeline); if(params->fd_read_for_subshell != -1) close(params->fd_read_for_subshell); } @@ -176,16 +174,25 @@ void error_identification(const struct param_type *params) print_error_msg(error_code_to_token(params->wrong_command)); } +/* + * Set params->tokens if possible + */ static int special_token_handling(struct w_queue *word_chain, struct c_queue *cmdlines, struct dynamic_array *tmp_word, int *ch, struct param_type *params, struct readline_type *readline) { - return - stream_redirect_tokens(word_chain, tmp_word, *ch, params, readline) ? - 1 : special_tokens(word_chain, cmdlines, tmp_word, ch, params, - readline); + int redirect_token_result, special_tokens_result; + + redirect_token_result = + stream_redirect_tokens(word_chain, tmp_word, *ch, params, readline); + if(redirect_token_result) + return 1; + + special_tokens_result = + special_tokens(word_chain, cmdlines, tmp_word, ch, params, readline); + return special_tokens_result; } static void wait_for_process_to_complete(struct p_queue *pid_store) @@ -242,8 +249,16 @@ static void postprocessing(struct w_queue *word_chain, struct c_queue *cmdlines, close_files(input_fd, output_fd); if(!params->background_process) { wait_for_process_to_complete(pid_store); + /* + * The SIGTTOU signal is sent to a group of background + * processes when attempting to write to the terminal or + * change settings -- this also includes attempting to change + * the current foreground group of terminal (tcsetpgrp). + * https://ru.stackoverflow.com/q/1472943 + */ signal(SIGTTOU, SIG_IGN); terminal_fd = get_true_terminal_fd(); + /* return terminal pgid as foreground group */ tcsetpgrp(terminal_fd, getpid()); } p_queue_clear(pid_store); @@ -282,36 +297,23 @@ int is_stream_redirection_set(const struct param_type *params) params->streams.output_stream_to_append; } -static void identify_general_pipe_pgid(struct param_type *params, int pid) +static void set_foreground_group(struct param_type *params, int pid) { int terminal_fd; - if(params->general_pipe_pgid == -1) - params->general_pipe_pgid = pid; - if(!params->background_process) { - /* foreground group */ - terminal_fd = get_true_terminal_fd(); - signal(SIGTTOU, SIG_IGN); - tcsetpgrp(terminal_fd, params->general_pipe_pgid); - } -} -static void identify_general_pgid(struct param_type *params, int pid) -{ - int terminal_fd; - if(params->general_pgid == -1) - params->general_pgid = pid; if(!params->background_process) { /* foreground group */ terminal_fd = get_true_terminal_fd(); signal(SIGTTOU, SIG_IGN); - tcsetpgrp(terminal_fd, params->general_pgid); + tcsetpgrp(terminal_fd, pid); } } static int find_subshell_after_pipe(struct readline_type *readline) { int i; - for(i = readline->considered_index; i <= readline->last_element_index; ++i) { + for(i = readline->considered_index; i <= readline->last_element_index; ++i) + { if(readline->arr[i] == ' ') continue; if(readline->arr[i] == '(') @@ -343,42 +345,39 @@ static void make_pipeline(struct w_queue *word_chain, struct c_queue *cmdlines, char **cmdline = NULL; int fd[2]; int subshell_fd[2]; - int save_read_fd, pid, subshell_after; + int save_read_fd, pid, subshell_after, subshell_before; subshell_after = find_subshell_after_pipe(readline); - cmdline = c_queue_pop(cmdlines); + subshell_before = params->fd_read_for_pipeline != -1 ? 1 : 0; + cmdline = c_queue_pop(cmdlines); + /* + * If there's more than one element in pipeline, then create a pipeline + * otherwise, one element is used, and then a subshell appears, then the + * channel will be shared between them. + */ if(!c_queue_is_empty(cmdlines)) pipe(fd); - - set_signal_disposition(params); - -#if 0 - if(params->fd_pipe_rd != -1) - /* set foreground group by subshell process */ - identify_general_pgid(params, params->general_pgid); -#endif - - if(c_queue_is_empty(cmdlines) && subshell_after) + else if(subshell_after) pipe(subshell_fd); + set_signal_disposition(params); pid = fork(); -#if 0 - /* parent process */ - if(pid) - identify_general_pgid(params, pid); -#endif + /* parent */ if(pid) { - /* else set foreground group by first pipe process */ - if(params->general_pipe_pgid == -1) - identify_general_pipe_pgid(params, pid); + /* + * Determine the shared pgid for all next pipe elements + */ + if(!subshell_before) + params->general_pipe_pgid = pid; } - + /* =================== first process from pipeline ===================*/ + /* child */ if(pid == 0) { - if(params->fd_read_for_channel != -1) { - dup2(params->fd_read_for_channel, 0); - close(params->fd_read_for_channel); + if(subshell_before) { + dup2(params->fd_read_for_pipeline, 0); + close(params->fd_read_for_pipeline); } if(!c_queue_is_empty(cmdlines)) { close(fd[0]); @@ -391,28 +390,40 @@ static void make_pipeline(struct w_queue *word_chain, struct c_queue *cmdlines, } if(is_stream_redirection_set(params)) { change_streams(input_fd, output_fd, 1, 1); - if(params->fd_read_for_channel != -1 && c_queue_is_empty(cmdlines)) + if(params->fd_read_for_pipeline != -1 && c_queue_is_empty(cmdlines)) change_streams(input_fd, output_fd, 1, 0); } /* change pgid */ - setpgid(getpid(), params->general_pipe_pgid == -1 ? getpid() : - params->general_pipe_pgid); + setpgid(getpid(), subshell_before == 1 ? + params->general_pipe_pgid : getpid()); + set_foreground_group(params, subshell_before == 1 ? + params->general_pipe_pgid : getpid()); execvp(cmdline[0], cmdline); perror(cmdline[0]); exit(1); } + /* ================= parent process ==================*/ p_queue_push(&pid_store, pid); - if(params->fd_read_for_channel != -1) - close(params->fd_read_for_channel); - + /* + * first process read data from subshell (via pipeline) + */ + if(subshell_before) + close(params->fd_read_for_pipeline); + /* + * last process in pipeline. Next comes subshell. We recorded all data in + * pipeline. Saved read descriptor so that subshell can use it later. + */ if(subshell_after && c_queue_is_empty(cmdlines)) { params->fd_read_for_subshell = subshell_fd[0]; close(subshell_fd[1]); } - + /* + * first process wrote the data to pipeline. Leaving only read descriptor + */ if(!c_queue_is_empty(cmdlines)) close(fd[1]); + /* extracting the remaining programs from pipeline */ while(!c_queue_is_empty(cmdlines)) { cmdline = c_queue_pop(cmdlines); /* if not last process in pipeline*/ @@ -421,8 +432,8 @@ static void make_pipeline(struct w_queue *word_chain, struct c_queue *cmdlines, pipe(fd); } else if(subshell_after) pipe(subshell_fd); - pid = fork(); +/* =============== child process ================== */ if(pid == 0) { if(!c_queue_is_empty(cmdlines)) { dup2(save_read_fd, 0); @@ -450,26 +461,26 @@ static void make_pipeline(struct w_queue *word_chain, struct c_queue *cmdlines, perror(cmdline[0]); exit(1); } +/*============= parent process ==============*/ if(!c_queue_is_empty(cmdlines)) { close(save_read_fd); close(fd[1]); } else { + /* last process */ close(fd[0]); if(subshell_after) close(subshell_fd[1]); } p_queue_push(&pid_store, pid); } +/*============= parent process ==============*/ if(subshell_after) params->fd_read_for_subshell = subshell_fd[0]; else params->fd_read_for_subshell = -1; postprocessing(word_chain, cmdlines, &pid_store, params, input_fd, output_fd); -#if 0 - if(!subshell_after) - params->general_pgid = -1; -#endif + params->pipeline = 0; } static void open_files(const struct param_type *params, int *input_fd, @@ -553,28 +564,28 @@ static void run_external_program(struct w_queue *word_chain, perror("fork error"); exit(1); } -#if 0 - /* parent process */ - if(pid) - identify_general_pgid(params, getpid()); -#endif - /* child process */ if(pid == 0) { if(is_stream_redirection_set(params)) change_streams(input_fd, output_fd, 0, 0); - if(params->fd_read_for_channel != -1) { - dup2(params->fd_read_for_channel, 0); - close(params->fd_read_for_channel); + /* + * In the case where the subshell sets fd_read_for_pipeline + * upon encountering a pipe ahead, but then the subshell + * is started again. We then read from fd_read_for_pipeline + * + * Example: ()|() + * + * Here we start the process in a subshell (second '('). + */ + if(params->fd_read_for_pipeline != -1) { + dup2(params->fd_read_for_pipeline, 0); + close(params->fd_read_for_pipeline); } /* set foreground process */ - if(params->subshell && params->general_pgid != -1) - setpgid(getpid(), params->general_pgid); - else { - identify_general_pgid(params, getpid()); - setpgid(getpid(), getpid()); - } + set_foreground_group(params, getpid()); + /* make the process group as a pid */ + setpgid(getpid(), getpid()); execvp(cmdline[0], cmdline); perror(cmdline[0]); exit(1); @@ -583,11 +594,6 @@ static void run_external_program(struct w_queue *word_chain, p_queue_push(&pid_store, pid); postprocessing(word_chain, cmdlines, &pid_store, params, input_fd, output_fd); - /* ??? */ -#if 0 - if(!params->subshell) - params->general_pgid = -1; -#endif } } @@ -720,6 +726,9 @@ static void generate_readline(struct readline_type *readline) restore_terminal_settings(&cur_terminal_settings, &save_terminal_settings); } +/* + * Used to run a single program or pipeline + */ static void command_processing(struct param_type *params, enum modes *current_mode, struct w_queue *word_chain, @@ -752,21 +761,19 @@ static void command_processing(struct param_type *params, add_word(word_chain, tmp_word, params); if(word_chain->first == NULL && cmdlines->first == NULL) { - /* if not ending subshell */ - if(params->tokens != '(') + /* + * Case ()|() + * Here we're on second '(' + * Empty programs in pipeline + * Subshell (second) read data from pipe via + * fd_read_for_pipeline (run_external_program()) + */ + if(params->tokens != '(') { params->wrong_command = err_empty_command; + params->pipeline = 0; + } goto clean; } -#if 0 - if((ch == new_line || ch == end_subshell) && - ((params->tokens == and && last_execution_status == 1) || - (params->tokens == or && last_execution_status == 0))) { - if(ch == end_subshell && params->subshell) - exit(1); - params->wrong_command = err_set_failure; - goto clean; - } -#endif /* not subshell process; was &&/|| command\n */ if(ch == new_line && ((params->tokens == and && last_execution_status == 1) || @@ -855,18 +862,18 @@ static void make_subshell(struct readline_type *readline, if(pipeline_after) { pipe(fd); - params->fd_read_for_channel = fd[0]; + params->fd_read_for_pipeline = fd[0]; } pid = fork(); - /* child */ + /* child -- subshell */ if(pid == 0) { params->subshell = 1; params->pipeline = 0; if(pipeline_after) { - identify_general_pipe_pgid(params, getpid()); - params->general_pgid = getpid(); - setpgid(getpid(), params->general_pipe_pgid); + /* all processes in pipeline will have the same pgid */ + setpgid(getpid(), getpid()); + set_foreground_group(params, getpid()); close(fd[0]); dup2(fd[1], 1); close(fd[1]); @@ -874,11 +881,19 @@ static void make_subshell(struct readline_type *readline, if(pipeline_before) { dup2(params->fd_read_for_subshell, 0); close(params->fd_read_for_subshell); - params->general_pgid = params->general_pipe_pgid; + /* + * pipeline has already created a process group + * subshell is a member of pipeline + */ setpgid(getpid(), params->general_pipe_pgid); } /* parent */ } else { + /* + * Delay to let the subshell (child process) finish and set + * last_execution_status, so the parent + * can check it — useful for && and ||. + */ sleep(1); if(pipeline_after) { close(fd[1]); @@ -895,6 +910,8 @@ static void word_separation_processing(int ch, struct param_type *params, struct dynamic_array *tmp_word, struct readline_type *readline) { + int token_handling_result; + /* could be a marker for the beginning of a blank word */ params->stored_symbol = ch; if(is_empty_word(ch, *params)) { @@ -913,11 +930,20 @@ static void word_separation_processing(int ch, struct param_type *params, params->escape_sequences = 1; return; } - if(special_token_handling(word_chain, cmdlines, tmp_word, &ch, params, - readline)) { + token_handling_result = + special_token_handling(word_chain, cmdlines, tmp_word, &ch, params, + readline); + /* was found special token */ + if(token_handling_result) { if(command_execution_condition(params, readline)) command_processing(params, current_mode, word_chain, cmdlines, tmp_word, readline, ch); + /* + * The start_subshell token acts as a special marker. + * It helps determine the point at which programs (or a pipeline) + * should start. However, even after that, we still launch a + * subshell process. + */ if(params->tokens == start_subshell) make_subshell(readline, params); return; @@ -926,8 +952,25 @@ static void word_separation_processing(int ch, struct param_type *params, error_identification(params); /* subshell termination failed; failure with (&&, ||) too */ - if(params->subshell) - exit(1); + if(params->subshell) { + /* + * (proc_1 && proc_2) -> false + * proc_1 went wrong + */ + if(params->tokens == and) + exit(1); + /* + * (proc_1 || proc_2) -> true + * proc_1 was completed successfully + */ + else if(params->tokens == or) + exit(0); + /* + * other errors related to subshell termination + */ + else + exit(1); + } clean_input_buffer(); reset_params(params, current_mode, word_chain, cmdlines, tmp_word, @@ -1022,19 +1065,23 @@ int main() params.new_readline = 0; if(ch == new_line) + /* also contains the following calls to generate_readline */ command_processing(¶ms, ¤t_mode, &word_chain, &cmdlines, &tmp_word, &readline, ch); else if(current_mode == word_separation) { word_separation_processing(ch, ¶ms, ¤t_mode, &word_chain, &cmdlines, &tmp_word, &readline); + /* + * parent process skiped subshell part + * move to position after ( + */ if(params.tokens == start_subshell && !params.subshell) { i = readline.considered_index; continue; } /* - * either a double token was found - * or the subshell part was skipped - */ + * double token was found + */ if(readline.considered_index != i) { i = readline.considered_index; continue; |