/* * arch-tag: main */ #include #include #include #include "shell.h" struct sh_command_t { char* name; int argc; char* argv[SH_MAXARGS]; short flags; char* stdin_filename; char* stdout_filename; }; void sh_print_prompt(void) { printf(">> "); } void sh_readline(char* buf) { fgets(buf, SH_MAXLINELEN, stdin); } int sh_parseline(char* line, struct sh_command_t* command) { char** p; int i, rdr_in_pos, rdr_out_pos; command->flags = 0; for(p = command->argv, command->argc = 0; (*p = (char*) strsep(&line, " \n\t")) != NULL; ) if (**p != '\0') { // Skip repeated spaces // Don't process more than SH_MAXARGS args if (p + 1 >= &(command->argv[SH_MAXARGS])) break; command->argc++; p++; } for (i = 0, rdr_in_pos = 0, rdr_out_pos = 0; i < command->argc; i++) { if (*(command->argv[i]) == '&') { //printf("%d %s\n", i, *(command->argv + i)); if (!rdr_in_pos && !rdr_out_pos) p = command->argv + i; command->flags |= SH_CMD_BACKGROUND; } else if (*(command->argv[i]) == '>') { //printf("%d %s\n", i, *(command->argv + i)); if (!rdr_in_pos) p = command->argv + i; rdr_out_pos = i; command->flags |= SH_CMD_REDIRECT_OUT; command->stdout_filename = *(command->argv + i + 1); } else if (*(command->argv[i]) == '<') { //printf("%d %s\n", i, *(command->argv + i)); if (!rdr_out_pos) p = command->argv + i; rdr_in_pos = i; command->flags |= SH_CMD_REDIRECT_IN; command->stdin_filename = *(command->argv + i + 1); } } *p = NULL; command->argc = p - command->argv; return command->argc; } int sh_getpathv(char** pathv) { int pathc; char** p, *path; // TODO: make a copy? path = (char*) getenv("PATH"); for(p = pathv, pathc = 0; (*p = (char*) strsep(&path, ":")) != NULL; ) if (**p != '\0') { if (p + 1 >= &pathv[SH_MAXPATHS]) break; pathc++; p++; } return pathc; } int sh_check_exec(const char* fullpath) { // XXX possible race condition return access(fullpath, X_OK); } char* sh_lookup_path(char* name, int pathc, char** pathv) { int i, fp_size, fd, path_len, last_sl_pos; char* fullpath, *curr_dir, *ex_dir; // TODO: check for absolute and relative paths if (*name == '/') { if (sh_check_exec(name) == 0) return name; else return NULL; } // TODO: use realpath(3) instead. if (*name == '.') { // Hack to do relative pathnames if ((curr_dir = getcwd(NULL, 0)) == NULL) { perror("getcwd()"); exit(1); } if ((ex_dir = (char*) malloc(strlen(curr_dir) + 1)) == NULL) { perror("malloc()"); exit(1); } strncpy(ex_dir, name, strlen(name) + 1); // locate the last '/' for (i = 0, path_len = strlen(ex_dir); i < path_len; i++) { // printf("%c\n", ex_dir[i]); if (ex_dir[i] == '/') { last_sl_pos = i; // printf("found '/' %d\n", i); } } ex_dir[last_sl_pos] = 0; name = name + last_sl_pos + 1; if ((fd = open(ex_dir, O_RDONLY)) < 0) { perror("open()"); exit(1); } //printf("%d %s %s\n", last_sl_pos, ex_dir, name); if (fchdir(fd) < 0) { perror("fchdir()"); exit(1); } free(ex_dir); if ((ex_dir = getcwd(NULL, 0)) == NULL) { perror("getcwd()"); exit(1); } if (chdir(curr_dir) < 0) { perror("chdir()"); exit(1); } if (asprintf(&fullpath, "%s/%s", ex_dir, name) < 0) { perror("asprintf()"); exit(1); } free(ex_dir); free(curr_dir); // printf("%s\n", fullpath); if (sh_check_exec(fullpath) == 0) return fullpath; else { free(fullpath); return NULL; } } for (i = 0; i < pathc; i++) { fp_size = strlen(name) + strlen(pathv[i]) + 2; if ((fullpath = (char*) malloc(fp_size)) == NULL) { perror("malloc()"); exit(1); } snprintf(fullpath, fp_size, "%s/%s", pathv[i], name); //printf("%s\n", fullpath); if (sh_check_exec(fullpath) == 0) return fullpath; free(fullpath); } return NULL; } void sh_exec_child(struct sh_command_t* command) { int in_fd, out_fd; if (command->flags & SH_CMD_REDIRECT_IN) { // printf("redirecting stdin to %s\n", command->stdin_filename); close(0); if ((in_fd = open(command->stdin_filename, O_RDONLY)) < 0) { perror("open()"); exit(1); } } if (command->flags & SH_CMD_REDIRECT_OUT) { // printf("redirecting stdout to %s\n", command->stdout_filename); close(1); if ((out_fd = open(command->stdout_filename, O_WRONLY | O_CREAT)) < 0) { perror("open()"); exit(1); } if (fchmod(out_fd, 400) < 0) { perror("fchmod()"); exit(1); } } execv(command->name, command->argv); } int main(int argc, char** argv) { char* buf; struct sh_command_t command; char* pathv[SH_MAXPATHS]; int pathc; pid_t pid; pathc = sh_getpathv(pathv); while(1) { if ((buf = (char*) malloc(SH_MAXLINELEN)) == NULL) { perror("malloc()"); exit(1); } sh_print_prompt(); sh_readline(buf); if (sh_parseline(buf, &command) == 0) goto done; if ((command.name = sh_lookup_path(command.argv[0], pathc, pathv)) == NULL) { printf("Command not found: %s\n", command.argv[0]); goto done; } printf("%s\n", command.name); if ((pid = fork()) == 0) { sh_exec_child(&command); } if (!(command.flags & SH_CMD_BACKGROUND)) waitpid(pid, NULL, 0); else printf("pid: %d\n", pid); free(command.name); done: free(buf); } }