back to scratko.xyz
summaryrefslogtreecommitdiff
path: root/shell.c
diff options
context:
space:
mode:
authorscratko <m@scratko.xyz>2024-11-23 01:24:26 +0300
committerscratko <m@scratko.xyz>2024-11-23 22:18:00 +0300
commit765c24a70be0b968a08bbd3c26b1644843863fcd (patch)
tree9996d731e6a675b0a4cfe307d936b252b1903c3a /shell.c
parent52f877a23cd26f57393e20b6fffef0dc4e093315 (diff)
downloadshell-765c24a70be0b968a08bbd3c26b1644843863fcd.tar.gz
shell-765c24a70be0b968a08bbd3c26b1644843863fcd.tar.bz2
shell-765c24a70be0b968a08bbd3c26b1644843863fcd.zip
Shell-VII releaseshell-VII
Diffstat (limited to 'shell.c')
-rw-r--r--shell.c401
1 files changed, 345 insertions, 56 deletions
diff --git a/shell.c b/shell.c
index 36eb916..be09e44 100644
--- a/shell.c
+++ b/shell.c
@@ -22,6 +22,7 @@ MAKE_QUEUE_PUSH(c_queue, cmdline, char**)
MAKE_QUEUE_PUSH(p_queue, pid, int)
enum modes { word_separation, whole_word };
+volatile sig_atomic_t last_execution_status = 0;
static void change_terminal_settings(struct termios *cur_terminal_settings,
struct termios *save_terminal_settings)
@@ -49,11 +50,17 @@ static void show_invitation()
static void handler(int signal)
{
int save_errno = errno;
- int pid;
+ int pid, result;
if(signal == SIGCHLD) {
do {
- pid = wait4(-1, NULL, WNOHANG, NULL);
+ pid = wait4(-1, &result, WNOHANG, NULL);
+ if(pid > 0) {
+ if(WIFEXITED(result))
+ last_execution_status = WEXITSTATUS(result) == 0 ? 0 : 1;
+ else
+ last_execution_status = 1;
+ }
} while(pid > 0);
}
errno = save_errno;
@@ -71,12 +78,17 @@ static void init_params(struct param_type *params, enum modes *current_mode)
params->streams.input_stream = NULL;
params->streams.output_stream = NULL;
params->streams.output_stream_to_append = NULL;
- /* 0 - success, 1 - error */
- params->last_execution_status = 0;
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_subshell = -1;
*current_mode = word_separation;
+ last_execution_status = 0;
}
static void clear_filename(struct param_type *params)
@@ -105,6 +117,10 @@ 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_subshell != -1)
+ close(params->fd_read_for_subshell);
}
static void prepare_for_next_command_on_line(struct param_type *params,
@@ -149,6 +165,8 @@ static const char* error_code_to_token(int error_code)
case err_empty_command: return "empty command";
case err_extra_chars_after_filename: return "extra chars after filename";
case err_odd_double_quotes: return "odd number of double quotes";
+ case err_missing_closing_bracket: return "missing closing bracket";
+ case err_repeated_background_ch: return "repeated &";
}
return NULL;
}
@@ -160,24 +178,28 @@ void error_identification(const struct param_type *params)
static int special_token_handling(struct w_queue *word_chain,
struct c_queue *cmdlines,
- struct dynamic_array *tmp_word, int ch,
+ 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) ?
+ stream_redirect_tokens(word_chain, tmp_word, *ch, params, readline) ?
1 : special_tokens(word_chain, cmdlines, tmp_word, ch, params,
readline);
}
static void wait_for_process_to_complete(struct p_queue *pid_store)
{
- int total_process_counter, current_process_counter, wait_pid;
+ int total_process_counter, current_process_counter, wait_pid, result;
total_process_counter = p_queue_get_process_quantity(pid_store);
current_process_counter = 0;
do {
- wait_pid = wait(NULL);
+ wait_pid = wait(&result);
+ if(WIFEXITED(result))
+ last_execution_status = WEXITSTATUS(result) == 0 ? 0 : 1;
+ else
+ last_execution_status = 1;
if(p_queue_find_pid(pid_store, wait_pid))
++current_process_counter;
} while(total_process_counter != current_process_counter);
@@ -202,23 +224,34 @@ static void close_files(int input_fd, int output_fd)
close(output_fd);
}
+static int get_true_terminal_fd()
+{
+ if(isatty(0))
+ return 0;
+ else if(isatty(1))
+ return 1;
+ else return 2;
+}
+
static void postprocessing(struct w_queue *word_chain, struct c_queue *cmdlines,
struct p_queue *pid_store, struct param_type *params,
int input_fd, int output_fd)
{
+ int terminal_fd;
clean_up_memory(word_chain, cmdlines, params);
close_files(input_fd, output_fd);
- if(params->tokens != '&') {
+ if(!params->background_process) {
wait_for_process_to_complete(pid_store);
signal(SIGTTOU, SIG_IGN);
- tcsetpgrp(0, getpid());
+ terminal_fd = get_true_terminal_fd();
+ tcsetpgrp(terminal_fd, getpid());
}
p_queue_clear(pid_store);
}
static void set_signal_disposition(struct param_type *params)
{
- if(params->tokens == '&')
+ if(params->background_process)
/* zombie process termination on signal */
signal(SIGCHLD, handler);
else
@@ -249,58 +282,146 @@ 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)
+{
+ 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->tokens != '&')
+ if(!params->background_process) {
/* foreground group */
- tcsetpgrp(0, params->general_pgid);
+ terminal_fd = get_true_terminal_fd();
+ signal(SIGTTOU, SIG_IGN);
+ tcsetpgrp(terminal_fd, params->general_pgid);
+ }
+}
+
+static int find_subshell_after_pipe(struct readline_type *readline)
+{
+ int i;
+ for(i = readline->considered_index; i <= readline->last_element_index; ++i) {
+ if(readline->arr[i] == ' ')
+ continue;
+ if(readline->arr[i] == '(')
+ return 1;
+ break;
+ }
+ return 0;
+}
+
+static int is_background_command(struct readline_type *readline)
+{
+ int i;
+ for(i = readline->considered_index; i <= readline->last_element_index; ++i){
+ if(readline->arr[i] == '&' && readline->arr[i+1] != '&')
+ return 1;
+ else if(readline->arr[i] == '&' && readline->arr[i+1] == '&')
+ ++i;
+ }
+ return 0;
}
static void make_pipeline(struct w_queue *word_chain, struct c_queue *cmdlines,
struct param_type *params,
+ struct readline_type *readline,
int input_fd, int output_fd)
{
struct p_queue pid_store;
p_queue_init(&pid_store);
char **cmdline = NULL;
int fd[2];
- int save_read_fd, pid;
+ int subshell_fd[2];
+ int save_read_fd, pid, subshell_after;
- pipe(fd);
+ subshell_after = find_subshell_after_pipe(readline);
cmdline = c_queue_pop(cmdlines);
+
+ 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)
+ pipe(subshell_fd);
+
pid = fork();
+#if 0
/* parent process */
if(pid)
identify_general_pgid(params, pid);
+#endif
+ if(pid) {
+ /* else set foreground group by first pipe process */
+ if(params->general_pipe_pgid == -1)
+ identify_general_pipe_pgid(params, pid);
+ }
if(pid == 0) {
- close(fd[0]);
- dup2(fd[1], 1);
- close(fd[1]);
- if(is_stream_redirection_set(params))
+ if(params->fd_read_for_channel != -1) {
+ dup2(params->fd_read_for_channel, 0);
+ close(params->fd_read_for_channel);
+ }
+ if(!c_queue_is_empty(cmdlines)) {
+ close(fd[0]);
+ dup2(fd[1], 1);
+ close(fd[1]);
+ } else if(subshell_after) {
+ close(subshell_fd[0]);
+ dup2(subshell_fd[1], 1);
+ close(subshell_fd[1]);
+ }
+ 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))
+ change_streams(input_fd, output_fd, 1, 0);
+ }
/* change pgid */
- setpgid(getpid(), params->general_pgid == -1 ?
- getpid() : params->general_pgid);
+ setpgid(getpid(), params->general_pipe_pgid == -1 ? getpid() :
+ params->general_pipe_pgid);
execvp(cmdline[0], cmdline);
perror(cmdline[0]);
exit(1);
}
-
- close(fd[1]);
p_queue_push(&pid_store, pid);
+ if(params->fd_read_for_channel != -1)
+ close(params->fd_read_for_channel);
+
+ if(subshell_after && c_queue_is_empty(cmdlines)) {
+ params->fd_read_for_subshell = subshell_fd[0];
+ close(subshell_fd[1]);
+ }
+
+ if(!c_queue_is_empty(cmdlines))
+ close(fd[1]);
while(!c_queue_is_empty(cmdlines)) {
cmdline = c_queue_pop(cmdlines);
/* if not last process in pipeline*/
- if(!c_queue_is_empty(cmdlines)) {
+ if(!c_queue_is_empty(cmdlines)){
save_read_fd = fd[0];
pipe(fd);
- }
+ } else if(subshell_after)
+ pipe(subshell_fd);
+
pid = fork();
if(pid == 0) {
if(!c_queue_is_empty(cmdlines)) {
@@ -314,13 +435,17 @@ static void make_pipeline(struct w_queue *word_chain, struct c_queue *cmdlines,
close(fd[0]);
}
/* for last process in pipeline */
- if(c_queue_is_empty(cmdlines))
+ if(c_queue_is_empty(cmdlines)) {
if(is_stream_redirection_set(params))
change_streams(input_fd, output_fd, 1, 0);
-
+ if(subshell_after) {
+ close(subshell_fd[0]);
+ dup2(subshell_fd[1], 1);
+ close(subshell_fd[1]);
+ }
+ }
/* change pgid */
- setpgid(getpid(), params->general_pgid);
-
+ setpgid(getpid(), params->general_pipe_pgid);
execvp(cmdline[0], cmdline);
perror(cmdline[0]);
exit(1);
@@ -328,12 +453,23 @@ static void make_pipeline(struct w_queue *word_chain, struct c_queue *cmdlines,
if(!c_queue_is_empty(cmdlines)) {
close(save_read_fd);
close(fd[1]);
- } else
+ } else {
close(fd[0]);
+ if(subshell_after)
+ close(subshell_fd[1]);
+ }
p_queue_push(&pid_store, pid);
}
+ 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
}
static void open_files(const struct param_type *params, int *input_fd,
@@ -380,7 +516,8 @@ static void change_directory(char **cmdline)
static void run_external_program(struct w_queue *word_chain,
struct c_queue *cmdlines,
- struct param_type *params)
+ struct param_type *params,
+ struct readline_type *readline)
{
int pid, input_fd, output_fd;
struct p_queue pid_store;
@@ -389,6 +526,11 @@ static void run_external_program(struct w_queue *word_chain,
input_fd = 0;
output_fd = 0;
+ if(is_background_command(readline))
+ params->background_process = 1;
+ else
+ params->background_process = 0;
+
if(is_stream_redirection_set(params))
open_files(params, &input_fd, &output_fd);
@@ -397,7 +539,8 @@ static void run_external_program(struct w_queue *word_chain,
return;
}
if(params->pipeline) {
- make_pipeline(word_chain, cmdlines, params, input_fd, output_fd);
+ make_pipeline(word_chain, cmdlines, params, readline,
+ input_fd, output_fd);
return;
}
cmdline = c_queue_pop(cmdlines);
@@ -410,19 +553,29 @@ 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, 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);
- /* change pgid */
- setpgid(getpid(), params->general_pgid == -1 ?
- getpid() : params->general_pgid);
+ if(params->fd_read_for_channel != -1) {
+ dup2(params->fd_read_for_channel, 0);
+ close(params->fd_read_for_channel);
+ }
+ /* set foreground process */
+ if(params->subshell && params->general_pgid != -1)
+ setpgid(getpid(), params->general_pgid);
+ else {
+ identify_general_pgid(params, getpid());
+ setpgid(getpid(), getpid());
+ }
execvp(cmdline[0], cmdline);
- params->last_execution_status = 1;
perror(cmdline[0]);
exit(1);
}
@@ -430,6 +583,11 @@ 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
}
}
@@ -500,10 +658,14 @@ static void generate_readline(struct readline_type *readline)
{
struct termios cur_terminal_settings, save_terminal_settings;
enum keys found_key = none;
- int match_result;
+ int match_result, terminal_fd;
char ch;
change_terminal_settings(&cur_terminal_settings, &save_terminal_settings);
+ /* return foreground group by main process */
+ terminal_fd = get_true_terminal_fd();
+ signal(SIGTTOU, SIG_IGN);
+ tcsetpgrp(terminal_fd, getpid());
while((ch = getchar()) && !readline_termination_condition(ch, readline)) {
if(ch == tab) {
@@ -589,27 +751,41 @@ static void command_processing(struct param_type *params,
} else if(params->empty_word_flag || params->is_word)
add_word(word_chain, tmp_word, params);
- if((params->tokens == and || params->tokens == or) &&
- params->last_execution_status == 1) {
- /* ??? */
- fprintf(stderr, "cannot be performed\n");
- return;
+ if(word_chain->first == NULL && cmdlines->first == NULL) {
+ /* if not ending subshell */
+ if(params->tokens != '(')
+ params->wrong_command = err_empty_command;
+ goto clean;
}
-
- if(word_chain->first == NULL) {
- params->wrong_command = err_empty_command;
+#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) ||
+ (params->tokens == or && last_execution_status == 0)))
+ goto clean;
- cmdline = create_cmdline(word_chain, w_queue_get_word_count(word_chain));
- c_queue_push(cmdlines, cmdline);
- run_external_program(word_chain, cmdlines, params);
+ if(word_chain->first != NULL) {
+ cmdline = create_cmdline(word_chain, w_queue_get_word_count(word_chain));
+ c_queue_push(cmdlines, cmdline);
+ }
+ run_external_program(word_chain, cmdlines, params, readline);
clean:
error_identification(params);
+ if(params->tokens == end_subshell && params->subshell)
+ exit(last_execution_status);
last_token = params->tokens;
- if(last_token == '&' || last_token == 0 || ch == new_line) {
+ if(last_token == 0 || ch == new_line) {
reset_params(params, current_mode, word_chain, cmdlines, tmp_word,
readline);
show_invitation();
@@ -619,6 +795,99 @@ clean:
tmp_word);
}
+/* searching for the paired closed bracket too */
+static int find_pipeline_after_subshell(struct readline_type *readline,
+ struct param_type *params)
+{
+ int i;
+ for(i = readline->considered_index; i <= readline->last_element_index &&
+ readline->arr[i] != ')'; ++i)
+ {}
+
+ if(i == readline->last_element_index+1) {
+ params->wrong_command = err_missing_closing_bracket;
+ return 0;
+ }
+ ++i;
+ while(i <= readline->last_element_index) {
+ if(readline->arr[i] == ' ') {
+ ++i;
+ continue;
+ }
+ break;
+ }
+ if(readline->arr[i] == '|')
+ return 1;
+ else
+ return 0;
+}
+
+static void readline_idx_after_subshell(struct readline_type *readline,
+ struct param_type *params)
+{
+ int i;
+ for(i = readline->considered_index; i <= readline->last_element_index &&
+ readline->arr[i] != ')'; ++i)
+ {}
+
+ if(i == readline->last_element_index + 1) {
+ params->wrong_command = err_missing_closing_bracket;
+ return;
+ }
+
+ ++i;
+ /* skip whitespace symbols */
+ for( ; i <= readline->last_element_index && readline->arr[i] == ' '; ++i)
+ {}
+ readline->considered_index = i;
+}
+
+static void make_subshell(struct readline_type *readline,
+ struct param_type *params)
+{
+ int fd[2];
+ int pid, pipeline_after, pipeline_before;
+
+ pipeline_after = 0;
+
+ pipeline_after = find_pipeline_after_subshell(readline, params);
+ pipeline_before = params->fd_read_for_subshell != -1 ? 1 : 0;
+
+ if(pipeline_after) {
+ pipe(fd);
+ params->fd_read_for_channel = fd[0];
+ }
+
+ pid = fork();
+ /* child */
+ 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);
+ close(fd[0]);
+ dup2(fd[1], 1);
+ close(fd[1]);
+ }
+ if(pipeline_before) {
+ dup2(params->fd_read_for_subshell, 0);
+ close(params->fd_read_for_subshell);
+ params->general_pgid = params->general_pipe_pgid;
+ setpgid(getpid(), params->general_pipe_pgid);
+ }
+ /* parent */
+ } else {
+ sleep(1);
+ if(pipeline_after) {
+ close(fd[1]);
+ params->general_pipe_pgid = pid;
+ }
+ readline_idx_after_subshell(readline, params);
+ }
+}
+
static void word_separation_processing(int ch, struct param_type *params,
enum modes *current_mode,
struct w_queue *word_chain,
@@ -644,15 +913,22 @@ 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,
+ if(special_token_handling(word_chain, cmdlines, tmp_word, &ch, params,
readline)) {
- if(command_execution_condition(params))
+ if(command_execution_condition(params, readline))
command_processing(params, current_mode, word_chain, cmdlines,
tmp_word, readline, ch);
+ if(params->tokens == start_subshell)
+ make_subshell(readline, params);
return;
}
if(params->wrong_command) {
error_identification(params);
+
+ /* subshell termination failed; failure with (&&, ||) too */
+ if(params->subshell)
+ exit(1);
+
clean_input_buffer();
reset_params(params, current_mode, word_chain, cmdlines, tmp_word,
readline);
@@ -660,9 +936,12 @@ static void word_separation_processing(int ch, struct param_type *params,
return;
}
if(check_separation(ch, *params)) {
- if(excessive_words(ch, params)) {
+ if(excessive_words(ch, params, readline)) {
error_identification(params);
clean_input_buffer();
+ /* subshell termination failed */
+ if(params->subshell)
+ exit(1);
reset_params(params, current_mode, word_chain, cmdlines, tmp_word,
readline);
show_invitation();
@@ -731,6 +1010,7 @@ int main()
dynarr_create_array(&tmp_word);
readline_create_array(&readline);
init_params(&params, &current_mode);
+ signal(SIGCHLD, handler);
show_invitation();
generate_readline(&readline);
@@ -747,9 +1027,18 @@ int main()
else if(current_mode == word_separation) {
word_separation_processing(ch, &params, &current_mode, &word_chain,
&cmdlines, &tmp_word, &readline);
- /* double token was found */
- if(readline.considered_index != i)
- ++i;
+ if(params.tokens == start_subshell && !params.subshell) {
+ i = readline.considered_index;
+ continue;
+ }
+ /*
+ * either a double token was found
+ * or the subshell part was skipped
+ */
+ if(readline.considered_index != i) {
+ i = readline.considered_index;
+ continue;
+ }
}
else if(current_mode == whole_word)
whole_word_processing(ch, &params, &current_mode, &word_chain,