Operating Systems 2019W: Tutorial 2: Difference between revisions
No edit summary |
No edit summary |
||
Line 1: | Line 1: | ||
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). | 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). | ||
‘’’Make sure you use the original code form 3000shell for each question.’’’ | |||
==Getting Started== | ==Getting Started== |
Revision as of 14:10, 23 January 2019
In this tutorial you will be experimenting with and extending 3000shell.c (listed below).
‘’’Make sure you use the original code form 3000shell for each question.’’’
Getting Started
You should download 3000shell.c on a Ubuntu Linux 18.04 system or similar. Compile it using the command
gcc -O -Wall 3000shell.c -o 3000shell
In the labs, you should use your openstack instance you created for Tutorial 1.
Tasks/Questions
- Compile and run 3000shell.c
- Try running programs in the background using &. What happens to the input and output of the program? Try this for simple programs like ls and bc. Then, try it for more complex interactive programs such as nano and top.
- You may have trouble interacting with the shell after running programs in the background. How can you recover from such a situation?
- Why are lines 208-211 there (the check for pid == -1)?
- Make find_binary show every attempt to find a binary.
- Make the shell output "Ouch!" when you send it a SIGUSR1 signal.
- Delete line 243 (SA_RESTART). How does the behavior of 3000shell change?
- Replace the use of find_env() with getenv(). How do their interfaces differ?
- Use putenv() to implement LASTCOMMAND from Tutorial 1.
- Implement output redirection for running external programs, i.e. "ls > ls.log".
- Make plist output the parent process id for each process, e.g. "5123 ls (5122)". Pay attention to the stat and status files in the per-process directories in /proc.
- (Advanced) Implement I/O redirection for plist
- (Advanced) Implement a built-in 300kill command that works like the standard kill command.
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;
}