Operating Systems 2017F: Tutorial 2: Difference between revisions
Created page with "In this tutorial you will be experimenting with and extending [http://homeostasis.scs.carleton.ca/~soma/os-2017f/code/tut2/3000shell.c 3000shell.c] (listed below). ==Getting..." |
|||
Line 12: | Line 12: | ||
==Code== | ==Code== | ||
<source lang="c" | <source lang="c" line> | ||
/* 3000shell.c */ | /* 3000shell.c */ | ||
/* v1 Sept. 24, 2017 */ | /* v1 Sept. 24, 2017 */ |
Revision as of 02:09, 25 September 2017
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.
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;
}