summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLAN-TW <lantw44@gmail.com>2013-10-30 00:30:49 +0800
committerLAN-TW <lantw44@gmail.com>2013-10-30 00:30:49 +0800
commitd054ffba1cdbbadcf62f45d4d80e46ee9d8ed772 (patch)
tree7fc212802668c43979cc078f6eb2d950116e089c
parent43c454e570ffb5a6915129175e1d300a8f204476 (diff)
downloadcn2013-d054ffba1cdbbadcf62f45d4d80e46ee9d8ed772.tar.gz
cn2013-d054ffba1cdbbadcf62f45d4d80e46ee9d8ed772.tar.zst
cn2013-d054ffba1cdbbadcf62f45d4d80e46ee9d8ed772.zip
HW1: RasShell 已實作基本的指令輸入功能
-rw-r--r--hw1/shell.c233
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;
}