Created
April 3, 2019 16:49
-
-
Save PrunedNeuron/0fc4f87f5af2041628d30a64dcffacb3 to your computer and use it in GitHub Desktop.
UNIX shell written in C.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <unistd.h> | |
| #include <limits.h> | |
| #include <math.h> | |
| #include <string.h> | |
| #include <sys/wait.h> | |
| #include <dirent.h> | |
| #include <sys/types.h> | |
| #include <sys/stat.h> | |
| #include <time.h> | |
| #include <fcntl.h> | |
| #include <grp.h> | |
| #include <pwd.h> | |
| #define rep(i, a, b) for(i = a; i < b; i++) | |
| #define rev(i, a, b) for(i = a; i > b; i--) | |
| #define TOK_BUFFER 64 | |
| #define COMM_BUFFER 64 | |
| #define TOK_DELIM " \t\n\a\r" | |
| #define COMM_DELIM ";" | |
| char* root; | |
| int shell_cd (char **args); | |
| int shell_exit (char **args); | |
| int shell_quit(char **args); | |
| int shell_pwd (char **args); | |
| int shell_echo (char **args); | |
| int shell_pinfo (char **args); | |
| int shell_ls (char **args); | |
| int shell_setenv (char **args); | |
| int shell_unsetenv (char **args); | |
| char *builtin_arr[] = {"cd","exit","pwd","echo","pinfo", "ls","setenv","unsetenv","quit"}; | |
| int (*builtin_functions[])(char**) = {&shell_cd,&shell_exit,&shell_pwd,&shell_echo,&shell_pinfo,&shell_ls,&shell_setenv,&shell_unsetenv,&shell_quit}; | |
| void sigintHandler(int sig_num) { | |
| signal(SIGINT,sigintHandler); | |
| fflush(stdout); | |
| } | |
| void pipehandler(char **args){ | |
| // File descriptors | |
| int filedes[2]; | |
| int filedes2[2]; | |
| int num_cmds = 0; | |
| char *command[256]; | |
| pid_t pid; | |
| int err = -1,end = 0, i = 0, j = 0,k = 0,l = 0; | |
| while (args[l] != NULL){ | |
| if (strcmp(args[l],"|") == 0){ | |
| num_cmds++; | |
| } | |
| l++; | |
| } | |
| num_cmds++; | |
| while (args[j] != NULL && end != 1){ | |
| k = 0; | |
| while (strcmp(args[j],"|") != 0){ | |
| command[k] = args[j]; | |
| j++; | |
| if (args[j] == NULL){ | |
| end = 1; | |
| k++; | |
| break; | |
| } | |
| k++; | |
| } | |
| command[k] = NULL; | |
| j++; | |
| if (i % 2 != 0){ | |
| pipe(filedes); | |
| }else{ | |
| pipe(filedes2); | |
| } | |
| pid=fork(); | |
| if(pid==-1){ | |
| if (i != num_cmds - 1){ | |
| if (i % 2 != 0){ | |
| close(filedes[1]); // for odd i | |
| }else{ | |
| close(filedes2[1]); // for even i | |
| } | |
| } | |
| printf("Child process could not be created\n"); | |
| return; | |
| } | |
| if(pid==0){ | |
| if (i == 0){ | |
| dup2(filedes2[1], STDOUT_FILENO); | |
| } | |
| else if (i == num_cmds - 1){ | |
| if (num_cmds % 2 != 0){ | |
| dup2(filedes[0],STDIN_FILENO); | |
| }else{ | |
| dup2(filedes2[0],STDIN_FILENO); | |
| } | |
| }else{ | |
| if (i % 2 != 0){ | |
| dup2(filedes2[0],STDIN_FILENO); | |
| dup2(filedes[1],STDOUT_FILENO); | |
| }else{ | |
| dup2(filedes[0],STDIN_FILENO); | |
| dup2(filedes2[1],STDOUT_FILENO); | |
| } | |
| } | |
| if (execvp(command[0],command)==err){ | |
| kill(getpid(),SIGTERM); | |
| } | |
| } | |
| if (i == 0){ | |
| close(filedes2[1]); | |
| } | |
| else if (i == num_cmds - 1){ | |
| if (num_cmds % 2 != 0){ | |
| close(filedes[0]); | |
| }else{ | |
| close(filedes2[0]); | |
| } | |
| }else{ | |
| if (i % 2 != 0){ | |
| close(filedes2[0]); | |
| close(filedes[1]); | |
| }else{ | |
| close(filedes[0]); | |
| close(filedes2[1]); | |
| } | |
| } | |
| waitpid(pid,NULL,0); | |
| i++; | |
| } | |
| } | |
| int redirect(char **args, int num) | |
| { | |
| char in[100],out[100],app[100]; | |
| int r = 0,pid,flag1=0,flag2=0,flag3=0; | |
| args[num] = NULL; | |
| pid = fork(); | |
| if (pid==0) | |
| { | |
| for (int i=0;i<num;i++) | |
| { | |
| if (strcmp(args[i],"<")==0) | |
| { | |
| args[i] = NULL; | |
| strcpy(in,args[i+1]); | |
| flag1 = 1; | |
| } | |
| if (args[i]!=NULL) | |
| { | |
| if (strcmp(args[i],">")==0) | |
| { | |
| args[i] = NULL; | |
| strcpy(out,args[i+1]); | |
| flag2 = 1; | |
| } | |
| } | |
| if (args[i]!=NULL) | |
| { | |
| if (args[i][1]==62) | |
| { | |
| args[i] = NULL; | |
| strcpy(app,args[i+1]); | |
| flag3 = 1; | |
| } | |
| } | |
| } | |
| if (flag1) | |
| { | |
| int fd; | |
| fd = open(in,O_RDONLY,0); | |
| if (fd<0) | |
| {perror("Could not open input file"); exit(0);} | |
| dup2(fd,0); | |
| close(fd); | |
| } | |
| if (flag2) | |
| { | |
| struct stat buf; | |
| if (stat(out,&buf)==0) | |
| { | |
| int fo; | |
| fo = open(out, O_WRONLY); | |
| if (fo<0) | |
| {perror("Could not open output file"); exit(0);} | |
| dup2(fo,1); | |
| close(fo); | |
| } | |
| else | |
| { | |
| int fo; | |
| fo = creat(out,0644); | |
| if (fo<0) | |
| {perror("Could not create output file"); exit(0);} | |
| dup2(fo,1); | |
| close(fo); | |
| } | |
| } | |
| if (flag3) | |
| { | |
| struct stat buffer; | |
| if (stat(app,&buffer)==0) | |
| { | |
| int fa; | |
| fa = open(app,O_APPEND | O_WRONLY); | |
| if (fa<0) | |
| {perror("Could not open output file"); exit(0);} | |
| dup2(fa,1); | |
| close(fa); | |
| } | |
| else | |
| { | |
| int fa; | |
| fa = creat(app,0644); | |
| if (fa<0) | |
| {perror("Could not create output file"); exit(0);} | |
| dup2(fa,1); | |
| close(fa); | |
| } | |
| } | |
| if (execvp(args[0],args)<0) | |
| {r=1; printf("%s: Command doesn't exist\n", args[0]);} | |
| } | |
| else | |
| {wait(NULL);} | |
| if (r!=1) | |
| {printf("%s with process id: %d exited normally\n",args[0],pid);} | |
| return 1; | |
| } | |
| int shell_setenv (char **args) { | |
| if (args[2] == NULL) args[2] = " "; | |
| if (setenv(args[1],args[2],1) != 0) perror("shell"); | |
| return 1; | |
| } | |
| int shell_unsetenv (char **args) { | |
| if (unsetenv(args[1]) != 0) perror("shell"); | |
| return 1; | |
| } | |
| int shell_cd (char** args) { | |
| if (args[1] == NULL) | |
| return 1; | |
| else if (strcmp(args[1],"~")==0){ | |
| chdir(root); | |
| } | |
| else if (chdir(args[1]) != 0) | |
| perror("SHELL"); | |
| return 1; | |
| } | |
| int shell_exit (char** args) { | |
| return EXIT_SUCCESS; | |
| } | |
| int shell_quit (char** args) { | |
| return EXIT_SUCCESS; | |
| } | |
| int shell_pwd (char** args) { | |
| char *curr_dir = (char *)malloc(1000*sizeof(char)); | |
| getcwd(curr_dir, 1000); | |
| printf("%s\n", curr_dir); | |
| free(curr_dir); | |
| return 1; | |
| } | |
| int shell_echo (char** args) { | |
| int i = 1; | |
| while(args[i] != NULL) { | |
| printf("%s ", args[i]); | |
| i++; | |
| } | |
| printf("\n"); | |
| return 1; | |
| } | |
| int shell_pinfo (char ** args) { | |
| char Process[1000]; | |
| strcpy(Process, "/proc/"); | |
| if(args[1]) | |
| strcat(Process, args[1]); | |
| else | |
| strcat(Process, "self"); | |
| char Stats[100]; | |
| strcpy(Stats, Process); strcat(Stats, "/stat"); | |
| int error_number[3]; | |
| error_number[0] = 0; | |
| FILE * stat = fopen(Stats, "r"); | |
| if(error_number[0]) { | |
| fprintf(stderr, "Error reading %s: %s\n", Stats, strerror(error_number[0])); | |
| return 1; | |
| } | |
| int pid; char status; char name[20]; | |
| fscanf(stat, "%d", &pid); fscanf(stat, "%s", name); fscanf(stat, " %c", &status); | |
| printf( "pid: %d\n", pid); | |
| printf( "Process Status: %c\n", status); | |
| fclose(stat); | |
| error_number[0] = 0; | |
| strcpy(Stats, Process); strcat(Stats, "/statm"); | |
| FILE * mem = fopen(Stats, "r"); | |
| if(error_number[0]) { | |
| fprintf(stderr, "Error reading %s: %s\n", Stats, strerror(error_number[0])); | |
| return 1; | |
| } | |
| int memSize; fscanf(mem, "%d", &memSize); | |
| fprintf(stdout, "Memory: %d\n", memSize); | |
| fclose(mem); | |
| char exePath[1000]; | |
| strcpy(Stats, Process); strcat(Stats, "/exe"); | |
| error_number[0] = 0; | |
| readlink(Stats, exePath, sizeof(exePath)); | |
| if(error_number[0]) { | |
| fprintf(stderr, "Error reading symbolic link %s: %s\n", Stats, strerror(error_number[0])); | |
| return 1; | |
| } | |
| int sameChars = 0, baseL = strlen(root); | |
| for(sameChars = 0; sameChars < baseL; sameChars++) | |
| if(root[sameChars] != exePath[sameChars]) break;; | |
| char relPath[1000]; | |
| if(sameChars == baseL) { | |
| relPath[0] = '~'; relPath[1] = '\0'; | |
| strcat(relPath, (const char *)&exePath[baseL]); | |
| } | |
| else { | |
| strcpy(relPath, exePath); | |
| relPath[strlen(exePath)] = '\0'; | |
| } | |
| int i = 0; | |
| while(exePath[i]) exePath[i++] = '\0'; | |
| fprintf(stdout, "Executable Path: %s\n", relPath); | |
| return 1; | |
| } | |
| int shell_ls (char** args) { | |
| DIR* dir; | |
| struct stat mystat[4]; | |
| struct passwd *user[2]; | |
| struct group *group[2]; | |
| char *buffer; | |
| int count[6]; | |
| count[1] = 0; | |
| count[2] = 0; | |
| count[3] = 0; | |
| count[4] = 1; | |
| count[5] = 0; | |
| unsigned char mod[13]; | |
| struct tm *starttime[2]; | |
| time_t now; | |
| int year; | |
| struct dirent **dient[4]; | |
| buffer = malloc(1024*sizeof(char)); | |
| while(args[count[4]] != NULL) { | |
| count[5] = 0; | |
| if (args[count[4]][count[5]] == 45) { | |
| count[5] = 1; | |
| while(args[count[4]][count[5]] != 0) { | |
| if(args[count[4]][count[5]] == 108) count[1] = 1; | |
| if(args[count[4]][count[5]] == 97) count[2] = 1; | |
| count[5]++; | |
| } | |
| } | |
| else { | |
| if (count[4] > 0) count[3] = count[4]; | |
| } | |
| count[4]++; | |
| } | |
| if (count[3] == 0 || args[count[3]] == NULL) args[count[3]] = "."; | |
| count[0] = scandir(".",&dient[0],NULL,alphasort); | |
| count[0] = scandir(args[count[3]],&dient[0],NULL,alphasort); | |
| if (count[0] < 0) perror("shell"); | |
| else { | |
| for(count[4]=0;count[4]<count[0];count[4]++) { | |
| if (count[1] == 1) { | |
| if (count[2] == 1) { | |
| sprintf(buffer,"%s/%s",args[count[3]],dient[0][count[4]]->d_name); | |
| stat(buffer,&mystat[0]); | |
| printf( (S_ISDIR(mystat[0].st_mode)) ? "d" : "-"); | |
| printf( (mystat[0].st_mode & S_IRUSR) ? "r" : "-"); | |
| printf( (mystat[0].st_mode & S_IWUSR) ? "w" : "-"); | |
| printf( (mystat[0].st_mode & S_IXUSR) ? "x" : "-"); | |
| printf( (mystat[0].st_mode & S_IRGRP) ? "r" : "-"); | |
| printf( (mystat[0].st_mode & S_IWGRP) ? "w" : "-"); | |
| printf( (mystat[0].st_mode & S_IXGRP) ? "x" : "-"); | |
| printf( (mystat[0].st_mode & S_IROTH) ? "r" : "-"); | |
| printf( (mystat[0].st_mode & S_IWOTH) ? "w" : "-"); | |
| printf( (mystat[0].st_mode & S_IXOTH) ? "x" : "-"); | |
| printf(" \t%d",(int)mystat[0].st_nlink); | |
| user[1] = getpwuid(mystat[0].st_uid); | |
| printf(" \t%s", user[1]->pw_name); | |
| group[1] = getgrgid(mystat[0].st_gid); | |
| printf(" \t%s", group[1]->gr_name); | |
| printf(" \t%lld",(long long)mystat[0].st_size); | |
| time(&now); | |
| year = localtime(&now)->tm_year; | |
| starttime[1] = localtime(&mystat[0].st_ctime); | |
| if(starttime[1]->tm_year == year) | |
| strftime(mod,13,"%b %e %R",starttime[1]); | |
| else | |
| strftime(mod,13,"%b %e %Y",starttime[1]); | |
| printf(" \t%s",mod ); | |
| printf(" \t%s\n",dient[0][count[4]]->d_name); | |
| } | |
| else { | |
| if ((dient[0][count[4]] ->d_name)[0] != 46) { | |
| sprintf(buffer,"%s/%s",args[count[3]],dient[0][count[4]]->d_name); | |
| stat(buffer,&mystat[0]); | |
| printf( (S_ISDIR(mystat[0].st_mode)) ? "d" : "-"); | |
| printf( (mystat[0].st_mode & S_IRUSR) ? "r" : "-"); | |
| printf( (mystat[0].st_mode & S_IWUSR) ? "w" : "-"); | |
| printf( (mystat[0].st_mode & S_IXUSR) ? "x" : "-"); | |
| printf( (mystat[0].st_mode & S_IRGRP) ? "r" : "-"); | |
| printf( (mystat[0].st_mode & S_IWGRP) ? "w" : "-"); | |
| printf( (mystat[0].st_mode & S_IXGRP) ? "x" : "-"); | |
| printf( (mystat[0].st_mode & S_IROTH) ? "r" : "-"); | |
| printf( (mystat[0].st_mode & S_IWOTH) ? "w" : "-"); | |
| printf( (mystat[0].st_mode & S_IXOTH) ? "x" : "-"); | |
| printf(" \t%d",(int)mystat[0].st_nlink); | |
| user[1] = getpwuid(mystat[0].st_uid); | |
| printf(" \t%s", user[1]->pw_name); | |
| group[1]=getgrgid(mystat[0].st_gid); | |
| printf(" \t%s", group[1]->gr_name); | |
| printf(" \t%lld",(long long)mystat[0].st_size); | |
| time(&now); | |
| year = localtime(&now)->tm_year; | |
| starttime[1] = localtime(&mystat[0].st_ctime); | |
| if(starttime[1]->tm_year == year) | |
| strftime(mod,13,"%b %e %R",starttime[1]); | |
| else | |
| strftime(mod,13,"%b %e %Y",starttime[1]); | |
| printf(" \t%s",mod ); | |
| printf(" \t%ld",mystat[0].st_mtime); | |
| printf(" \t%s\n",dient[0][count[4]]->d_name); | |
| } | |
| } | |
| } | |
| else { | |
| if (count[2] == 1) { | |
| printf(" %s\n",dient[0][count[4]]->d_name); | |
| } | |
| else { | |
| if ((dient[0][count[4]] ->d_name)[0] != 46) printf("%s ",dient[0][count[4]]->d_name); | |
| } | |
| } | |
| free(dient[0][count[4]]); | |
| } | |
| free(dient[0]); | |
| } | |
| return 1; | |
| } | |
| char* returnPath (char* cwd) { | |
| int i,cwd_size = strlen(cwd), root_size = strlen(root); | |
| if (root_size > cwd_size) { | |
| return cwd; | |
| } | |
| else if (root_size == cwd_size) { | |
| return "~"; | |
| } | |
| else { | |
| char *new = (char*)malloc(100); | |
| new[0] = '~'; | |
| new[1] = '/'; | |
| for (i = 0 ; i < cwd_size-root_size-1; i++) { | |
| new[i+2] = cwd[root_size+i+1]; | |
| } | |
| return new; | |
| } | |
| } | |
| void printPrompt (char *root) { | |
| char hostname[1024]; | |
| char cwd[1024]; | |
| hostname[1023] = '\0'; | |
| gethostname(hostname, 1023); | |
| char *username = getenv("USER"); | |
| getcwd(cwd, sizeof(cwd)); | |
| char *path = returnPath(cwd); | |
| printf("\n<%s@%s:%s/> ",username, hostname, path); | |
| } | |
| char *readCommands (void) { | |
| int buffer_size = 1024,check, pos = 0, i; | |
| char *buffer = malloc(sizeof(char) * buffer_size); | |
| if (!buffer) { | |
| fprintf(stderr, "ALLOCATION ERROR\n"); | |
| exit(EXIT_FAILURE); | |
| } | |
| for (i = 0; ;i++) { | |
| check = getchar(); | |
| if (check == EOF || check == '\n') { | |
| buffer[pos] = '\0'; | |
| return buffer; | |
| } | |
| else | |
| buffer[pos] = check; | |
| pos++; | |
| if (pos >= buffer_size) { | |
| buffer_size += 1024; | |
| buffer = realloc(buffer, buffer_size); | |
| if (!buffer) { | |
| fprintf(stderr, "ALLOCATION ERROR\n"); | |
| exit(EXIT_FAILURE); | |
| } | |
| } | |
| } | |
| } | |
| char **splitLine (char* line) { | |
| int buffer_size = COMM_BUFFER; | |
| int position = 0, i = 0; | |
| char **commands = malloc(buffer_size * (sizeof(char*))); | |
| char *command; | |
| if (!commands) { | |
| fprintf(stderr, "Allocaiton Error \n"); | |
| exit(EXIT_FAILURE); | |
| } | |
| command = strtok(line, COMM_DELIM); | |
| for (i = 0;; i++) { | |
| if (command!= NULL) { | |
| commands[position++] = command; | |
| if (buffer_size < position) { | |
| buffer_size += COMM_BUFFER; | |
| commands = realloc(commands, buffer_size * sizeof(char*)); | |
| if (!commands) { | |
| fprintf(stderr, "Allocaiton Error \n"); | |
| exit(EXIT_FAILURE); | |
| } | |
| } | |
| command = strtok(NULL, COMM_DELIM); | |
| continue; | |
| } | |
| break; | |
| } | |
| commands[position] = NULL; | |
| return commands; | |
| } | |
| char **splitCommand (char* line) { | |
| int buffer_size = TOK_BUFFER; | |
| int position = 0, i = 0; | |
| char **tokens = malloc(buffer_size * (sizeof(char*))); | |
| char *token; | |
| if (!tokens) { | |
| fprintf(stderr, "Allocaiton Error \n"); | |
| exit(EXIT_FAILURE); | |
| } | |
| token = strtok(line, TOK_DELIM); | |
| for (i = 0;; i++) { | |
| if (token!= NULL) { | |
| tokens[position++] = token; | |
| if (buffer_size < position) { | |
| buffer_size += TOK_BUFFER; | |
| tokens = realloc(tokens, buffer_size * sizeof(char*)); | |
| if (!tokens) { | |
| fprintf(stderr, "Allocaiton Error \n"); | |
| exit(EXIT_FAILURE); | |
| } | |
| } | |
| token = strtok(NULL, TOK_DELIM); | |
| continue; | |
| } | |
| break; | |
| } | |
| tokens[position] = NULL; | |
| return tokens; | |
| } | |
| int launch (char** args) { | |
| int i,j,background = 0, redirectflag = 0, piping = 0; | |
| for (i = 0;args[i] != NULL;i++) { | |
| for (j = 0;args[i][j] != '\0'; j++) { | |
| if (args[i][j] == '>' || args[i][j] == '<') { | |
| redirectflag = 1; | |
| } | |
| if (args[i][j] == '|') { | |
| piping = 1; | |
| } | |
| if (args[i][j] == '&') { | |
| background = 1; | |
| } | |
| } | |
| } | |
| if (piping) { | |
| pipehandler(args); | |
| return 1; | |
| } | |
| if (redirectflag) { | |
| return redirect(args, i); | |
| } | |
| pid_t pid, wpid; | |
| int state; | |
| pid = fork(); | |
| if (pid < 0) { | |
| perror("ERROR"); | |
| } | |
| else if(!pid) { | |
| if ( execvp(args[0], args) == -1) | |
| perror("ERROR"); | |
| exit(EXIT_FAILURE); | |
| } | |
| if(!background) { | |
| wpid = waitpid (pid, &state, WUNTRACED); | |
| for (i = 0;; i++) { | |
| if (!WIFEXITED(state) && !WIFSIGNALED(state)) { | |
| wpid = waitpid (pid, &state, WUNTRACED); | |
| continue; | |
| } | |
| break; | |
| } | |
| } | |
| return 1; | |
| } | |
| int checkCommand (char** args) { | |
| int count; | |
| if (args[0] == NULL) | |
| return EXIT_FAILURE; | |
| count = 0; | |
| while (count < sizeof(builtin_arr)/sizeof(char*)) { | |
| if (strcmp(args[0],builtin_arr[count]) == 0) | |
| return (*builtin_functions[count])(args); | |
| count++; | |
| } | |
| return launch(args); | |
| } | |
| void interpretCommand(void) { | |
| char **args; | |
| char *commands; | |
| char **split_line; | |
| int state; | |
| int i,j; | |
| signal(SIGINT,sigintHandler); | |
| for (i = 0;; i++) { | |
| printPrompt(root); | |
| commands = readCommands(); | |
| split_line = splitLine(commands); | |
| for (j = 0;split_line[j] != NULL ;j++) { | |
| args = splitCommand(split_line[j]); | |
| state = checkCommand(args); | |
| free (args); | |
| if (!state) { | |
| break; | |
| } | |
| } | |
| free(commands); | |
| free(split_line); | |
| if(!state) { | |
| break; | |
| } | |
| } | |
| } | |
| int main(int arg1, char **arg2) { | |
| root = getenv("PWD"); | |
| interpretCommand(); | |
| return EXIT_SUCCESS; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment