Logo  

CS456 - Systems Programming

Process invocation and an internal process table

The following is the code to actually process the command stack:

extern cmd_t *cmdstack[MAXCMDS];
extern int csp;

void sys(void)
{
  pid_t pid;

  int outfd = -1;
  for(int cp = csp-1; cp >= 0; cp--) {
    if (cmdstack[cp]->argv[0] == NULL) continue;

    if (strcmp(cmdstack[cp]->argv[0], "cd") == 0) {
      if (cmdstack[cp]->argv[1] != NULL) chdir(cmdstack[cp]->argv[1]);
      else chdir(getenv("HOME"));
    } else {
      pid = run(cmdstack[cp], &outfd, cp > 0);
      if (pid < 0) break;
      procs[++psp] = pid;
    }
  }

  flushcmds();

  waitall();
}


First outfd is initialized to -1, indicating that the top command in the command stack is not writing to a pipe. We use our own command pointer rather than modifying the command stack pointer as flushcmds will reset it. The sanity check for null commands allows for such syntax errors as: "cmd |" or "| cmd", "cmd | | cmd", etc. Such error checking should be done at the point of pushing the command and aborting the entire pipeline.

Of particular note is here we handle the "cd" command, which must always be handled by the shell as a built-in command, which changes the shells current working directory via the chdir() system call. Most shells have many built-in commands, if only for efficiency (such as echo). Common built-in commands include:

cd Change the current working directory
exit / logout Exit the shell
jobs Displays stopped or backgrounded jobs
fg Places a background or stopped job into the foreground
bg Places a stopped job into the background
setenv / export Update or modify the environment
echo / printf Print strings to the output
limit / ulimit Set the resource limits for processes
nice Launch jobs with a specific CPU priority
nohup Prevent commands from being sent a hangup signal on logout (used mostly for persistent background jobs)


to name a few.

If it's not the cd built-in, then we attempt to launch the command in the normal way via the run() function, pushing the pid of the launched command onto a process stack (procs). After the command stack has been flushed, we wait for all the launched jobs to complete in the waitall() function:

int procs[MAXCMDS];
int psp = 0;

void waitall()
{
  while (psp) {
    pid_t ret, pid = procs[psp--];
    do {
      ret = waitpid(pid, NULL, 0);
    } while (ret != pid);
  }
}


In this version we're just using waitpid to specifically wait for the top most job in the command stack to complete. Jobs below it may complete, but we can wait to reap them until the we've reaped the current one. Normally the top most job will be the first job in a pipeline, so is most likely to finish first, so this will still most likely reap the processes in the most likely order of completion.

There is no support for job control in this setup and waitall() will likely need to be replaced entirely with a pause() loop or some other mechanism to pause the shell until all foreground processes have completed or been stopped. A more complicated process table indicating process state and foreground / background status will also be required to implement job control.

invoke.c

#include "shell.h"

void redir(int nfd, char *name, int flags)
{
  close(nfd);
  if (open(name, flags, 0666) != nfd) {
    perror(name);
    exit(1);
  }
}

pid_t run(cmd_t *c, int *outfd, int inpipe)
{
  int pfd[2];

  if (inpipe) {
    if (pipe(pfd) < 0) {
      perror("pipe");
      return -1;
    }
  }

  pid_t pid = fork();
  if (pid < 0) {
    perror("fork");
    return -1;
  }
  if (pid > 0) {
    if (*outfd >= 0) close(*outfd);
    if (inpipe) {
      close(pfd[0]);
      *outfd = pfd[1];
    }
    return pid;
  }

  /* Handle i/o redirections here: */
  if (c->input != NULL) redir(STDIN_FILENO, c->input, O_RDONLY);
  if (c->output != NULL) redir(STDOUT_FILENO, c->output, O_WRONLY|O_CREAT| (c->append? O_APPEND: O_TRUNC));
  if (*outfd >= 0) {
    dup2(*outfd, STDOUT_FILENO);
    close(*outfd);
  }
  if (inpipe) {
    close(pfd[1]);
    dup2(pfd[0], STDIN_FILENO);
    close(pfd[0]);
  }
  execvp(c->argv[0], c->argv);
  perror("exec");
  exit(1);
}

int procs[MAXCMDS];
int psp = 0;

void waitall()
{
  while (psp) {
    pid_t ret, pid = procs[psp--];
    do {
      ret = waitpid(pid, NULL, 0);
    } while (ret != pid);
  }
}

extern cmd_t *cmdstack[MAXCMDS];
extern int csp;

void sys(void)
{
  pid_t pid;

  int outfd = -1;
  for(int cp = csp-1; cp >= 0; cp--) {
    if (cmdstack[cp]->argv[0] == NULL) continue;

    if (strcmp(cmdstack[cp]->argv[0], "cd") == 0) {
      if (cmdstack[cp]->argv[1] != NULL) chdir(cmdstack[cp]->argv[1]);
      else chdir(getenv("HOME"));
    } else {
      pid = run(cmdstack[cp], &outfd, cp > 0);
      if (pid < 0) break;
      procs[++psp] = pid;
    }
  }

  flushcmds();

  waitall();
}