#include #include #include #include #include #include #include #include #include #include #include #include #include /* Macros */ #define MAX_STRING_LENGTH 200 #define MAX_TOKENS 100 #define MAX_TOKEN_LENGTH 30 #define MAX_SIMPLE 5 #define MAX_PIPES 5 #define NOT_FOUND -1 #define REGULAR -1 #define DEFAULT_PERMISSION 0660 #define DEFAULT_PROTOCOL 0 #define DEFAULT_QUEUE_LENGTH 5 #define SOCKET_SLEEP 1 /* Enumerators */ enum { FALSE, TRUE }; enum metacharacterEnum { SEMICOLON, BACKGROUND, END_OF_LINE, REDIRECT_OUTPUT, REDIRECT_INPUT, APPEND_OUTPUT, PIPE, REDIRECT_OUTPUT_SERVER, REDIRECT_OUTPUT_CLIENT, REDIRECT_INPUT_SERVER, REDIRECT_INPUT_CLIENT }; enum builtInEnum { ECHO_BUILTIN, SETENV, GETENV, CD }; enum descriptorEnum { STDIN, STDOUT, STDERR }; enum pipeEnum { READ, WRITE }; enum IOEnum { NO_REDIRECT, FILE_REDIRECT, SERVER_REDIRECT, CLIENT_REDIRECT }; enum socketEnum { CLIENT, SERVER }; enum { INPUT_SOCKET, OUTPUT_SOCKET }; /* Every simple command has one of these associated with it */ struct simple { char* token [MAX_TOKENS]; /* The tokens of the command */ int count; /* The number of tokens */ int outputRedirect; /* Set to an IOEnum */ int inputRedirect; /* Set to an IOEnum */ int append; /* Set to true for append mode */ char *outputFile; /* Name of output file or NULL if none */ char *inputFile; /* Name of input file or NULL if none */ char *outputSocket; /* Output socket name or NULL if none */ char *inputSocket; /* Name of input socket or NULL if none */ }; /* Every pipeline has one of these associated with it */ struct pipeline { struct simple simple [MAX_SIMPLE]; /* Commands in pipe */ int count; /* The number of simple commands */ }; /* Every command sequence has one of these associated with it */ struct sequence { struct pipeline pipeline [MAX_PIPES]; /* Pipes in sequence */ int count; /* The number of pipes */ int background; /* True if this is a background sequence */ }; /* Prototypes */ struct sequence parseSequence (); struct pipeline parsePipeline (); struct simple parseSimple (); char *nextToken (); char *peekToken (); char *lastToken (); char* getToken (); /* Globals */ char* metacharacters [] = { ";", "&", "\n", ">", "<", ">>", "|", "@>s", "@>c", "@ 1) /* Process any non-empty line */ { sequence = parseSequence (); /* Parse the line */ /* If no errors occurred during the parsing, */ /* execute the command */ if (!errorFlag) executeSequence (&sequence); } } } /****************************************************************/ /* PARSER ROUTINES */ /****************************************************************/ struct sequence parseSequence () { struct sequence q; /* Parse a command sequence and return structure description */ q.count = 0; /* Number of pipes in the sequence */ q.background = FALSE; /* Default is not in background */ while (TRUE) /* Loop until no semicolon delimiter is found */ { q.pipeline[q.count++] = parsePipeline (); /* Parse */ if (peekCode () != SEMICOLON) break; nextToken (); /* Flush semicolon delimiter */ } if (peekCode () == BACKGROUND) /* Sequence is in background */ { q.background = TRUE; nextToken (); /* Flush ampersand */ } getToken (END_OF_LINE); /* Check end-of-line is reached */ return (q); } /****************************************************************/ struct pipeline parsePipeline () { struct pipeline p; /* Parse a pipeline and return a structure description of it */ p.count = 0; /* The number of simple commands in the pipeline */ while (TRUE) /* Loop until no pipe delimiter is found */ { p.simple[p.count++] = parseSimple (); /* Parse command */ if (peekCode () != PIPE) break; nextToken (); /* Flush pipe delimiter */ } return (p); } /****************************************************************/ struct simple parseSimple () { struct simple s; int code; int done; /* Parse a simple command and return a structure description */ s.count = 0; /* The number of tokens in the simple command */ s.outputFile = s.inputFile = NULL; s.inputSocket = s.outputSocket = NULL; s.outputRedirect = s.inputRedirect = NO_REDIRECT; /* Defaults */ s.append = FALSE; while (peekCode () == REGULAR) /* Store all regular tokens */ s.token[s.count++] = nextToken (); s.token[s.count] = NULL; /* NULL-terminate token list */ done = FALSE; /* Parse special metacharacters that follow, like > and >> */ do { code = peekCode ();/* Peek at next token */ switch (code) { case REDIRECT_INPUT: /* < */ nextToken (); s.inputFile = getToken (REGULAR); s.inputRedirect = FILE_REDIRECT; break; case REDIRECT_OUTPUT: /* > */ case APPEND_OUTPUT: /* >> */ nextToken (); s.outputFile = getToken (REGULAR); s.outputRedirect = FILE_REDIRECT; s.append = (code == APPEND_OUTPUT); break; case REDIRECT_OUTPUT_SERVER: /* @>s */ nextToken (); s.outputSocket = getToken (REGULAR); s.outputRedirect = SERVER_REDIRECT; break; case REDIRECT_OUTPUT_CLIENT: /* @>c */ nextToken (); s.outputSocket = getToken (REGULAR); s.outputRedirect = CLIENT_REDIRECT; break; case REDIRECT_INPUT_SERVER: /* @background) /* Execute in background */ { if (fork () == 0) { printf ("[%d]\n", getpid ()); /* Display child PID */ /* Child process */ signal (SIGQUIT, originalQuitHandler); /* Oldhandler */ setpgid (0, getpid ()); /* Change process group */ for (i = 0; i < p->count; i++) /* Execute pipelines */ executePipeline (&p->pipeline[i]); exit (/* EXIT_SUCCESS */ 0); } } else /* Execute in foreground */ for (i = 0; i < p->count; i++) /* Execute each pipeline */ executePipeline (&p->pipeline[i]); } /****************************************************************/ executePipeline (p) struct pipeline *p; { int pid, processGroup, result; /* Execute every simple command in pipeline (possibly one) */ if (p->count == 1 && builtIn (p->simple[0].token[0])) executeSimple (&p->simple[0]); /* Execute it directly */ else { if ((pid = fork ()) == 0) { /* Child shell executes the simple commands */ if (p->count == 1) executeSimple (&p->simple[0]); /* Execute command */ else executePipes (p); /* Execute more than one command */ exit ( /* EXIT_SUCCESS */ 0); } else { /* Parent shell waits for child to complete */ waitForPID (pid); } } } /****************************************************************/ waitForPID (pid) int pid; { int status; /* Return when the child process with PID pid terminates */ while (wait (&status) != pid); } /****************************************************************/ executePipes (p) struct pipeline *p; { int pipes, status, i; int pipefd [MAX_PIPES][2]; /* Execute two or more simple commands connected by pipes */ pipes = p->count - 1; /* Number of pipes to build */ for (i = 0; i < pipes; i++) /* Build the pipes */ pipe (pipefd[i]); for (i = 0; i < p->count; i++) /* Build one process per pipe */ { if (fork () != 0) continue; /* Child shell */ /* First, connect stdin to pipe if not the first command */ if (i != 0) dup2 (pipefd[i-1][READ], STDIN); /* Second, connect stdout to pipe if not the last command */ if (i != p->count - 1) dup2 (pipefd[i][WRITE], STDOUT); /* Third, close all of the pipes' file descriptors */ closeAllPipes (pipefd, pipes); /* Last, execute the simple command */ executeSimple (&p->simple[i]); exit (/* EXIT_SUCCESS */0); } /* The parent shell comes here after forking the children */ closeAllPipes (pipefd, pipes); for (i = 0; i < p->count; i++) /* Wait for children to finish */ wait (&status); } /****************************************************************/ closeAllPipes (pipefd, pipes) int pipefd [][2]; int pipes; { int i; /* Close every pipe's file descriptors */ for (i = 0; i < pipes; i++) { close (pipefd[i][READ]); close (pipefd[i][WRITE]); } } /****************************************************************/ executeSimple (p) struct simple* p; { int copyStdin, copyStdout; /* Execute a simple command */ if (builtIn (p->token[0])) /* Built-in */ { /* The parent shell is executing this, so remember */ /* stdin and stdout in case of built-in redirection */ copyStdin = dup (STDIN); copyStdout = dup (STDOUT); if (redirect (p)) executeBuiltIn (p); /* Execute built-in */ /* Restore stdin and stdout */ dup2 (copyStdin, STDIN); dup2 (copyStdout, STDOUT); close (copyStdin); close (copyStdout); } else if (redirect (p)) /* Redirect if necessary */ executePrimitive (p); /* Execute primitive command */ } /****************************************************************/ executePrimitive (p) struct simple* p; { /* Execute a command by exec'ing */ if (execvp (p->token[0], p->token) == -1) { perror ("ish"); exit (/* EXIT_FAILURE */ 1); } } /****************************************************************/ /* BUILT-IN COMMANDS */ /****************************************************************/ builtInCode (token) char* token; { /* Return the index of token in the builtIns array */ return (findString (builtIns, token)); } /****************************************************************/ builtIn (token) char* token; { /* Return true if token is a built-in */ return (builtInCode (token) != NOT_FOUND); } /****************************************************************/ executeBuiltIn (p) struct simple* p; { /* Execute a single built-in command */ switch (builtInCode (p->token[0])) { case CD: executeCd (p); break; case ECHO_BUILTIN: executeEcho (p); break; case GETENV: executeGetenv (p); break; case SETENV: executeSetenv (p); break; } } /****************************************************************/ executeEcho (p) struct simple* p; { int i; /* Echo the tokens in this command */ for (i = 1; i < p->count; i++) printf ("%s "> p->token[i]); printf ("\n"); } /****************************************************************/ executeGetenv (p) struct simple* p; { char* value; /* Echo the value of an environment variable */ if (p->count != 2) { error ("Usage: getenv variable\n"); return; } value = getenv (p->token[1]); if (value == NULL) printf ("Environment variable is not currently set\n"); else printf ("%s\n", value); } /****************************************************************/ executeSetenv (p) struct simple* p; { /* Set the value of an environment variable */ if (p->count != 3) error ("Usage: setenv variable value\n"); else setenv (p->token[1], p->token[2]); } /****************************************************************/ setenv (envName, newValue) char* envName; char* newValue; { int i = 0; char newStr [MAX_STRING_LENGTH]; int len; /* Set the environment variable envName to newValue */ sprintf (newStr, "%s=%s", envName, newValue); len = strlen (envName) + 1; while (environ[i] != NULL) { if (strncmp (environ[i], newStr, len) == 0) break; ++i; } if (environ[i] == NULL) environ[i+1] = NULL; environ[i] = (char*) malloc (strlen (newStr) + 1); strcpy (environ[i], newStr); } /****************************************************************/ executeCd (p) struct simple* p; { /* Change directory */ if (p->count != 2) error ("Usage: cd path\n"); else if (chdir (p->token[1]) == -1) perror ("ish"); } /****************************************************************/ /* REDIRECTION */ /****************************************************************/ redirect (p) struct simple *p; { int mask; /* Perform input redirection */ switch (p->inputRedirect) { case FILE_REDIRECT: /* Redirect from a file */ if (!dupFd (p->inputFile, O_RDONLY, STDIN)) return(FALSE); break; case SERVER_REDIRECT: /* Redirect from a server socket */ if (!server (p->inputSocket, INPUT_SOCKET)) return(FALSE); break; case CLIENT_REDIRECT: /* Redirect from a client socket */ if (!client (p->inputSocket, INPUT_SOCKET)) return(FALSE); break; } /* Perform output redirection */ switch (p->outputRedirect) { case FILE_REDIRECT: /* Redirect to a file */ mask = O_CREAT | O_WRONLY | (p->append?O_APPEND:O_TRUNC); if (!dupFd (p->outputFile, mask, STDOUT)) return (FALSE); break; case SERVER_REDIRECT: /* Redirect to a server socket */ if (!server(p->outputSocket,OUTPUT_SOCKET)) return(FALSE); break; case CLIENT_REDIRECT: /* Redirect to a client socket */ if (!client(p->outputSocket,OUTPUT_SOCKET)) return(FALSE); break; } return (TRUE); /* If I got here, then everything went OK */ } /****************************************************************/ dupFd (name, mask, stdFd) char* name; int mask, stdFd; { int fd; /* Duplicate a new file descriptor over stdin/stdout */ fd = open (name, mask, DEFAULT_PERMISSION); if (fd == -1) { error ("Cannot redirect\n"); return (FALSE); } dup2 (fd, stdFd); /* Copy over standard file descriptor */ close (fd); /* Close other one */ return (TRUE); } /****************************************************************/ /* SOCKET MANAGEMENT */ /****************************************************************/ internetAddress (name) char* name; { /* If name contains a digit, assume it's an internet address */ return (strpbrk (name, "01234567890") != NULL); } /****************************************************************/ socketRedirect (type) int type; { return (type == SERVER_REDIRECT || type == CLIENT_REDIRECT); } /****************************************************************/ getHostAndPort (str, name, port) char *str, *name; int* port; { char *tok1, *tok2; /* Decode name and port number from input string of the form */ /* NAME.PORT */ tok1 = strtok (str, "."); tok2 = strtok (NULL, "."); if (tok2 == NULL) /* Name missing, so assume local host */ { strcpy (name, ""); sscanf (tok1, "%d", port); } else { strcpy (name, tok1); sscanf (tok2, "%d", port); } } /****************************************************************/ client (name, type) char* name; int type; { int clientFd, result, internet, domain, serverLen, port; char hostName [100]; struct sockaddr_un serverUNIXAddress; struct sockaddr_in serverINETAddress; struct sockaddr* serverSockAddrPtr; struct hostent* hostStruct; struct in_addr* hostNode; /* Open a client socket with specified name and type */ internet = internetAddress (name); /* Internet socket? */ domain = internet ? AF_INET : AF_UNIX; /* Pick domain */ /* Create client socket */ clientFd = socket (domain, SOCK_STREAM, DEFAULT_PROTOCOL); if (clientFd == -1) { perror ("ish"); return (FALSE); } if (internet) /* Internet socket */ { getHostAndPort (name, hostName, &port); /* Get name, port */ if (hostName[0] == NULL) gethostname (hostName, 100); serverINETAddress.sin_family = AF_INET; /* Internet */ hostStruct = gethostbyname (hostName); /* Find host */ if (hostStruct == NULL) { perror ("ish"); return (FALSE); } hostNode = (struct in_addr*) hostStruct->h_addr; printf ("IP address = %s\n", inet_ntoa (*hostNode)); serverINETAddress.sin_addr = *hostNode; /* Set IP address */ serverINETAddress.sin_port = port; /* Set port */ serverSockAddrPtr = (struct sockaddr*) &serverINETAddress; serverLen = sizeof (serverINETAddress); } else /* UNIX domain socket */ { serverUNIXAddress.sun_family = AF_UNIX; /* Domain */ strcpy (serverUNIXAddress.sun_path, name); /* File name */ serverSockAddrPtr = (struct sockaddr*) &serverUNIXAddress; serverLen = sizeof (serverUNIXAddress); } do /* Connect to server */ { result = connect (clientFd, serverSockAddrPtr, serverLen); if (result == -1) sleep (SOCKET_SLEEP); /* Try again soon */ } while (result == -1); /* Perform redirection */ if (type == OUTPUT_SOCKET) dup2 (clientFd, STDOUT); if (type == INPUT_SOCKET) dup2 (clientFd, STDIN); close (clientFd); /* Close original client file descriptor */ return (TRUE); } /****************************************************************/ server (name, type) char* name; int type; { int serverFd, clientFd, serverLen, clientLen; int domain, internet, port; struct sockaddr_un serverUNIXAddress; struct sockaddr_un clientUNIXAddress; struct sockaddr_in serverINETAddress; struct sockaddr_in clientINETAddress; struct sockaddr* serverSockAddrPtr; struct sockaddr* clientSockAddrPtr; /* Prepare a server socket */ internet = internetAddress (name); /* Internet? */ domain = internet ? AF_INET : AF_UNIX; /* Pick domain */ /* Create the server socket*/ serverFd = socket (domain, SOCK_STREAM, DEFAULT_PROTOCOL); if (serverFd == -1) { perror ("ish"); return (FALSE); } if (internet) /* Internet socket */ { sscanf (name, "%d", &port); /* Get port number */ /* Fill in server socket address fields */ serverLen = sizeof (serverINETAddress); bzero ((char*) &serverINETAddress, serverLen); serverINETAddress.sin_family = AF_INET; /* Domain */ serverINETAddress.sin_addr.s_addr = htonl (INADDR_ANY); serverINETAddress.sin_port = htons (port); /* Port */ serverSockAddrPtr = (struct sockaddr*) &serverINETAddress; } else /* UNIX domain socket */ { serverUNIXAddress.sun_family = AF_UNIX; /* Domain */ strcpy (serverUNIXAddress.sun_path,name); /* Filename */ serverSockAddrPtr = (struct sockaddr*) &serverUNIXAddress; serverLen = sizeof (serverUNIXAddress); unlink (name); /* Delete socket if it already exists */ } /* Bind to socket address */ if (bind (serverFd, serverSockAddrPtr, serverLen) == -1) { perror ("ish"); return (FALSE); } /* Set max pending connection queue length */ if (listen (serverFd, DEFAULT_QUEUE_LENGTH) == -1) { perror ("ish"); return (FALSE); } if (internet) /* Internet socket */ { clientLen = sizeof (clientINETAddress); clientSockAddrPtr = (struct sockaddr*) &clientINETAddress; } else /* UNIX domain socket */ { clientLen = sizeof (clientUNIXAddress); clientSockAddrPtr = (struct sockaddr*) &clientUNIXAddress; } /* Accept a connection */ clientFd = accept (serverFd, clientSockAddrPtr, &clientLen); close (serverFd); /* Close original server socket */ if (clientFd == -1) { perror ("ish"); return (FALSE); } /* Perform redirection */ if (type == OUTPUT_SOCKET) dup2 (clientFd, STDOUT); if (type == INPUT_SOCKET) dup2 (clientFd, STDIN); close (clientFd); /* Close original client socket */ return (TRUE); }