#include #include #include #include #include #include #include #include "syntax.h" #include "utils.h" void execute_command_pipeline(Pipeline pipeline, int stdin_fd, int stdout_fd, bool wait_here, int* pgid, ShellState* shstate); DECLARE_MAYBE(StrPair, MaybeStrPair) MaybeStrPair parse_assignment(char* token) { printf("%s\n", token); // TODO: ideally shouldn't mutate maybe if (token == NULL) return (MaybeStrPair){.some = false}; char* eq = strchr(token, '='); if (eq == NULL || eq == token) return (MaybeStrPair){.some = false}; *eq = '\0'; if (!is_identifier(token)) exit(42); return (MaybeStrPair){.some = true, .value = (StrPair){.key = token, .val = eq+1}}; } bool handle_builtin(ExecutableCommand cmd, ShellState* shstate) { assert(cmd.args[0] != NULL); // TODO: handle id=value assignments if (strcmp(cmd.args[0], "exit") == 0) { exit(0); } else if (strcmp(cmd.args[0], "cd") == 0) { assert(cmd.args[1] != NULL); chdir(cmd.args[1]); return true; } else if (strcmp(cmd.args[0], "alias") == 0) { // TODO: if no arg1 -> print alias list MaybeStrPair m_assignment = parse_assignment(cmd.args[1]); assert(m_assignment.some); printf("%s:%s\n", m_assignment.value.key, m_assignment.value.val); set_map_pair(&shstate->aliases, m_assignment.value); return true; } return false; } void execute_cmd(ExecutableCommand cmd, int* pgid) { pid_t pid = fork(); if (pid == 0) { if (*pgid == -1) { setpgid(0, 0); } else { setpgid(0, *pgid); } dup2(cmd.stdin_fd, STDIN_FILENO); dup2(cmd.stdout_fd, STDOUT_FILENO); for (int i = 0; i < cmd.redirects.count; i++) { FdRedirect fd_rd = *get_from_FdRedirectVec(&cmd.redirects, i); dup2(fd_rd.to, fd_rd.redirected); } for (int j = 0; j < cmd.child_open_fds.count; j++) { int fd = *get_from_IntVec(&cmd.child_open_fds, j); close(fd); } signal(SIGINT, SIG_DFL); signal(SIGQUIT, SIG_DFL); signal(SIGTSTP, SIG_DFL); signal(SIGTTIN, SIG_DFL); signal(SIGTTOU, SIG_DFL); execvp(cmd.args[0], cmd.args); exit(67); } if (*pgid == -1) { setpgid(pid, pid); *pgid = pid; } else { setpgid(pid, *pgid); } } ExecutableCommand process_pl_command(PlCommand pl_cmd, int* pgid, ShellState* shstate) { StrVec arg_vec = make_StrVec(256); FdRedirectVec fd_redirs = make_FdRedirectVec(256); IntVec parent_open_fds = make_IntVec(256); IntVec child_open_fds = make_IntVec(256); int ps_pipe_fds[2]; for (int i = 0; i < pl_cmd.args.count; i++) { Arg arg = *get_from_ArgVec(&pl_cmd.args, i); switch (arg.kind) { case ARG_LIT: push_to_StrVec(&arg_vec, arg.as.lit_str); break; case ARG_PS_IN: pipe(ps_pipe_fds); execute_command_pipeline(*(Pipeline*)arg.as.sub_pipeline, STDIN_FILENO, ps_pipe_fds[1], false, pgid, shstate); close(ps_pipe_fds[1]); char pname[64]; snprintf(pname, sizeof(pname), "/dev/fd/%d", ps_pipe_fds[0]); push_to_IntVec(&parent_open_fds, ps_pipe_fds[0]); push_to_StrVec(&arg_vec, strdup(pname)); break; case ARG_PS_OUT: // TODO: do waiting correctly pipe(ps_pipe_fds); // TODO: don't do fcntl? fcntl(ps_pipe_fds[1], F_SETFD, FD_CLOEXEC); execute_command_pipeline(*(Pipeline*)arg.as.sub_pipeline, ps_pipe_fds[0], STDOUT_FILENO, false, pgid, shstate); fcntl(ps_pipe_fds[1], F_SETFD, 0); close(ps_pipe_fds[0]); char name[64]; snprintf(name, sizeof(name), "/dev/fd/%d", ps_pipe_fds[1]); push_to_IntVec(&parent_open_fds, ps_pipe_fds[1]); push_to_StrVec(&arg_vec, strdup(name)); break; default: exit(67); } } for (int j = 0; j < pl_cmd.redirects.count; j++) { Redirect redir = *get_from_RedirectVec(&pl_cmd.redirects, j); FdRedirect fd_redir; int fd; switch (redir.kind) { case REDIR_READ: fd = open(redir.as.filename, O_RDONLY, 0644); push_to_IntVec(&parent_open_fds, fd); push_to_IntVec(&child_open_fds, fd); fd_redir = (FdRedirect){.to = fd, .redirected = STDIN_FILENO}; break; case REDIR_WRITE_APPEND: fd = open(redir.as.filename, O_WRONLY | O_CREAT | O_APPEND, 0644); push_to_IntVec(&parent_open_fds, fd); push_to_IntVec(&child_open_fds, fd); fd_redir = (FdRedirect){.to = fd, .redirected = STDOUT_FILENO}; break; case REDIR_WRITE_TRUNC: fd = open(redir.as.filename, O_WRONLY | O_CREAT | O_TRUNC, 0644); push_to_IntVec(&parent_open_fds, fd); push_to_IntVec(&child_open_fds, fd); fd_redir = (FdRedirect){.to = fd, .redirected = STDOUT_FILENO}; break; default: exit(67); } push_to_FdRedirectVec(&fd_redirs, fd_redir); } char** args = malloc(sizeof(char*) * (arg_vec.count + 1)); for (int i = 0; i < arg_vec.count; i++) { if (i == 0) { char* arg = *get_from_StrVec(&arg_vec, 0); MaybeStr m_dealias = get_map_value(&shstate->aliases, arg); if (m_dealias.some) { args[0] = m_dealias.value; printf("%s\n", args[0]); } else { printf("Nah\n"); args[0] = arg; } } else { args[i] = *get_from_StrVec(&arg_vec, i); } } args[arg_vec.count] = NULL; return (ExecutableCommand){.args = args, .redirects = fd_redirs, .parent_open_fds = parent_open_fds, .child_open_fds = child_open_fds}; } void execute_command_pipeline(Pipeline pipeline, int stdin_fd, int stdout_fd, bool wait_here, int* pgid, ShellState* shstate) { int trunk_fd = stdin_fd; for (int i = 0; i < pipeline.count; i++) { PlCommand cmd = *get_from_Pipeline(&pipeline, i); ExecutableCommand exec_cmd = process_pl_command(cmd, pgid, shstate); exec_cmd.stdin_fd = trunk_fd; if (i > 0) { push_to_IntVec(&exec_cmd.parent_open_fds, trunk_fd); push_to_IntVec(&exec_cmd.child_open_fds, trunk_fd); } exec_cmd.stdout_fd = stdout_fd; if (cmd.conn == CONN_PIPE) { int pipe_fds[2]; pipe(pipe_fds); exec_cmd.stdout_fd = pipe_fds[1]; push_to_IntVec(&exec_cmd.parent_open_fds, pipe_fds[1]); push_to_IntVec(&exec_cmd.child_open_fds, pipe_fds[1]); trunk_fd = pipe_fds[0]; } if (!handle_builtin(exec_cmd, shstate)) { execute_cmd(exec_cmd, pgid); } for (int j = 0; j < exec_cmd.parent_open_fds.count; j++) { int fd = *get_from_IntVec(&exec_cmd.parent_open_fds, j); close(fd); } } if (wait_here) { tcsetpgrp(STDIN_FILENO, *pgid); while (waitpid(-*pgid, NULL, 0) > 0); tcsetpgrp(STDIN_FILENO, getpgrp()); } } void execute_pipeline_tree(PipelineTree pltree, ShellState* shstate) { int pgid = -1; signal(SIGTTIN, SIG_IGN); signal(SIGTTOU, SIG_IGN); execute_command_pipeline(*pltree.root, STDIN_FILENO, STDOUT_FILENO, true, &pgid, shstate); }