/*---------------------------------------------------------------------*/ /* Matthew Reeves */ /* */ /* This program creates a simple shell using C system calls */ /* */ /* *** NOTE - Pipe implementation for this program is incomplete */ /* *** beyond identification of pipe symbol being entered by user */ /* *** and validation that first and second commands non-null/non-empty*/ /*---------------------------------------------------------------------*/ #include #include #include #include #include #include #include extern char **environ; int main() { char c; /*stores character user inputs*/ char str[200] = ""; /*string to store all characters input each cmd*/ char *tok; /*used to capture return from strtok*/ char *args[20]; /*stores command and arguements tokenized by strtok*/ int num_args; char *cmd1; char *cmd2; char *path_original; /*stores return from getenv("PATH")*/ char *dir; /*stores path returned from using strtok on path*/ char path[_POSIX_PATH_MAX+1]; /*used to preserve path_original value*/ char fullpath[500]; /*stores full path for command; is passed execve*/ int pipe_check; /*used to check if pipe entered by user*/ int pipe_status; int pfd[2]; int eof_check; /*stores read return to check for eof*/ int ok; /*used to check if "path"/command is executable*/ int count = 0; /*counting variable*/ int nonwhitespace = 0; /*used to check if only spaces and tabs in input*/ pid_t cpid; /* child's PID */ int status; int wait_check; /*return value from wait call*/ char msg[200]; /*stores exit status message*/ /*finds value for PATH environment variable, stores in path_original*/ path_original = getenv("PATH"); while (c != EOF) { /*writes '# ' to screen to precede each prompt*/ write(1,"#",1); write(1," ",1); /*resets count and nonwhitespace to zero*/ count = 0; nonwhitespace = 0; pipe_check == 0; /*read input from user char by char, until '\n' read.*/ while ((eof_check = read(0,&c,1)) == 1 && c != '\n') { /*tracks if non-space, non-tab character entered*/ if (c != ' ' && c != '\t') nonwhitespace++; /*tracks if pipe entered by user*/ if (c == '|') pipe_check++; /*checks for tab character*/ if (c == '\t') c = ' '; /*add char c value to string str*/ str[count] = c; str[count+1] = '\0'; /*append null (\0) to end of str*/ count++; } /*if eof (i.e. ctrl-d) entered, exit program*/ if (eof_check == 0) exit(0); /*if only whitespace entered, skip rest, return to input prompt*/ if (nonwhitespace == 0) continue; /*echo user command to screen*/ write(1, str, count); write(1,"\n",1); /*checks if pipe entered by user*/ if (pipe_check != 0) { /*separate first and second commands*/ cmd1 = strtok(str, "|"); cmd2 = strtok(NULL, "|"); /*check if first command null*/ if (cmd1 == NULL || str[0] == '|') { sprintf(msg, "No words in first command.\n"); write(1,msg,strlen(msg)); continue; } /*check if second command null*/ if (cmd2 == NULL) { sprintf(msg, "No words in second command.\n"); write(1,msg,strlen(msg)); continue; } /*reset tracking values*/ count = 0; nonwhitespace = 0; while (count < strlen(cmd1)) { /*checks if all chars in cmd1 are ' ' or '\t'*/ if (cmd1[count] != ' ' && cmd1[count] != '\t') { nonwhitespace++; } count++; } /*if cmd1 all whitespace, skip to next iteration*/ if (nonwhitespace == 0) { sprintf(msg, "No words in first command.\n"); write(1,msg,strlen(msg)); continue; } /*reset tracking values*/ count = 0; nonwhitespace = 0; while (count < strlen(cmd2)) { /*checks if all chars in cmd2 are ' ' or '\t'*/ if (cmd2[count] != ' ' && cmd2[count] != '\t') { nonwhitespace++; } count++; } /*if cmd2 all whitespace, skip to next iteration*/ if (nonwhitespace == 0) { sprintf(msg, "No words in second command.\n"); write(1,msg,strlen(msg)); continue; } /*---------------------------------------------------------------------*/ /* Future code for implementing pipes will go here if/when completed */ /*---------------------------------------------------------------------*/ /* */ /* pipe_status = pipe(pfd); */ /* */ /* if (pipe_status == -1) */ /* { */ /* perror("pipe"); */ /* exit(4); */ /* } */ /* */ /* */ /* */ /* close(pfd[1]); */ /* */ /*---------------------------------------------------------------------*/ } else { /*tokenize str, store in args*/ tok = strtok(str, " "); /*stores presumed command in args[0]*/ args[0] = tok; count = 1; /*continues to split str on " " until trailing null found*/ while (tok != NULL) { tok = strtok(NULL, " "); args[count] = tok; count++; } count = 0; /*reset count*/ /*checks if command entered has a slash and if executable*/ if (strchr(args[0],'/') != NULL) { ok = access(args[0],X_OK) == 0; strcpy(fullpath, args[0]); } else /*no slash, search paths for proper directory*/ { strcpy(path, path_original); /*preserve original path value*/ ok = 0; /*set ok to 0, initially assumes not executable*/ dir = strtok(path,":"); /*tokenize getenv("PATH") return*/ /*build path + command for every possible path until all paths tried (strtok returns null) or executable identified at path*/ while (dir != NULL) { strcpy(fullpath,dir); strcat(fullpath,"/"); strcat(fullpath,args[0]); ok = access(fullpath,X_OK) == 0; if (ok) break; dir =strtok(NULL,":"); } } /*if cmd not executable, advise user, return to input prompt*/ if (ok == 0) { sprintf(msg,"Command \'%s\' is not executable\n", args[0]); write(1,msg,strlen(msg)); continue; } /* Create a child process.*/ if ((cpid = fork()) == 0) /*if fork returns 0 we're in the child*/ { /*attempt to execute command*/ execve(fullpath, args, environ); perror("execve"); exit(1); } if (cpid == -1) /*If fork returns -1, it encountered an error.*/ { perror("fork"); exit(2); } /* Otherwise fork was successful. Wait for the child to exit.*/ wait_check = wait(&status); if (wait_check == -1) { perror("wait"); exit(3); } if (status & 0xff) /* abnormal termination */ { sprintf(msg,"Process %d terminated abnormally for reason %d\n", wait_check, status & 0xff); } else /* normal termination */ { sprintf(msg,"Process %d terminated normally with status %d\n", wait_check, (status >> 8) & 0xff); } write(1,msg,strlen(msg)); } } exit(0); }