Operating Systems 2017F: Tutorial 2

From Soma-notes
Revision as of 22:15, 24 September 2017 by Soma (talk | contribs)
Jump to navigation Jump to search

THIS TUTORIAL IS NOT YET FINALIZED

In this tutorial you will be experimenting with and extending 3000shell.c (listed below).

Getting Started

You should download and run 3000shell.c on a Ubuntu Linux 16.04 system or similar. Compile it using the command

gcc -O -Wall 3000shell.c -o 3000shell

In the labs, use either the 2401 Virtualbox VM or create your own in [openstack.scs.carleton.ca SCS Openstack] using the Ubuntu 16.04 cloud image. If you have issues with Openstack please ask the TAs for assistance.

Experiments

Tasks

Code

/* 3000shell.c */
/* v1 Sept. 24, 2017 */
/* based off of csimpleshell.c, Enrico Franchi © 2005
      https://web.archive.org/web/20170223203852/
      http://rik0.altervista.org/snippets/csimpleshell.html */
/* Original under "BSD" license */
/* This version is under GPLv3, copyright Anil Somayaji */
/* You really shouldn't be incorporating parts of this in any other code,
   it is meant for teaching, not production */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <dirent.h>
#include <ctype.h>
#include <fcntl.h>
#include <signal.h>

#define BUFFER_SIZE 1<<16
#define ARR_SIZE 1<<16
#define COMM_SIZE 32

const char *proc_prefix = "/proc";

void parse_args(char *buffer, char** args, 
                size_t args_size, size_t *nargs)
{
    char *buf_args[args_size]; /* You need C99 */
    char **cp;
    char *wbuf;
    size_t i, j;
    
    wbuf=buffer;
    buf_args[0]=buffer; 
    args[0] =buffer;
    
    for(cp=buf_args; (*cp=strsep(&wbuf, " \n\t")) != NULL ;){
        if ((*cp != '\0') && (++cp >= &buf_args[args_size]))
            break;
    }
    
    for (j=i=0; buf_args[i]!=NULL; i++){
        if(strlen(buf_args[i])>0)
            args[j++]=buf_args[i];
    }
    
    *nargs=j;
    args[j]=NULL;
}


/* this is kind of like getenv() */
char *find_env(char *envvar, char *notfound, char *envp[])
{
        const int MAXPATTERN = 128;
        int i, p;
        char c;
        char pattern[MAXPATTERN];
        char *value = NULL;

        p = 0;
        while ((c = envvar[p])) {
                pattern[p] = c;
                p++;
                if (p == (MAXPATTERN - 2)) {
                        break;
                }
        }
        pattern[p] = '=';
        p++;
        pattern[p] = '\0';
        
        i = 0;
        while (envp[i] != NULL) {
                if (strncmp(pattern, envp[i], p) == 0) {                        
                        value = envp[i] + p;
                }
                i++;
        }

        if (value == NULL) {
                return notfound;
        } else {
                return value;
        }
}

void find_binary(char *name, char *path, char *fn, int fn_size) {
        char *n, *p;
        int r, stat_return;

        struct stat file_status;

        if (name[0] == '.' || name[0] == '/') {
                strncpy(fn, name, fn_size);
                return;
        }
        
        p = path;
        while (*p != '\0') {       
                r = 0;
                while (*p != '\0' && *p != ':' && r < fn_size - 1) {
                        fn[r] = *p;
                        r++;
                        p++;
                }

                fn[r] = '/';
                r++;
                
                n = name;
                while (*n != '\0' && r < fn_size) {
                        fn[r] = *n;
                        n++;
                        r++;
                }
                fn[r] = '\0';

                
                stat_return = stat(fn, &file_status);

                if (stat_return == 0) {
                        return;
                }

                if (*p != '\0') {
                        p++;
                }
        }
}

void setup_comm_fn(char *pidstr, char *comm_fn)
{
        char *c;

        strcpy(comm_fn, proc_prefix);
        c = comm_fn + strlen(comm_fn);
        *c = '/';
        c++;
        strcpy(c, pidstr);
        c = c + strlen(pidstr);
        strcpy(c, "/comm");
}

void plist()
{
        DIR *proc;
        struct dirent *e;
        int result;
        char comm[COMM_SIZE];  /* seems to just need 16 */        
        char comm_fn[512];
        int fd, i, n;

        proc = opendir(proc_prefix);

        if (proc == NULL) {
                fprintf(stderr, "ERROR: Couldn't open /proc.\n");
        }
        
        for (e = readdir(proc); e != NULL; e = readdir(proc)) {
                if (isdigit(e->d_name[0])) {
                        setup_comm_fn(e->d_name, comm_fn);
                        fd = open(comm_fn, O_RDONLY);
                        if (fd > -1) {                                
                                n = read(fd, comm, COMM_SIZE);
                                close(fd);
                                for (i=0; i < n; i++) {
                                        if (comm[i] == '\n') {
                                                comm[i] = '\0';
                                                break;
                                        }
                                }
                                printf("%s: %s\n", e->d_name, comm);
                        } else {
                                printf("%s\n", e->d_name);
                        }
                }
        }
        
        result = closedir(proc);
        if (result) {
                fprintf(stderr, "ERROR: Couldn't close /proc.\n");
        }
}

void signal_handler(int the_signal)
{
        int pid, status;

        if (the_signal == SIGHUP) {
                fprintf(stderr, "Received SIGHUP.\n");
                return;
        }
        
        if (the_signal != SIGCHLD) {
                fprintf(stderr, "Child handler called for signal %d?!\n",
                        the_signal);
                return;
        }

        pid = wait(&status);

        if (pid == -1) {
                /* nothing to wait for */
                return;
        }
        
        if (WIFEXITED(status)) {
                fprintf(stderr, "\nProcess %d exited with status %d.\n",
                        pid, WEXITSTATUS(status));
        } else {
                fprintf(stderr, "\nProcess %d aborted.\n", pid);
        }
}


int main(int argc, char *argv[], char *envp[])
{
    char buffer[BUFFER_SIZE];
    char *args[ARR_SIZE];
    char bin_fn[BUFFER_SIZE];
    struct sigaction signal_handler_struct;
    char *s;    

    int *ret_status = NULL;
    size_t nargs;
    pid_t pid;
    int background;
    
    char *username;
    char *default_username = "UNKNOWN";

    char *path;
    char *default_path = "/usr/bin:/bin";

    memset (&signal_handler_struct, 0, sizeof(signal_handler_struct));
    signal_handler_struct.sa_handler = signal_handler;
    signal_handler_struct.sa_flags = SA_RESTART;
    
    if (sigaction(SIGCHLD, &signal_handler_struct, NULL)) {
            fprintf(stderr, "Couldn't register SIGCHLD handler.\n");
    }

    if (sigaction(SIGHUP, &signal_handler_struct, NULL)) {
            fprintf(stderr, "Couldn't register SIGHUP handler.\n");
    }
    
    username = find_env("USER", default_username, envp);
    path = find_env("PATH", default_path, envp);
        
    while(1){
            printf("%s $ ", username);
            s = fgets(buffer, BUFFER_SIZE, stdin);
            
            if (s == NULL) {
                    /* we reached EOF */
                    printf("\n");
                    exit(0);
            }
        
            parse_args(buffer, args, ARR_SIZE, &nargs); 
            
            if (nargs==0) continue;
            
            if (!strcmp(args[0], "exit")) {
                    exit(0);
            }
            
            if (!strcmp(args[0], "plist")) {
                    plist();
                    continue;
            }

            background = 0;            
            if (strcmp(args[nargs-1], "&") == 0) {
                    background = 1;
                    args[nargs-1] = NULL;
                    nargs--;
            }
            
            pid = fork();
            if (pid) {
                    if (background) {
                            fprintf(stderr,
                                    "Process %d running in the background.\n",
                                   pid);
                    } else {
                            pid = wait(ret_status);
                    }
            } else {
                    find_binary(args[0], path, bin_fn, BUFFER_SIZE);
                    
                    if (execve(bin_fn, args, envp)) {
                            puts(strerror(errno));
                            exit(127);
                    }
            }
    }    
    return 0;
}