diff options
author | LAN-TW <lantw44@gmail.com> | 2013-10-30 00:30:49 +0800 |
---|---|---|
committer | LAN-TW <lantw44@gmail.com> | 2013-10-30 00:30:49 +0800 |
commit | d054ffba1cdbbadcf62f45d4d80e46ee9d8ed772 (patch) | |
tree | 7fc212802668c43979cc078f6eb2d950116e089c | |
parent | 43c454e570ffb5a6915129175e1d300a8f204476 (diff) | |
download | cn2013-d054ffba1cdbbadcf62f45d4d80e46ee9d8ed772.tar.gz cn2013-d054ffba1cdbbadcf62f45d4d80e46ee9d8ed772.tar.zst cn2013-d054ffba1cdbbadcf62f45d4d80e46ee9d8ed772.zip |
HW1: RasShell 已實作基本的指令輸入功能
-rw-r--r-- | hw1/shell.c | 233 |
1 files changed, 223 insertions, 10 deletions
diff --git a/hw1/shell.c b/hw1/shell.c index 0d6db2a..b4acf4c 100644 --- a/hw1/shell.c +++ b/hw1/shell.c @@ -8,6 +8,7 @@ #include <ctype.h> #include <errno.h> +#include <fcntl.h> #include <inttypes.h> #include <locale.h> #include <pwd.h> @@ -15,8 +16,11 @@ #include <stdbool.h> #include <stdint.h> #include <stdio.h> +#include <stdlib.h> #include <string.h> #include <sys/types.h> +#include <sys/wait.h> +#include <termios.h> #include <time.h> #include <unistd.h> @@ -28,14 +32,6 @@ #define STRV(x) ((char**)(x)) #define STR(x) ((char*)(x)) -typedef int (*RasShellFunc) (char* argv[]); -typedef char* (*RasShellReadline) (void* data); - -typedef struct { - char* cmd; - RasShellFunc func; -} RasShellBuiltin; - typedef struct { uint64_t count; char* reason; @@ -52,6 +48,7 @@ typedef struct { typedef struct { List* argrp; /* A List of RasShellCmdComp */ + pid_t pgid; /* process group ID */ } RasShellCmd; typedef struct { @@ -60,15 +57,28 @@ typedef struct { List* saved_list; /* A list of RasShellSaved */ char* saved_tmpdir; int last_rval; + pid_t pid; + pid_t pgid; int attr_login : 1; /* login shell */ int attr_rshell : 1; /* restricted shell */ int attr_debug : 1; /* debug message */ + int attr_isterm : 1; /* shell is run in a terminal */ char* user_name; char user_ps; int req_exit : 1; /* request for exit */ int req_exit_status; /* requested exit status */ + int term_fd; /* shell terminal fd */ + struct termios tmode; /* shell terminal mode */ } RasShell; +typedef int (*RasShellFunc) (int argc, char* argv[], RasShell* shell); +typedef char* (*RasShellRead) (void* data); + +typedef struct { + char* cmd; + RasShellFunc func; +} RasShellBuiltin; + #define PARSE_QUOTE_REGULAR '\"' #define PARSE_QUOTE_SUPER '\'' #define PARSE_ESCAPE '\\' @@ -135,7 +145,7 @@ static void complete_component (Array*** ws, int* append, } static int ras_shell_cmd_parse ( - RasShellCmd* cmd, const char* cmdstr, RasShellReadline read_cb, void* data) { + RasShellCmd* cmd, const char* cmdstr, RasShellRead read_cb, void* data) { bool is_quoted = false; bool is_escaped = false; @@ -144,6 +154,7 @@ static int ras_shell_cmd_parse ( int append = APPEND_STR; /* current working string */ cmd->argrp = list_create (); + cmd->pgid = 0; RasShellCmdComp comp; Array* str; @@ -250,8 +261,195 @@ static void ras_shell_cmd_print (RasShellCmd* cmd) { } } +static int ras_shell_builtin_echo (int argc, char* argv[], RasShell* shell) { + for (int i = 1; argv[i] != NULL; i++) { + if (i > 1) { + putchar (' '); + } + fputs (argv[i], stdout); + } + putchar ('\n'); + return 0; +} + +static int ras_shell_builtin_exit (int argc, char* argv[], RasShell* shell) { + if (argc != 2) { + fprintf ( + stderr, "%s: too %s arguments\n", argv[0], argc > 2 ? "many" : "few"); + return 1; + } + shell->req_exit = true; + shell->req_exit_status = atoi (argv[1]); + return 0; +} + +static int ras_shell_builtin_printenv (int argc, char* argv[], RasShell* shell) { + if (argc > 2) { + fprintf (stderr, "%s: too many arguments\n", argv[0]); + return 1; + } + if (argc < 2) { + extern char** environ; + for (int i = 0; environ[i] != NULL; i++) { + puts (environ[i]); + } + } else { + char* res = getenv (argv[1]); + if (res == NULL) { + return 1; + } + puts (res); + } + return 0; +} + +static int ras_shell_builtin_setenv (int argc, char* argv[], RasShell* shell) { + if (argc != 3) { + fprintf ( + stderr, "%s: too %s arguments\n", argv[0], argc > 3 ? "many" : "few"); + return 1; + } + return - setenv (argv[1], argv[2], 1); +} + static int ras_shell_cmd_run (RasShellCmd* cmd, RasShell* shell) { - return -1; + const static RasShellBuiltin builtins[] = { + { "echo", ras_shell_builtin_echo }, + { "exit", ras_shell_builtin_exit }, + { "printenv", ras_shell_builtin_printenv }, + { "setenv", ras_shell_builtin_setenv }, + { NULL, NULL } + }; + + ListNode* iter = list_node_front (cmd->argrp); + pid_t child; + int pipefd[2], infile, outfile; + + cmd->pgid = 0; + infile = STDIN_FILENO; + outfile = STDOUT_FILENO; + + for (int i = 1; iter != NULL; iter = list_next (iter), i++) { + RasShellCmdComp* comp = list_data (iter); + RasShellFunc builtin_func = NULL; + + for (int j = 0; builtins[j].cmd != NULL; j++) { + char* cmd = STRV (array_data (comp->arg))[0]; + if (cmd == NULL) { + continue; + } + if (strcmp (builtins[j].cmd, cmd) == 0) { + builtin_func = builtins[j].func; + break; + } + } + + if (list_len (cmd->argrp) <= 1 && builtin_func != NULL) { + return (*builtin_func)( + array_getlen (comp->arg) - 1, + STRV (array_data (comp->arg)), shell); + } + + if (list_next (iter) != NULL) { + if (pipe (pipefd) < 0) { + fprintf (stderr, "%s: pipe: %s\n", shell->name, strerror (errno)); + break; + } + outfile = pipefd[1]; + } else { + if (array_getlen (comp->redir_stdout)) { + outfile = open (array_data (comp->redir_stdout), O_WRONLY | O_CREAT | O_TRUNC); + if (outfile < 0) { + fprintf (stderr, "%s: %s: %s\n", + shell->name, STR (array_data (comp->redir_stdout)), strerror (errno)); + break; + } + } else { + outfile = STDOUT_FILENO; + } + if (array_getlen (comp->tapipe)) { + int tpnum; + sscanf (STR (array_data (comp->tapipe)) + 1, "%d", &tpnum); + if (tpnum < 1) { + fprintf (stderr, "%s: invalid TA pipe number `%s\'\n", + shell->name, STR (array_data (comp->tapipe))); + break; + } + // TODO: TA pipe handling + } + } + + child = fork (); + + if (child < 0) { + fprintf (stderr, "%s: fork: %s\n", shell->name, strerror (errno)); + continue; + } else if (child > 0) { + /* parent */ + + if (cmd->pgid == 0) { + cmd->pgid = child; + } + setpgid (child, cmd->pgid); + + } else { + /* child */ + + pid_t pid = getpid (); + if (cmd->pgid == 0) { + cmd->pgid = pid; + } + setpgid (pid, cmd->pgid); + if (shell->attr_isterm) { + tcsetpgrp (shell->attr_isterm, cmd->pgid); + } + + if (infile != STDIN_FILENO) { + dup2 (infile, STDIN_FILENO); + close (infile); + } + if (outfile != STDOUT_FILENO) { + dup2 (outfile, STDOUT_FILENO); + close (STDOUT_FILENO); + } + + if (builtin_func == NULL) { + char* execname = STRV (array_data (comp->arg))[0]; + char** execargv = STRV (array_data (comp->arg)); + execvp (execname, execargv); + fprintf (stderr, "%s: %s: %s\n", shell->name, execname, strerror (errno)); + exit (1); + } + } + + if (infile != STDIN_FILENO) { + close (infile); + } + if (outfile != STDOUT_FILENO) { + close (outfile); + } + } + +/* if (shell->attr_isterm) { + tcsetpgrp (shell->term_fd, cmd->pgid); + }*/ + + int status = 0; + waitpid (- cmd->pgid, &status, 0); + + if (shell->attr_isterm) { + tcsetpgrp (shell->term_fd, shell->pgid); + tcsetattr (shell->term_fd, TCSADRAIN, &shell->tmode); + } + + if (infile != STDIN_FILENO) { + close (infile); + } + if (outfile != STDOUT_FILENO) { + close (outfile); + } + + return WEXITSTATUS (status); } static void ras_shell_cmd_destroy (RasShellCmd* cmd) { @@ -283,10 +481,14 @@ static char* ras_shell_cmd_read (void* data) { } static int ras_shell_init (RasShell* shell, int argc, char* argv[]) { + shell->last_rval = 0; + shell->pid = getpid (); + shell->pgid = getpgid (shell->pid); shell->attr_login = argv[0][0] == '-'; shell->attr_rshell = false; shell->attr_debug = false; + shell->attr_isterm = isatty (STDIN_FILENO) == 1; shell->req_exit = false; shell->req_exit_status = 0; @@ -347,6 +549,17 @@ static int ras_shell_init (RasShell* shell, int argc, char* argv[]) { shell->user_name = pwname == NULL ? NULL : xstrdup (pwname->pw_name); shell->user_ps = geteuid () ? '$' : '#'; + if (shell->attr_isterm) { + shell->term_fd = STDIN_FILENO; + if (tcgetattr (shell->term_fd, &shell->tmode) < 0) { + shell->attr_isterm = false; + } + } + + if (shell->attr_rshell) { + setenv ("PATH", "bin:.", 1); + } + return 0; } |