/Classwork/CS3214 - Computer Systems/Project 5 - Web Service/Makefile |
---|
0,0 → 1,13 |
CC = gcc |
#CFLAGS=-Wall -O3 -Werror |
CFLAGS=-Wall -O1 -g -Werror |
LDLIBS=-lpthread |
# Use make's default rules |
all: sysstatd |
sysstatd: sysstatd.o csapp.o threadpool_exec.o list.o |
clean: |
rm -f *.o sysstatd |
/Classwork/CS3214 - Computer Systems/Project 5 - Web Service/README |
---|
0,0 → 1,15 |
Group members: Kevin Lee (klee482) |
Sysstatd is a simple webserver that allows a client to send commands |
and pull data from the webserver. The code for binding a TCP socket |
that listens for clients is seperated from the HTTP parsing code. This |
allows the webserver to easily be reimplemented to connect to a relay |
server instead of hosting. For each connection, the processing is sent |
to a thread pool that parses the HTTP request that the client sends. |
This allows the webserver to server multiple clients at once. The |
webserver also keeps the connection open, thus it implements persistant |
connections as according to HTTP/1.1. Within each processing thread, |
the data header is checked and the specified action is taken. The |
webserver supports querying for files, loadavg, and meminfo. It also |
provides an interface for calling a runloop, allocanon, and freeanon |
function. 404s are returned for unknown queries. |
/Classwork/CS3214 - Computer Systems/Project 5 - Web Service/csapp.c |
---|
0,0 → 1,828 |
/* $begin csapp.c */ |
#include "csapp.h" |
/************************** |
* Error-handling functions |
**************************/ |
/* $begin errorfuns */ |
/* $begin unixerror */ |
void unix_error(char *msg) /* unix-style error */ |
{ |
fprintf(stderr, "%s: %s\n", msg, strerror(errno)); |
// exit(0); |
} |
/* $end unixerror */ |
void posix_error(int code, char *msg) /* posix-style error */ |
{ |
fprintf(stderr, "%s: %s\n", msg, strerror(code)); |
// exit(0); |
} |
void dns_error(char *msg) /* dns-style error */ |
{ |
fprintf(stderr, "%s: DNS error %d\n", msg, h_errno); |
// exit(0); |
} |
void app_error(char *msg) /* application error */ |
{ |
fprintf(stderr, "%s\n", msg); |
// exit(0); |
} |
/* $end errorfuns */ |
/********************************************* |
* Wrappers for Unix process control functions |
********************************************/ |
/* $begin forkwrapper */ |
pid_t Fork(void) |
{ |
pid_t pid; |
if ((pid = fork()) < 0) |
unix_error("Fork error"); |
return pid; |
} |
/* $end forkwrapper */ |
void Execve(const char *filename, char *const argv[], char *const envp[]) |
{ |
if (execve(filename, argv, envp) < 0) |
unix_error("Execve error"); |
} |
/* $begin wait */ |
pid_t Wait(int *status) |
{ |
pid_t pid; |
if ((pid = wait(status)) < 0) |
unix_error("Wait error"); |
return pid; |
} |
/* $end wait */ |
pid_t Waitpid(pid_t pid, int *iptr, int options) |
{ |
pid_t retpid; |
if ((retpid = waitpid(pid, iptr, options)) < 0) |
unix_error("Waitpid error"); |
return(retpid); |
} |
/* $begin kill */ |
void Kill(pid_t pid, int signum) |
{ |
int rc; |
if ((rc = kill(pid, signum)) < 0) |
unix_error("Kill error"); |
} |
/* $end kill */ |
void Pause() |
{ |
(void)pause(); |
return; |
} |
unsigned int Sleep(unsigned int secs) |
{ |
unsigned int rc; |
if ((rc = sleep(secs)) < 0) |
unix_error("Sleep error"); |
return rc; |
} |
unsigned int Alarm(unsigned int seconds) { |
return alarm(seconds); |
} |
void Setpgid(pid_t pid, pid_t pgid) { |
int rc; |
if ((rc = setpgid(pid, pgid)) < 0) |
unix_error("Setpgid error"); |
return; |
} |
pid_t Getpgrp(void) { |
return getpgrp(); |
} |
/************************************ |
* Wrappers for Unix signal functions |
***********************************/ |
/* $begin sigaction */ |
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); |
} |
/* $end sigaction */ |
void Sigprocmask(int how, const sigset_t *set, sigset_t *oldset) |
{ |
if (sigprocmask(how, set, oldset) < 0) |
unix_error("Sigprocmask error"); |
return; |
} |
void Sigemptyset(sigset_t *set) |
{ |
if (sigemptyset(set) < 0) |
unix_error("Sigemptyset error"); |
return; |
} |
void Sigfillset(sigset_t *set) |
{ |
if (sigfillset(set) < 0) |
unix_error("Sigfillset error"); |
return; |
} |
void Sigaddset(sigset_t *set, int signum) |
{ |
if (sigaddset(set, signum) < 0) |
unix_error("Sigaddset error"); |
return; |
} |
void Sigdelset(sigset_t *set, int signum) |
{ |
if (sigdelset(set, signum) < 0) |
unix_error("Sigdelset error"); |
return; |
} |
int Sigismember(const sigset_t *set, int signum) |
{ |
int rc; |
if ((rc = sigismember(set, signum)) < 0) |
unix_error("Sigismember error"); |
return rc; |
} |
/******************************** |
* Wrappers for Unix I/O routines |
********************************/ |
int Open(const char *pathname, int flags, mode_t mode) |
{ |
int rc; |
if ((rc = open(pathname, flags, mode)) < 0) |
unix_error("Open error"); |
return rc; |
} |
ssize_t Read(int fd, void *buf, size_t count) |
{ |
ssize_t rc; |
if ((rc = read(fd, buf, count)) < 0) |
unix_error("Read error"); |
return rc; |
} |
ssize_t Write(int fd, const void *buf, size_t count) |
{ |
ssize_t rc; |
if ((rc = write(fd, buf, count)) < 0) |
unix_error("Write error"); |
return rc; |
} |
off_t Lseek(int fildes, off_t offset, int whence) |
{ |
off_t rc; |
if ((rc = lseek(fildes, offset, whence)) < 0) |
unix_error("Lseek error"); |
return rc; |
} |
void Close(int fd) |
{ |
int rc; |
if ((rc = close(fd)) < 0) |
unix_error("Close error"); |
} |
int Select(int n, fd_set *readfds, fd_set *writefds, |
fd_set *exceptfds, struct timeval *timeout) |
{ |
int rc; |
if ((rc = select(n, readfds, writefds, exceptfds, timeout)) < 0) |
unix_error("Select error"); |
return rc; |
} |
int Dup2(int fd1, int fd2) |
{ |
int rc; |
if ((rc = dup2(fd1, fd2)) < 0) |
unix_error("Dup2 error"); |
return rc; |
} |
void Stat(const char *filename, struct stat *buf) |
{ |
if (stat(filename, buf) < 0) |
unix_error("Stat error"); |
} |
void Fstat(int fd, struct stat *buf) |
{ |
if (fstat(fd, buf) < 0) |
unix_error("Fstat error"); |
} |
/*************************************** |
* Wrappers for memory mapping functions |
***************************************/ |
void *Mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset) |
{ |
void *ptr; |
if ((ptr = mmap(addr, len, prot, flags, fd, offset)) == ((void *) -1)) |
unix_error("mmap error"); |
return(ptr); |
} |
void Munmap(void *start, size_t length) |
{ |
if (munmap(start, length) < 0) |
unix_error("munmap error"); |
} |
/*************************************************** |
* Wrappers for dynamic storage allocation functions |
***************************************************/ |
void *Malloc(size_t size) |
{ |
void *p; |
if ((p = malloc(size)) == NULL) |
unix_error("Malloc error"); |
return p; |
} |
void *Realloc(void *ptr, size_t size) |
{ |
void *p; |
if ((p = realloc(ptr, size)) == NULL) |
unix_error("Realloc error"); |
return p; |
} |
void *Calloc(size_t nmemb, size_t size) |
{ |
void *p; |
if ((p = calloc(nmemb, size)) == NULL) |
unix_error("Calloc error"); |
return p; |
} |
void Free(void *ptr) |
{ |
free(ptr); |
} |
/****************************************** |
* Wrappers for the Standard I/O functions. |
******************************************/ |
void Fclose(FILE *fp) |
{ |
if (fclose(fp) != 0) |
unix_error("Fclose error"); |
} |
FILE *Fdopen(int fd, const char *type) |
{ |
FILE *fp; |
if ((fp = fdopen(fd, type)) == NULL) |
unix_error("Fdopen error"); |
return fp; |
} |
char *Fgets(char *ptr, int n, FILE *stream) |
{ |
char *rptr; |
if (((rptr = fgets(ptr, n, stream)) == NULL) && ferror(stream)) |
app_error("Fgets error"); |
return rptr; |
} |
FILE *Fopen(const char *filename, const char *mode) |
{ |
FILE *fp; |
if ((fp = fopen(filename, mode)) == NULL) |
unix_error("Fopen error"); |
return fp; |
} |
void Fputs(const char *ptr, FILE *stream) |
{ |
if (fputs(ptr, stream) == EOF) |
unix_error("Fputs error"); |
} |
size_t Fread(void *ptr, size_t size, size_t nmemb, FILE *stream) |
{ |
size_t n; |
if (((n = fread(ptr, size, nmemb, stream)) < nmemb) && ferror(stream)) |
unix_error("Fread error"); |
return n; |
} |
void Fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream) |
{ |
if (fwrite(ptr, size, nmemb, stream) < nmemb) |
unix_error("Fwrite error"); |
} |
/**************************** |
* Sockets interface wrappers |
****************************/ |
int Socket(int domain, int type, int protocol) |
{ |
int rc; |
if ((rc = socket(domain, type, protocol)) < 0) |
unix_error("Socket error"); |
return rc; |
} |
void Setsockopt(int s, int level, int optname, const void *optval, int optlen) |
{ |
int rc; |
if ((rc = setsockopt(s, level, optname, optval, optlen)) < 0) |
unix_error("Setsockopt error"); |
} |
void Bind(int sockfd, struct sockaddr *my_addr, int addrlen) |
{ |
int rc; |
if ((rc = bind(sockfd, my_addr, addrlen)) < 0) |
unix_error("Bind error"); |
} |
void Listen(int s, int backlog) |
{ |
int rc; |
if ((rc = listen(s, backlog)) < 0) |
unix_error("Listen error"); |
} |
int Accept(int s, struct sockaddr *addr, socklen_t *addrlen) |
{ |
int rc; |
if ((rc = accept(s, addr, addrlen)) < 0) |
unix_error("Accept error"); |
return rc; |
} |
void Connect(int sockfd, struct sockaddr *serv_addr, int addrlen) |
{ |
int rc; |
if ((rc = connect(sockfd, serv_addr, addrlen)) < 0) |
unix_error("Connect error"); |
} |
/************************ |
* DNS interface wrappers |
***********************/ |
/* $begin gethostbyname */ |
struct hostent *Gethostbyname(const char *name) |
{ |
struct hostent *p; |
if ((p = gethostbyname(name)) == NULL) |
dns_error("Gethostbyname error"); |
return p; |
} |
/* $end gethostbyname */ |
struct hostent *Gethostbyaddr(const char *addr, int len, int type) |
{ |
struct hostent *p; |
if ((p = gethostbyaddr(addr, len, type)) == NULL) |
dns_error("Gethostbyaddr error"); |
return p; |
} |
/************************************************ |
* Wrappers for Pthreads thread control functions |
************************************************/ |
void Pthread_create(pthread_t *tidp, pthread_attr_t *attrp, |
void * (*routine)(void *), void *argp) |
{ |
int rc; |
if ((rc = pthread_create(tidp, attrp, routine, argp)) != 0) |
posix_error(rc, "Pthread_create error"); |
} |
void Pthread_cancel(pthread_t tid) { |
int rc; |
if ((rc = pthread_cancel(tid)) != 0) |
posix_error(rc, "Pthread_cancel error"); |
} |
void Pthread_join(pthread_t tid, void **thread_return) { |
int rc; |
if ((rc = pthread_join(tid, thread_return)) != 0) |
posix_error(rc, "Pthread_join error"); |
} |
/* $begin detach */ |
void Pthread_detach(pthread_t tid) { |
int rc; |
if ((rc = pthread_detach(tid)) != 0) |
posix_error(rc, "Pthread_detach error"); |
} |
/* $end detach */ |
void Pthread_exit(void *retval) { |
pthread_exit(retval); |
} |
pthread_t Pthread_self(void) { |
return pthread_self(); |
} |
void Pthread_once(pthread_once_t *once_control, void (*init_function)()) { |
pthread_once(once_control, init_function); |
} |
/******************************* |
* Wrappers for Posix semaphores |
*******************************/ |
void Sem_init(sem_t *sem, int pshared, unsigned int value) |
{ |
if (sem_init(sem, pshared, value) < 0) |
unix_error("Sem_init error"); |
} |
void P(sem_t *sem) |
{ |
if (sem_wait(sem) < 0) |
unix_error("P error"); |
} |
void V(sem_t *sem) |
{ |
if (sem_post(sem) < 0) |
unix_error("V error"); |
} |
/********************************************************************* |
* The Rio package - robust I/O functions |
**********************************************************************/ |
/* |
* rio_readn - robustly read n bytes (unbuffered) |
*/ |
/* $begin rio_readn */ |
ssize_t rio_readn(int fd, void *usrbuf, size_t n) |
{ |
size_t nleft = n; |
ssize_t nread; |
char *bufp = usrbuf; |
while (nleft > 0) { |
if ((nread = read(fd, bufp, nleft)) < 0) { |
if (errno == EINTR) /* interrupted by sig handler return */ |
nread = 0; /* and call read() again */ |
else |
return -1; /* errno set by read() */ |
} |
else if (nread == 0) |
break; /* EOF */ |
nleft -= nread; |
bufp += nread; |
} |
return (n - nleft); /* return >= 0 */ |
} |
/* $end rio_readn */ |
/* |
* rio_writen - robustly write n bytes (unbuffered) |
*/ |
/* $begin rio_writen */ |
ssize_t rio_writen(int fd, void *usrbuf, size_t n) |
{ |
size_t nleft = n; |
ssize_t nwritten; |
char *bufp = usrbuf; |
while (nleft > 0) { |
if ((nwritten = write(fd, bufp, nleft)) <= 0) { |
if (errno == EINTR) /* interrupted by sig handler return */ |
nwritten = 0; /* and call write() again */ |
else |
return -1; /* errno set by write() */ |
} |
nleft -= nwritten; |
bufp += nwritten; |
} |
return n; |
} |
/* $end rio_writen */ |
/* |
* rio_read - This is a wrapper for the Unix read() function that |
* transfers min(n, rio_cnt) bytes from an internal buffer to a user |
* buffer, where n is the number of bytes requested by the user and |
* rio_cnt is the number of unread bytes in the internal buffer. On |
* entry, rio_read() refills the internal buffer via a call to |
* read() if the internal buffer is empty. |
*/ |
/* $begin rio_read */ |
static ssize_t rio_read(rio_t *rp, char *usrbuf, size_t n) |
{ |
int cnt; |
while (rp->rio_cnt <= 0) { /* refill if buf is empty */ |
rp->rio_cnt = read(rp->rio_fd, rp->rio_buf, |
sizeof(rp->rio_buf)); |
if (rp->rio_cnt < 0) { |
if (errno != EINTR) /* interrupted by sig handler return */ |
return -1; |
} |
else if (rp->rio_cnt == 0) /* EOF */ |
return 0; |
else |
rp->rio_bufptr = rp->rio_buf; /* reset buffer ptr */ |
} |
/* Copy min(n, rp->rio_cnt) bytes from internal buf to user buf */ |
cnt = n; |
if (rp->rio_cnt < n) |
cnt = rp->rio_cnt; |
memcpy(usrbuf, rp->rio_bufptr, cnt); |
rp->rio_bufptr += cnt; |
rp->rio_cnt -= cnt; |
return cnt; |
} |
/* $end rio_read */ |
/* |
* rio_readinitb - Associate a descriptor with a read buffer and reset buffer |
*/ |
/* $begin rio_readinitb */ |
void rio_readinitb(rio_t *rp, int fd) |
{ |
rp->rio_fd = fd; |
rp->rio_cnt = 0; |
rp->rio_bufptr = rp->rio_buf; |
} |
/* $end rio_readinitb */ |
/* |
* rio_readnb - Robustly read n bytes (buffered) |
*/ |
/* $begin rio_readnb */ |
ssize_t rio_readnb(rio_t *rp, void *usrbuf, size_t n) |
{ |
size_t nleft = n; |
ssize_t nread; |
char *bufp = usrbuf; |
while (nleft > 0) { |
if ((nread = rio_read(rp, bufp, nleft)) < 0) { |
if (errno == EINTR) /* interrupted by sig handler return */ |
nread = 0; /* call read() again */ |
else |
return -1; /* errno set by read() */ |
} |
else if (nread == 0) |
break; /* EOF */ |
nleft -= nread; |
bufp += nread; |
} |
return (n - nleft); /* return >= 0 */ |
} |
/* $end rio_readnb */ |
/* |
* rio_readlineb - robustly read a text line (buffered) |
*/ |
/* $begin rio_readlineb */ |
ssize_t rio_readlineb(rio_t *rp, void *usrbuf, size_t maxlen) |
{ |
int n, rc; |
char c, *bufp = usrbuf; |
for (n = 1; n < maxlen; n++) { |
if ((rc = rio_read(rp, &c, 1)) == 1) { |
*bufp++ = c; |
if (c == '\n') |
break; |
} else if (rc == 0) { |
if (n == 1) |
return 0; /* EOF, no data read */ |
else |
break; /* EOF, some data was read */ |
} else |
return -1; /* error */ |
} |
*bufp = 0; |
return n; |
} |
/* $end rio_readlineb */ |
/********************************** |
* Wrappers for robust I/O routines |
**********************************/ |
ssize_t Rio_readn(int fd, void *ptr, size_t nbytes) |
{ |
ssize_t n; |
if ((n = rio_readn(fd, ptr, nbytes)) < 0) { |
unix_error("Rio_readn error"); |
return -1; |
} |
return n; |
} |
void Rio_writen(int fd, void *usrbuf, size_t n) |
{ |
if (rio_writen(fd, usrbuf, n) != n) |
unix_error("Rio_writen error"); |
} |
void Rio_readinitb(rio_t *rp, int fd) |
{ |
rio_readinitb(rp, fd); |
} |
ssize_t Rio_readnb(rio_t *rp, void *usrbuf, size_t n) |
{ |
ssize_t rc; |
if ((rc = rio_readnb(rp, usrbuf, n)) < 0) { |
unix_error("Rio_readnb error"); |
return -1; |
} |
return rc; |
} |
ssize_t Rio_readlineb(rio_t *rp, void *usrbuf, size_t maxlen) |
{ |
ssize_t rc; |
if ((rc = rio_readlineb(rp, usrbuf, maxlen)) < 0) { |
unix_error("Rio_readlineb error"); |
return -1; |
} |
return rc; |
} |
/******************************** |
* Client/server helper functions |
********************************/ |
/* |
* open_clientfd - open connection to server at <hostname, port> |
* and return a socket descriptor ready for reading and writing. |
* Returns -1 and sets errno on Unix error. |
* Returns -2 and sets h_errno on DNS (gethostbyname) error. |
*/ |
/* $begin open_clientfd */ |
int open_clientfd(char *hostname, int port) |
{ |
int clientfd; |
struct hostent *hp; |
struct sockaddr_in serveraddr; |
if ((clientfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) |
return -1; /* check errno for cause of error */ |
/* Fill in the server's IP address and port */ |
if ((hp = gethostbyname(hostname)) == NULL) |
return -2; /* check h_errno for cause of error */ |
bzero((char *) &serveraddr, sizeof(serveraddr)); |
serveraddr.sin_family = AF_INET; |
bcopy((char *)hp->h_addr_list[0], |
(char *)&serveraddr.sin_addr.s_addr, hp->h_length); |
serveraddr.sin_port = htons(port); |
/* Establish a connection with the server */ |
if (connect(clientfd, (SA *) &serveraddr, sizeof(serveraddr)) < 0) |
return -1; |
return clientfd; |
} |
/* $end open_clientfd */ |
/* |
* open_listenfd - open and return a listening socket on port |
* Returns -1 and sets errno on Unix error. |
*/ |
/* $begin open_listenfd */ |
int open_listenfd(int port) |
{ |
int listenfd, optval=1; |
struct sockaddr_in serveraddr; |
/* Create a socket descriptor */ |
if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) |
return -1; |
/* Eliminates "Address already in use" error from bind. */ |
if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, |
(const void *)&optval , sizeof(int)) < 0) |
return -1; |
/* Listenfd will be an endpoint for all requests to port |
on any IP address for this host */ |
bzero((char *) &serveraddr, sizeof(serveraddr)); |
serveraddr.sin_family = AF_INET; |
serveraddr.sin_addr.s_addr = htonl(INADDR_ANY); |
serveraddr.sin_port = htons((unsigned short)port); |
if (bind(listenfd, (SA *)&serveraddr, sizeof(serveraddr)) < 0) |
return -1; |
/* Make it a listening socket ready to accept connection requests */ |
if (listen(listenfd, LISTENQ) < 0) |
return -1; |
return listenfd; |
} |
/* $end open_listenfd */ |
/****************************************** |
* Wrappers for the client/server helper routines |
******************************************/ |
int Open_clientfd(char *hostname, int port) |
{ |
int rc; |
if ((rc = open_clientfd(hostname, port)) < 0) { |
if (rc == -1) { |
unix_error("Open_clientfd Unix error"); |
return -1; |
} else { |
dns_error("Open_clientfd DNS error"); |
return -1; |
} |
} |
return rc; |
} |
int Open_listenfd(int port) |
{ |
int rc; |
if ((rc = open_listenfd(port)) < 0) { |
unix_error("Open_listenfd error"); |
return -1; |
} |
return rc; |
} |
/* $end csapp.c */ |
/Classwork/CS3214 - Computer Systems/Project 5 - Web Service/csapp.h |
---|
0,0 → 1,168 |
/* $begin csapp.h */ |
#ifndef __CSAPP_H__ |
#define __CSAPP_H__ |
#include <stdio.h> |
#include <stdlib.h> |
#include <unistd.h> |
#include <string.h> |
#include <ctype.h> |
#include <setjmp.h> |
#include <signal.h> |
#include <sys/time.h> |
#include <sys/types.h> |
#include <sys/wait.h> |
#include <sys/stat.h> |
#include <fcntl.h> |
#include <sys/mman.h> |
#include <errno.h> |
#include <math.h> |
#include <pthread.h> |
#include <semaphore.h> |
#include <sys/socket.h> |
#include <netdb.h> |
#include <netinet/in.h> |
#include <arpa/inet.h> |
/* Default file permissions are DEF_MODE & ~DEF_UMASK */ |
/* $begin createmasks */ |
#define DEF_MODE S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH |
#define DEF_UMASK S_IWGRP|S_IWOTH |
/* $end createmasks */ |
/* Simplifies calls to bind(), connect(), and accept() */ |
/* $begin sockaddrdef */ |
typedef struct sockaddr SA; |
/* $end sockaddrdef */ |
/* Persistent state for the robust I/O (Rio) package */ |
/* $begin rio_t */ |
#define RIO_BUFSIZE 8192 |
typedef struct { |
int rio_fd; /* descriptor for this internal buf */ |
int rio_cnt; /* unread bytes in internal buf */ |
char *rio_bufptr; /* next unread byte in internal buf */ |
char rio_buf[RIO_BUFSIZE]; /* internal buffer */ |
} rio_t; |
/* $end rio_t */ |
/* External variables */ |
extern int h_errno; /* defined by BIND for DNS errors */ |
extern char **environ; /* defined by libc */ |
/* Misc constants */ |
#define MAXLINE 8192 /* max text line length */ |
#define MAXBUF 8192 /* max I/O buffer size */ |
#define LISTENQ 1024 /* second argument to listen() */ |
/* Our own error-handling functions */ |
void unix_error(char *msg); |
void posix_error(int code, char *msg); |
void dns_error(char *msg); |
void app_error(char *msg); |
/* Process control wrappers */ |
pid_t Fork(void); |
void Execve(const char *filename, char *const argv[], char *const envp[]); |
pid_t Wait(int *status); |
pid_t Waitpid(pid_t pid, int *iptr, int options); |
void Kill(pid_t pid, int signum); |
unsigned int Sleep(unsigned int secs); |
void Pause(void); |
unsigned int Alarm(unsigned int seconds); |
void Setpgid(pid_t pid, pid_t pgid); |
pid_t Getpgrp(); |
/* Signal wrappers */ |
typedef void handler_t(int); |
handler_t *Signal(int signum, handler_t *handler); |
void Sigprocmask(int how, const sigset_t *set, sigset_t *oldset); |
void Sigemptyset(sigset_t *set); |
void Sigfillset(sigset_t *set); |
void Sigaddset(sigset_t *set, int signum); |
void Sigdelset(sigset_t *set, int signum); |
int Sigismember(const sigset_t *set, int signum); |
/* Unix I/O wrappers */ |
int Open(const char *pathname, int flags, mode_t mode); |
ssize_t Read(int fd, void *buf, size_t count); |
ssize_t Write(int fd, const void *buf, size_t count); |
off_t Lseek(int fildes, off_t offset, int whence); |
void Close(int fd); |
int Select(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, |
struct timeval *timeout); |
int Dup2(int fd1, int fd2); |
void Stat(const char *filename, struct stat *buf); |
void Fstat(int fd, struct stat *buf) ; |
/* Memory mapping wrappers */ |
void *Mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset); |
void Munmap(void *start, size_t length); |
/* Standard I/O wrappers */ |
void Fclose(FILE *fp); |
FILE *Fdopen(int fd, const char *type); |
char *Fgets(char *ptr, int n, FILE *stream); |
FILE *Fopen(const char *filename, const char *mode); |
void Fputs(const char *ptr, FILE *stream); |
size_t Fread(void *ptr, size_t size, size_t nmemb, FILE *stream); |
void Fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream); |
/* Dynamic storage allocation wrappers */ |
void *Malloc(size_t size); |
void *Realloc(void *ptr, size_t size); |
void *Calloc(size_t nmemb, size_t size); |
void Free(void *ptr); |
/* Sockets interface wrappers */ |
int Socket(int domain, int type, int protocol); |
void Setsockopt(int s, int level, int optname, const void *optval, int optlen); |
void Bind(int sockfd, struct sockaddr *my_addr, int addrlen); |
void Listen(int s, int backlog); |
int Accept(int s, struct sockaddr *addr, socklen_t *addrlen); |
void Connect(int sockfd, struct sockaddr *serv_addr, int addrlen); |
/* DNS wrappers */ |
struct hostent *Gethostbyname(const char *name); |
struct hostent *Gethostbyaddr(const char *addr, int len, int type); |
/* Pthreads thread control wrappers */ |
void Pthread_create(pthread_t *tidp, pthread_attr_t *attrp, |
void * (*routine)(void *), void *argp); |
void Pthread_join(pthread_t tid, void **thread_return); |
void Pthread_cancel(pthread_t tid); |
void Pthread_detach(pthread_t tid); |
void Pthread_exit(void *retval); |
pthread_t Pthread_self(void); |
void Pthread_once(pthread_once_t *once_control, void (*init_function)()); |
/* POSIX semaphore wrappers */ |
void Sem_init(sem_t *sem, int pshared, unsigned int value); |
void P(sem_t *sem); |
void V(sem_t *sem); |
/* Rio (Robust I/O) package */ |
ssize_t rio_readn(int fd, void *usrbuf, size_t n); |
ssize_t rio_writen(int fd, void *usrbuf, size_t n); |
void rio_readinitb(rio_t *rp, int fd); |
ssize_t rio_readnb(rio_t *rp, void *usrbuf, size_t n); |
ssize_t rio_readlineb(rio_t *rp, void *usrbuf, size_t maxlen); |
/* Wrappers for Rio package */ |
ssize_t Rio_readn(int fd, void *usrbuf, size_t n); |
void Rio_writen(int fd, void *usrbuf, size_t n); |
void Rio_readinitb(rio_t *rp, int fd); |
ssize_t Rio_readnb(rio_t *rp, void *usrbuf, size_t n); |
ssize_t Rio_readlineb(rio_t *rp, void *usrbuf, size_t maxlen); |
/* Client/server helper functions */ |
int open_clientfd(char *hostname, int portno); |
int open_listenfd(int portno); |
/* Wrappers for client/server helper functions */ |
int Open_clientfd(char *hostname, int port); |
int Open_listenfd(int port); |
#endif /* __CSAPP_H__ */ |
/* $end csapp.h */ |
/Classwork/CS3214 - Computer Systems/Project 5 - Web Service/id.txt |
---|
0,0 → 1,0 |
klee482 |
/Classwork/CS3214 - Computer Systems/Project 5 - Web Service/list.c |
---|
0,0 → 1,532 |
#include "list.h" |
#include <assert.h> |
/* Our doubly linked lists have two header elements: the "head" |
just before the first element and the "tail" just after the |
last element. The `prev' link of the front header is null, as |
is the `next' link of the back header. Their other two links |
point toward each other via the interior elements of the list. |
An empty list looks like this: |
+------+ +------+ |
<---| head |<--->| tail |---> |
+------+ +------+ |
A list with two elements in it looks like this: |
+------+ +-------+ +-------+ +------+ |
<---| head |<--->| 1 |<--->| 2 |<--->| tail |<---> |
+------+ +-------+ +-------+ +------+ |
The symmetry of this arrangement eliminates lots of special |
cases in list processing. For example, take a look at |
list_remove(): it takes only two pointer assignments and no |
conditionals. That's a lot simpler than the code would be |
without header elements. |
(Because only one of the pointers in each header element is used, |
we could in fact combine them into a single header element |
without sacrificing this simplicity. But using two separate |
elements allows us to do a little bit of checking on some |
operations, which can be valuable.) */ |
static bool is_sorted (struct list_elem *a, struct list_elem *b, |
list_less_func *less, void *aux); |
/* Returns true if ELEM is a head, false otherwise. */ |
static inline bool |
is_head (struct list_elem *elem) |
{ |
return elem != NULL && elem->prev == NULL && elem->next != NULL; |
} |
/* Returns true if ELEM is an interior element, |
false otherwise. */ |
static inline bool |
is_interior (struct list_elem *elem) |
{ |
return elem != NULL && elem->prev != NULL && elem->next != NULL; |
} |
/* Returns true if ELEM is a tail, false otherwise. */ |
static inline bool |
is_tail (struct list_elem *elem) |
{ |
return elem != NULL && elem->prev != NULL && elem->next == NULL; |
} |
/* Initializes LIST as an empty list. */ |
void |
list_init (struct list *list) |
{ |
assert (list != NULL); |
list->head.prev = NULL; |
list->head.next = &list->tail; |
list->tail.prev = &list->head; |
list->tail.next = NULL; |
} |
/* Returns the beginning of LIST. */ |
struct list_elem * |
list_begin (struct list *list) |
{ |
assert (list != NULL); |
return list->head.next; |
} |
/* Returns the element after ELEM in its list. If ELEM is the |
last element in its list, returns the list tail. Results are |
undefined if ELEM is itself a list tail. */ |
struct list_elem * |
list_next (struct list_elem *elem) |
{ |
assert (is_head (elem) || is_interior (elem)); |
return elem->next; |
} |
/* Returns LIST's tail. |
list_end() is often used in iterating through a list from |
front to back. See the big comment at the top of list.h for |
an example. */ |
struct list_elem * |
list_end (struct list *list) |
{ |
assert (list != NULL); |
return &list->tail; |
} |
/* Returns the LIST's reverse beginning, for iterating through |
LIST in reverse order, from back to front. */ |
struct list_elem * |
list_rbegin (struct list *list) |
{ |
assert (list != NULL); |
return list->tail.prev; |
} |
/* Returns the element before ELEM in its list. If ELEM is the |
first element in its list, returns the list head. Results are |
undefined if ELEM is itself a list head. */ |
struct list_elem * |
list_prev (struct list_elem *elem) |
{ |
assert (is_interior (elem) || is_tail (elem)); |
return elem->prev; |
} |
/* Returns LIST's head. |
list_rend() is often used in iterating through a list in |
reverse order, from back to front. Here's typical usage, |
following the example from the top of list.h: |
for (e = list_rbegin (&foo_list); e != list_rend (&foo_list); |
e = list_prev (e)) |
{ |
struct foo *f = list_entry (e, struct foo, elem); |
...do something with f... |
} |
*/ |
struct list_elem * |
list_rend (struct list *list) |
{ |
assert (list != NULL); |
return &list->head; |
} |
/* Return's LIST's head. |
list_head() can be used for an alternate style of iterating |
through a list, e.g.: |
e = list_head (&list); |
while ((e = list_next (e)) != list_end (&list)) |
{ |
... |
} |
*/ |
struct list_elem * |
list_head (struct list *list) |
{ |
assert (list != NULL); |
return &list->head; |
} |
/* Return's LIST's tail. */ |
struct list_elem * |
list_tail (struct list *list) |
{ |
assert (list != NULL); |
return &list->tail; |
} |
/* Inserts ELEM just before BEFORE, which may be either an |
interior element or a tail. The latter case is equivalent to |
list_push_back(). */ |
void |
list_insert (struct list_elem *before, struct list_elem *elem) |
{ |
assert (is_interior (before) || is_tail (before)); |
assert (elem != NULL); |
elem->prev = before->prev; |
elem->next = before; |
before->prev->next = elem; |
before->prev = elem; |
} |
/* Removes elements FIRST though LAST (exclusive) from their |
current list, then inserts them just before BEFORE, which may |
be either an interior element or a tail. */ |
void |
list_splice (struct list_elem *before, |
struct list_elem *first, struct list_elem *last) |
{ |
assert (is_interior (before) || is_tail (before)); |
if (first == last) |
return; |
last = list_prev (last); |
assert (is_interior (first)); |
assert (is_interior (last)); |
/* Cleanly remove FIRST...LAST from its current list. */ |
first->prev->next = last->next; |
last->next->prev = first->prev; |
/* Splice FIRST...LAST into new list. */ |
first->prev = before->prev; |
last->next = before; |
before->prev->next = first; |
before->prev = last; |
} |
/* Inserts ELEM at the beginning of LIST, so that it becomes the |
front in LIST. */ |
void |
list_push_front (struct list *list, struct list_elem *elem) |
{ |
list_insert (list_begin (list), elem); |
} |
/* Inserts ELEM at the end of LIST, so that it becomes the |
back in LIST. */ |
void |
list_push_back (struct list *list, struct list_elem *elem) |
{ |
list_insert (list_end (list), elem); |
} |
/* Removes ELEM from its list and returns the element that |
followed it. Undefined behavior if ELEM is not in a list. |
It's not safe to treat ELEM as an element in a list after |
removing it. In particular, using list_next() or list_prev() |
on ELEM after removal yields undefined behavior. This means |
that a naive loop to remove the elements in a list will fail: |
** DON'T DO THIS ** |
for (e = list_begin (&list); e != list_end (&list); e = list_next (e)) |
{ |
...do something with e... |
list_remove (e); |
} |
** DON'T DO THIS ** |
Here is one correct way to iterate and remove elements from a |
list: |
for (e = list_begin (&list); e != list_end (&list); e = list_remove (e)) |
{ |
...do something with e... |
} |
If you need to free() elements of the list then you need to be |
more conservative. Here's an alternate strategy that works |
even in that case: |
while (!list_empty (&list)) |
{ |
struct list_elem *e = list_pop_front (&list); |
...do something with e... |
} |
*/ |
struct list_elem * |
list_remove (struct list_elem *elem) |
{ |
assert (is_interior (elem)); |
elem->prev->next = elem->next; |
elem->next->prev = elem->prev; |
return elem->next; |
} |
/* Removes the front element from LIST and returns it. |
Undefined behavior if LIST is empty before removal. */ |
struct list_elem * |
list_pop_front (struct list *list) |
{ |
struct list_elem *front = list_front (list); |
list_remove (front); |
return front; |
} |
/* Removes the back element from LIST and returns it. |
Undefined behavior if LIST is empty before removal. */ |
struct list_elem * |
list_pop_back (struct list *list) |
{ |
struct list_elem *back = list_back (list); |
list_remove (back); |
return back; |
} |
/* Returns the front element in LIST. |
Undefined behavior if LIST is empty. */ |
struct list_elem * |
list_front (struct list *list) |
{ |
assert (!list_empty (list)); |
return list->head.next; |
} |
/* Returns the back element in LIST. |
Undefined behavior if LIST is empty. */ |
struct list_elem * |
list_back (struct list *list) |
{ |
assert (!list_empty (list)); |
return list->tail.prev; |
} |
/* Returns the number of elements in LIST. |
Runs in O(n) in the number of elements. */ |
size_t |
list_size (struct list *list) |
{ |
struct list_elem *e; |
size_t cnt = 0; |
for (e = list_begin (list); e != list_end (list); e = list_next (e)) |
cnt++; |
return cnt; |
} |
/* Returns true if LIST is empty, false otherwise. */ |
bool |
list_empty (struct list *list) |
{ |
return list_begin (list) == list_end (list); |
} |
/* Swaps the `struct list_elem *'s that A and B point to. */ |
static void |
swap (struct list_elem **a, struct list_elem **b) |
{ |
struct list_elem *t = *a; |
*a = *b; |
*b = t; |
} |
/* Reverses the order of LIST. */ |
void |
list_reverse (struct list *list) |
{ |
if (!list_empty (list)) |
{ |
struct list_elem *e; |
for (e = list_begin (list); e != list_end (list); e = e->prev) |
swap (&e->prev, &e->next); |
swap (&list->head.next, &list->tail.prev); |
swap (&list->head.next->prev, &list->tail.prev->next); |
} |
} |
/* Returns true only if the list elements A through B (exclusive) |
are in order according to LESS given auxiliary data AUX. */ |
static bool |
is_sorted (struct list_elem *a, struct list_elem *b, |
list_less_func *less, void *aux) |
{ |
if (a != b) |
while ((a = list_next (a)) != b) |
if (less (a, list_prev (a), aux)) |
return false; |
return true; |
} |
/* Finds a run, starting at A and ending not after B, of list |
elements that are in nondecreasing order according to LESS |
given auxiliary data AUX. Returns the (exclusive) end of the |
run. |
A through B (exclusive) must form a non-empty range. */ |
static struct list_elem * |
find_end_of_run (struct list_elem *a, struct list_elem *b, |
list_less_func *less, void *aux) |
{ |
assert (a != NULL); |
assert (b != NULL); |
assert (less != NULL); |
assert (a != b); |
do |
{ |
a = list_next (a); |
} |
while (a != b && !less (a, list_prev (a), aux)); |
return a; |
} |
/* Merges A0 through A1B0 (exclusive) with A1B0 through B1 |
(exclusive) to form a combined range also ending at B1 |
(exclusive). Both input ranges must be nonempty and sorted in |
nondecreasing order according to LESS given auxiliary data |
AUX. The output range will be sorted the same way. */ |
static void |
inplace_merge (struct list_elem *a0, struct list_elem *a1b0, |
struct list_elem *b1, |
list_less_func *less, void *aux) |
{ |
assert (a0 != NULL); |
assert (a1b0 != NULL); |
assert (b1 != NULL); |
assert (less != NULL); |
assert (is_sorted (a0, a1b0, less, aux)); |
assert (is_sorted (a1b0, b1, less, aux)); |
while (a0 != a1b0 && a1b0 != b1) |
if (!less (a1b0, a0, aux)) |
a0 = list_next (a0); |
else |
{ |
a1b0 = list_next (a1b0); |
list_splice (a0, list_prev (a1b0), a1b0); |
} |
} |
/* Sorts LIST according to LESS given auxiliary data AUX, using a |
natural iterative merge sort that runs in O(n lg n) time and |
O(1) space in the number of elements in LIST. */ |
void |
list_sort (struct list *list, list_less_func *less, void *aux) |
{ |
size_t output_run_cnt; /* Number of runs output in current pass. */ |
assert (list != NULL); |
assert (less != NULL); |
/* Pass over the list repeatedly, merging adjacent runs of |
nondecreasing elements, until only one run is left. */ |
do |
{ |
struct list_elem *a0; /* Start of first run. */ |
struct list_elem *a1b0; /* End of first run, start of second. */ |
struct list_elem *b1; /* End of second run. */ |
output_run_cnt = 0; |
for (a0 = list_begin (list); a0 != list_end (list); a0 = b1) |
{ |
/* Each iteration produces one output run. */ |
output_run_cnt++; |
/* Locate two adjacent runs of nondecreasing elements |
A0...A1B0 and A1B0...B1. */ |
a1b0 = find_end_of_run (a0, list_end (list), less, aux); |
if (a1b0 == list_end (list)) |
break; |
b1 = find_end_of_run (a1b0, list_end (list), less, aux); |
/* Merge the runs. */ |
inplace_merge (a0, a1b0, b1, less, aux); |
} |
} |
while (output_run_cnt > 1); |
assert (is_sorted (list_begin (list), list_end (list), less, aux)); |
} |
/* Inserts ELEM in the proper position in LIST, which must be |
sorted according to LESS given auxiliary data AUX. |
Runs in O(n) average case in the number of elements in LIST. */ |
void |
list_insert_ordered (struct list *list, struct list_elem *elem, |
list_less_func *less, void *aux) |
{ |
struct list_elem *e; |
assert (list != NULL); |
assert (elem != NULL); |
assert (less != NULL); |
for (e = list_begin (list); e != list_end (list); e = list_next (e)) |
if (less (elem, e, aux)) |
break; |
return list_insert (e, elem); |
} |
/* Iterates through LIST and removes all but the first in each |
set of adjacent elements that are equal according to LESS |
given auxiliary data AUX. If DUPLICATES is non-null, then the |
elements from LIST are appended to DUPLICATES. */ |
void |
list_unique (struct list *list, struct list *duplicates, |
list_less_func *less, void *aux) |
{ |
struct list_elem *elem, *next; |
assert (list != NULL); |
assert (less != NULL); |
if (list_empty (list)) |
return; |
elem = list_begin (list); |
while ((next = list_next (elem)) != list_end (list)) |
if (!less (elem, next, aux) && !less (next, elem, aux)) |
{ |
list_remove (next); |
if (duplicates != NULL) |
list_push_back (duplicates, next); |
} |
else |
elem = next; |
} |
/* Returns the element in LIST with the largest value according |
to LESS given auxiliary data AUX. If there is more than one |
maximum, returns the one that appears earlier in the list. If |
the list is empty, returns its tail. */ |
struct list_elem * |
list_max (struct list *list, list_less_func *less, void *aux) |
{ |
struct list_elem *max = list_begin (list); |
if (max != list_end (list)) |
{ |
struct list_elem *e; |
for (e = list_next (max); e != list_end (list); e = list_next (e)) |
if (less (max, e, aux)) |
max = e; |
} |
return max; |
} |
/* Returns the element in LIST with the smallest value according |
to LESS given auxiliary data AUX. If there is more than one |
minimum, returns the one that appears earlier in the list. If |
the list is empty, returns its tail. */ |
struct list_elem * |
list_min (struct list *list, list_less_func *less, void *aux) |
{ |
struct list_elem *min = list_begin (list); |
if (min != list_end (list)) |
{ |
struct list_elem *e; |
for (e = list_next (min); e != list_end (list); e = list_next (e)) |
if (less (e, min, aux)) |
min = e; |
} |
return min; |
} |
/Classwork/CS3214 - Computer Systems/Project 5 - Web Service/list.h |
---|
0,0 → 1,170 |
#ifndef __LIST_H |
#define __LIST_H |
/* This code is taken from the Pintos education OS. |
* For copyright information, see www.pintos-os.org */ |
/* Doubly linked list. |
This implementation of a doubly linked list does not require |
use of dynamically allocated memory. Instead, each structure |
that is a potential list element must embed a struct list_elem |
member. All of the list functions operate on these `struct |
list_elem's. The list_entry macro allows conversion from a |
struct list_elem back to a structure object that contains it. |
For example, suppose there is a needed for a list of `struct |
foo'. `struct foo' should contain a `struct list_elem' |
member, like so: |
struct foo |
{ |
struct list_elem elem; |
int bar; |
...other members... |
}; |
Then a list of `struct foo' can be be declared and initialized |
like so: |
struct list foo_list; |
list_init (&foo_list); |
Iteration is a typical situation where it is necessary to |
convert from a struct list_elem back to its enclosing |
structure. Here's an example using foo_list: |
struct list_elem *e; |
for (e = list_begin (&foo_list); e != list_end (&foo_list); |
e = list_next (e)) |
{ |
struct foo *f = list_entry (e, struct foo, elem); |
...do something with f... |
} |
You can find real examples of list usage throughout the |
source; for example, malloc.c, palloc.c, and thread.c in the |
threads directory all use lists. |
The interface for this list is inspired by the list<> template |
in the C++ STL. If you're familiar with list<>, you should |
find this easy to use. However, it should be emphasized that |
these lists do *no* type checking and can't do much other |
correctness checking. If you screw up, it will bite you. |
Glossary of list terms: |
- "front": The first element in a list. Undefined in an |
empty list. Returned by list_front(). |
- "back": The last element in a list. Undefined in an empty |
list. Returned by list_back(). |
- "tail": The element figuratively just after the last |
element of a list. Well defined even in an empty list. |
Returned by list_end(). Used as the end sentinel for an |
iteration from front to back. |
- "beginning": In a non-empty list, the front. In an empty |
list, the tail. Returned by list_begin(). Used as the |
starting point for an iteration from front to back. |
- "head": The element figuratively just before the first |
element of a list. Well defined even in an empty list. |
Returned by list_rend(). Used as the end sentinel for an |
iteration from back to front. |
- "reverse beginning": In a non-empty list, the back. In an |
empty list, the head. Returned by list_rbegin(). Used as |
the starting point for an iteration from back to front. |
- "interior element": An element that is not the head or |
tail, that is, a real list element. An empty list does |
not have any interior elements. |
*/ |
#include <stdbool.h> |
#include <stddef.h> |
#include <stdint.h> |
/* List element. */ |
struct list_elem |
{ |
struct list_elem *prev; /* Previous list element. */ |
struct list_elem *next; /* Next list element. */ |
}; |
/* List. */ |
struct list |
{ |
struct list_elem head; /* List head. */ |
struct list_elem tail; /* List tail. */ |
}; |
/* Converts pointer to list element LIST_ELEM into a pointer to |
the structure that LIST_ELEM is embedded inside. Supply the |
name of the outer structure STRUCT and the member name MEMBER |
of the list element. See the big comment at the top of the |
file for an example. */ |
#define list_entry(LIST_ELEM, STRUCT, MEMBER) \ |
((STRUCT *) ((uint8_t *) &(LIST_ELEM)->next \ |
- offsetof (STRUCT, MEMBER.next))) |
void list_init (struct list *); |
/* List traversal. */ |
struct list_elem *list_begin (struct list *); |
struct list_elem *list_next (struct list_elem *); |
struct list_elem *list_end (struct list *); |
struct list_elem *list_rbegin (struct list *); |
struct list_elem *list_prev (struct list_elem *); |
struct list_elem *list_rend (struct list *); |
struct list_elem *list_head (struct list *); |
struct list_elem *list_tail (struct list *); |
/* List insertion. */ |
void list_insert (struct list_elem *, struct list_elem *); |
void list_splice (struct list_elem *before, |
struct list_elem *first, struct list_elem *last); |
void list_push_front (struct list *, struct list_elem *); |
void list_push_back (struct list *, struct list_elem *); |
/* List removal. */ |
struct list_elem *list_remove (struct list_elem *); |
struct list_elem *list_pop_front (struct list *); |
struct list_elem *list_pop_back (struct list *); |
/* List elements. */ |
struct list_elem *list_front (struct list *); |
struct list_elem *list_back (struct list *); |
/* List properties. */ |
size_t list_size (struct list *); |
bool list_empty (struct list *); |
/* Miscellaneous. */ |
void list_reverse (struct list *); |
/* Compares the value of two list elements A and B, given |
auxiliary data AUX. Returns true if A is less than B, or |
false if A is greater than or equal to B. */ |
typedef bool list_less_func (const struct list_elem *a, |
const struct list_elem *b, |
void *aux); |
/* Operations on lists with ordered elements. */ |
void list_sort (struct list *, |
list_less_func *, void *aux); |
void list_insert_ordered (struct list *, struct list_elem *, |
list_less_func *, void *aux); |
void list_unique (struct list *, struct list *duplicates, |
list_less_func *, void *aux); |
/* Max and min. */ |
struct list_elem *list_max (struct list *, list_less_func *, void *aux); |
struct list_elem *list_min (struct list *, list_less_func *, void *aux); |
#endif /* list.h */ |
/Classwork/CS3214 - Computer Systems/Project 5 - Web Service/sysstatd |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/Classwork/CS3214 - Computer Systems/Project 5 - Web Service/sysstatd |
---|
Property changes: |
Added: svn:mime-type |
+application/octet-stream |
\ No newline at end of property |
/Classwork/CS3214 - Computer Systems/Project 5 - Web Service/sysstatd.c |
---|
0,0 → 1,582 |
#include "csapp.h" |
#include "threadpool_exec.h" |
#include "list.h" |
#define THREAD_POOL_SIZE 10 |
// Struct for passing data to threads in the thread pool |
struct parse_struct { |
int connfd; |
char *rootdir; |
}; |
void * runloop(void *arg); |
void allocanon(void); |
void freeanon(void); |
void parse_request(struct parse_struct *p); |
void read_requesthdrs(rio_t *rp, char *close_connection); |
int parse_uri(char *uri, char *filename, char *cgiargs, char *rootdir); |
void serve_file(int fd, char *filename, int filesize); |
void serve_json(int fd, char *data); |
void check_callback(char *data, char *cgiargs); |
void get_filetype(char *filename, char *filetype); |
void get_loadavg(char *ret_json); |
void get_meminfo(char *ret_json); |
void clienterror(int fd, char *cause, char *errnum, char *shortmsg, char *longmsg); |
int Open_listenfd_6(int port); |
static pthread_mutex_t last_activity_mutex; |
static time_t last_activity; |
static pthread_mutex_t mem_list_mutex; |
static struct list mem_list; |
struct mem_elem { |
struct list_elem elem; |
void * ptr; |
}; |
int main(int argc, char **argv) |
{ |
int listenfd, connfd; |
int port = 0, c; |
struct sockaddr_in clientaddr; |
socklen_t clientlen = sizeof(clientaddr); |
char *relayhost = NULL, *rootdir = NULL; |
char *default_dir = "."; |
// Check arguments passed into program |
while ((c = getopt(argc, argv, "p:r:R:")) != -1) { |
switch (c) { |
case 'p': |
port = atoi(optarg); |
break; |
case 'r': |
relayhost = optarg; |
break; |
case 'R': |
rootdir = optarg; |
break; |
default: |
break; |
} |
} |
if (port == 0 && relayhost == NULL) { |
fprintf(stderr, "usage: -p <port> -r <relayhost:port> -R <root directory path>\n"); |
exit(1); |
} |
// If rootdir is not specified, use current directory |
if (rootdir == NULL) { |
rootdir = default_dir; |
} |
// Create the thread pool to process client requests |
struct thread_pool * t_pool = thread_pool_new(THREAD_POOL_SIZE); |
// Initialize static variables and mutex for such variables |
list_init(&mem_list); |
pthread_mutex_init(&last_activity_mutex, NULL); |
pthread_mutex_init(&mem_list_mutex, NULL); |
// Open a listening port if it is specified |
if (port != 0) { |
listenfd = Open_listenfd_6(port); |
while (1) { |
connfd = Accept(listenfd, (SA *)&clientaddr, &clientlen); |
struct parse_struct p; |
p.connfd = connfd; |
p.rootdir = rootdir; |
thread_pool_submit(t_pool, (thread_pool_callable_func_t) parse_request, &p); |
} |
} else { // Connect to the relay server |
char *c, host[MAXLINE], buf[MAXLINE]; |
int p; |
// Parse the hostname and port |
c = strtok(relayhost, ":"); |
strcpy(host, c); |
c = strtok(NULL, ":"); |
p = atoi(c); |
while (1) { |
printf("Connecting to relay server\n"); |
// Initialize the last activity time |
pthread_mutex_lock(&last_activity_mutex); |
last_activity = time(NULL); |
pthread_mutex_unlock(&last_activity_mutex); |
// Establish TCP connection to relay server |
listenfd = Open_clientfd(host, p); |
if (listenfd == -1) { |
printf("Relay open error\n"); |
exit(1); |
} |
// Send relay identifier |
strcpy(buf, "group244\r\n"); |
Rio_writen(listenfd, buf, strlen(buf)); |
// Handle HTML requests |
struct parse_struct ps; |
ps.connfd = listenfd; |
ps.rootdir = rootdir; |
thread_pool_submit(t_pool, (thread_pool_callable_func_t) parse_request, &ps); |
// Reconnect if there has been no activity for 300 seconds |
while(1) { |
time_t cur_time = time(NULL); |
pthread_mutex_lock(&last_activity_mutex); |
if (difftime(cur_time, last_activity) > 300) { |
pthread_mutex_unlock(&last_activity_mutex); |
break; |
} else { |
pthread_mutex_unlock(&last_activity_mutex); |
sleep(1); |
} |
} |
} |
} |
} |
/* |
* parse_request - handle one HTTP request/response transaction |
*/ |
void parse_request(struct parse_struct *p) |
{ |
struct parse_struct *p_data = p; |
int fd = p_data->connfd; |
int parse_ret; |
struct stat sbuf; |
char buf[MAXLINE], method[MAXLINE], uri[MAXLINE], version[MAXLINE], data[MAXLINE]; |
char filename[MAXLINE], cgiargs[MAXLINE]; |
char close_connection = 0; |
rio_t rio; |
while (1) { |
/* Read request line and headers */ |
Rio_readinitb(&rio, fd); |
// Make sure that EOF is not returned for the socket |
if (Rio_readlineb(&rio, buf, MAXLINE) > 0) { |
// Save current time (activity) |
pthread_mutex_lock(&last_activity_mutex); |
last_activity = time(NULL); |
pthread_mutex_unlock(&last_activity_mutex); |
// Parse received data |
sscanf(buf, "%s %s %s", method, uri, version); |
if (strcasecmp(method, "GET")) { |
clienterror(fd, method, "501", "Not Implemented", "Sysstatd does not implement this method"); |
continue; |
} |
// Check the headers, we're only looking for Connection:close here |
read_requesthdrs(&rio, &close_connection); |
if (close_connection == -1) { |
Close(fd); |
break; |
} |
/* Parse URI from GET request */ |
parse_ret = parse_uri(uri, filename, cgiargs, p_data->rootdir); |
if (parse_ret == 0) { // Return file |
if (stat(filename, &sbuf) < 0) { |
clienterror(fd, filename, "404", "Not found", "Sysstatd couldn't find this file"); |
continue; |
} |
if (!(S_ISREG(sbuf.st_mode)) || !(S_IRUSR & sbuf.st_mode)) { |
clienterror(fd, filename, "403", "Forbidden", "Sysstatd couldn't read the file"); |
continue; |
} |
serve_file(fd, filename, sbuf.st_size); |
} else if (parse_ret == 1) { // Return /loadavg |
get_loadavg(data); |
check_callback(data, cgiargs); |
serve_json(fd, data); |
} else if (parse_ret == 2) { // Return /meminfo |
get_meminfo(data); |
check_callback(data, cgiargs); |
serve_json(fd, data); |
} else if (parse_ret == 3) { // Execute /runloop |
pthread_t thr; |
pthread_create(&thr, NULL, runloop, NULL); |
} else if (parse_ret == 4) { // Execute /allocanon |
printf("Allocanon\n"); |
allocanon(); |
} else if (parse_ret == 5) { // Execute /freeanon |
printf("Freeanon\n"); |
freeanon(); |
} else { |
clienterror(fd, uri, "404", "Not found", "Sysstatd does not implement this URL"); |
} |
// Close the connection if HTTP 1.0 is used or close is specified in the header |
if (!strcmp(version, "HTTP/1.0") || close_connection) { |
Close(fd); |
break; |
} |
} else { |
Close(fd); |
break; |
} |
} |
} |
/* |
* runloop - spins for 15 seconds then returns |
*/ |
void * runloop(void *arg) { |
time_t t1 = time(NULL); |
time_t t2; |
do { |
t2 = time(NULL); |
} while (difftime(t2,t1) < 15); |
return NULL; |
} |
/* |
* allocanon - allocates 64MB in anonymous virtual memory |
*/ |
void allocanon() { |
// Get the address mapping for a 64MB |
void * new_map = mmap(NULL, 67108864, PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); |
if (new_map == MAP_FAILED) { |
printf("Allocanon failed\n"); |
return; |
} else { |
printf("%p\n", new_map); |
} |
// Do something with the newly allocated memory |
memset(new_map, 0, 67108864); |
// Save address in list for later removal |
struct mem_elem *m = malloc(sizeof(struct mem_elem)); |
m->ptr = new_map; |
pthread_mutex_lock(&mem_list_mutex); |
list_push_back(&mem_list, &m->elem); |
pthread_mutex_unlock(&mem_list_mutex); |
} |
/* |
* freeanon - frees a block of anonymous virtual memory previously allocated |
*/ |
void freeanon() { |
// Remove the most recently allocated memory block |
pthread_mutex_lock(&mem_list_mutex); |
if (list_size(&mem_list)) { |
struct list_elem *e = list_pop_back(&mem_list); |
struct mem_elem *m = list_entry(e, struct mem_elem, elem); |
munmap(m->ptr, 67108864); |
free(m); |
} |
pthread_mutex_unlock(&mem_list_mutex); |
} |
/* |
* read_requesthdrs - read and parse HTTP request headers |
*/ |
void read_requesthdrs(rio_t *rp, char *close_connection) |
{ |
char buf[MAXLINE]; |
do { |
if (Rio_readlineb(rp, buf, MAXLINE) > 0) { |
// Check if the header contains a request to close connection |
if (!strcmp(buf, "Connection: close\r\n")) |
*close_connection = 1; |
// printf("%s", buf); |
} else { |
*close_connection = -1; |
return; |
} |
} while (strcmp(buf, "\r\n")); |
return; |
} |
/* |
* parse_uri - parse URI into filename and CGI args |
* return 0 if file, 1 if loadavg, 2 if meminfo, |
* 3 if runloop, 4 if allocanon, 5 if freeanon |
* return -1 if uri is not supported |
*/ |
int parse_uri(char *uri, char *filename, char *cgiargs, char *rootdir) |
{ |
char *ptr; |
// Save cgiargs if they exist |
if (!strstr(uri, "?")) { |
strcpy(cgiargs, ""); |
} else { |
ptr = index(uri, '?'); |
if (ptr) { |
strcpy(cgiargs, ptr+1); |
*ptr = '\0'; |
} |
else |
strcpy(cgiargs, ""); |
} |
// Check uri for specific commands |
if (!strncmp(uri, "/files", 6)) { |
strcpy(filename, rootdir); |
strcat(filename, uri+6); |
// Ensure that all "/../" in the filename is replaced with "////" |
char *s = strstr(filename, "/../"); |
while (s) { |
strncpy(s, "////", 4); |
s = strstr(filename, "/../"); |
} |
// printf("%s\n", filename); |
return 0; |
} else if (!strcmp(uri, "/loadavg")) { |
return 1; |
} else if (!strcmp(uri, "/meminfo")) { |
return 2; |
} else if (!strcmp(uri, "/runloop")) { |
return 3; |
} else if (!strcmp(uri, "/allocanon")) { |
return 4; |
} else if (!strcmp(uri, "/freeanon")) { |
return 5; |
} else { |
return -1; |
} |
} |
/* |
* serve_json - returns JSON data to the client |
*/ |
void serve_json(int fd, char *data) { |
char buf[MAXBUF]; |
/* Send response headers to client */ |
sprintf(buf, "HTTP/1.1 200 OK\r\n"); |
sprintf(buf, "%sServer: Sysstatd Web Server\r\n", buf); |
sprintf(buf, "%sContent-length: %d\r\n", buf, (int)strlen(data)); |
sprintf(buf, "%sContent-type: %s\r\n\r\n", buf, "application/json"); |
Rio_writen(fd, buf, strlen(buf)); |
/* Send json data to client */ |
Rio_writen(fd, data, strlen(data)); |
} |
/* |
* get_loadavg - returns /proc/loadavg in JSON format |
*/ |
void get_loadavg(char *ret_json) { |
FILE *f; |
char line[MAXLINE], load1[10], load2[10], load3[10]; |
char threads[20], total_threads[20], active_threads[20]; |
memset(active_threads, 0, 20); |
memset(total_threads, 0, 20); |
// Read data from /proc/loadavg |
f = Fopen("/proc/loadavg", "rt"); |
Fgets(line, MAXLINE, f); |
Fclose(f); |
// Format data into JSON format |
sscanf(line, "%s %s %s %s", load1, load2, load3, threads); |
int i = strcspn(threads, "/"); |
strncpy(active_threads, threads, i); |
strcpy(total_threads, threads+i+1); |
sprintf(ret_json, "{\"total_threads\": \"%s\", \"loadavg\": [\"%s\", \"%s\", \"%s\"], \"running_threads\": \"%s\"}", |
total_threads, load1, load2, load3, active_threads); |
} |
/* |
* get_meminfo - returns /proc/meminfo in JSON format |
*/ |
void get_meminfo(char *ret_json) { |
FILE *f; |
char line[MAXLINE], s1[MAXLINE], s2[MAXLINE], buf[MAXLINE]; |
strcpy(ret_json, "{"); |
// Read data and convert to JSON format |
f = Fopen("/proc/meminfo", "rt"); |
while (Fgets(line, MAXLINE, f) != NULL) { |
sscanf(line, "%s %s", s1, s2); |
strcpy(buf, "\""); |
strncat(buf, s1, strlen(s1)-1); |
strcat(buf, "\": \""); |
strcat(buf, s2); |
strcat(buf, "\", "); |
strcat(ret_json, buf); |
} |
Fclose(f); |
strcpy(ret_json+strlen(ret_json)-2, "}"); |
} |
/* |
* serve_file - copy a file back to the client |
*/ |
void serve_file(int fd, char *filename, int filesize) |
{ |
int srcfd; |
char *srcp, filetype[MAXLINE], buf[MAXBUF]; |
/* Send response headers to client */ |
get_filetype(filename, filetype); |
sprintf(buf, "HTTP/1.1 200 OK\r\n"); |
sprintf(buf, "%sServer: Sysstatd Web Server\r\n", buf); |
sprintf(buf, "%sContent-length: %d\r\n", buf, filesize); |
sprintf(buf, "%sContent-type: %s\r\n\r\n", buf, filetype); |
Rio_writen(fd, buf, strlen(buf)); |
/* Send response body to client */ |
srcfd = Open(filename, O_RDONLY, 0); |
srcp = Mmap(0, filesize, PROT_READ, MAP_PRIVATE, srcfd, 0); |
Close(srcfd); |
Rio_writen(fd, srcp, filesize); |
Munmap(srcp, filesize); |
} |
/* |
* check_callback - check to see if a callback is specified in cgiargs |
*/ |
void check_callback(char *data, char *cgiargs) |
{ |
char *p, buf[MAXLINE]; |
// If cgiargs is empty, return |
if (!strcmp(cgiargs, "")) |
return; |
p = strtok(cgiargs, "&"); |
while (p != NULL) { |
if (!strncmp(p, "callback=", 9)) { |
strcpy(buf, p+9); |
strcat(buf, "("); |
strcat(buf, data); |
strcat(buf, ")"); |
strcpy(data, buf); |
} |
p = strtok(NULL, "&"); |
} |
} |
/* |
* get_filetype - derive file type from file name |
*/ |
void get_filetype(char *filename, char *filetype) |
{ |
if (strstr(filename, ".html")) |
strcpy(filetype, "text/html"); |
else if (strstr(filename, ".gif")) |
strcpy(filetype, "image/gif"); |
else if (strstr(filename, ".jpg")) |
strcpy(filetype, "image/jpeg"); |
else if (strstr(filename, ".js")) |
strcpy(filetype, "application/javascript"); |
else if (strstr(filename, ".css")) |
strcpy(filetype, "text/css"); |
else |
strcpy(filetype, "text/plain"); |
} |
/* |
* clienterror - returns an error message to the client |
*/ |
void clienterror(int fd, char *cause, char *errnum, char *shortmsg, char *longmsg) |
{ |
char buf[MAXLINE], body[MAXBUF]; |
/* Build the HTTP response body */ |
sprintf(body, "<html><title>Sysstatd Error</title>"); |
sprintf(body, "%s<body bgcolor=""ffffff"">\r\n", body); |
sprintf(body, "%s%s: %s\r\n", body, errnum, shortmsg); |
sprintf(body, "%s<p>%s: %s\r\n", body, longmsg, cause); |
sprintf(body, "%s<hr><em>The Sysstatd Web server</em>\r\n", body); |
/* Print the HTTP response */ |
sprintf(buf, "HTTP/1.1 %s %s\r\n", errnum, shortmsg); |
Rio_writen(fd, buf, strlen(buf)); |
sprintf(buf, "Content-type: text/html\r\n"); |
Rio_writen(fd, buf, strlen(buf)); |
sprintf(buf, "Content-length: %d\r\n\r\n", (int)strlen(body)); |
Rio_writen(fd, buf, strlen(buf)); |
Rio_writen(fd, body, strlen(body)); |
} |
/* |
* Open_listenfd_6 - opens and returns a listening socket on the specified port. |
* will attempt to open an ipv6 socket over an ipv4 socket. |
*/ |
int Open_listenfd_6(int port) { |
char p[10]; |
sprintf(p, "%d", port); |
// Initialize stuffs |
struct addrinfo *ai; |
struct addrinfo hints; |
memset (&hints, '\0', sizeof (hints)); |
hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG; |
hints.ai_socktype = SOCK_STREAM; |
int e = getaddrinfo(NULL, p, &hints, &ai); |
if (e != 0) { |
fprintf(stderr, "Error at getaddrinfo()\n"); |
exit(1); |
} |
int nfds = 0; |
struct addrinfo *runp = ai; |
// Loop for ai_family == 10 (AF_INET6) for an ipv6 socket. Choose the first |
// socket in the list otherwise |
int inet_6 = -1, i; |
while (runp != NULL) { |
if (runp->ai_family == 10) |
inet_6 = nfds; |
++nfds; |
runp = runp->ai_next; |
} |
if (inet_6 > 0) { |
runp = ai; |
for (i = 0; i < inet_6; i++) { |
runp = runp->ai_next; |
} |
} else { |
runp = ai; |
} |
// Create, bind, and listen on the specified socket |
int listenfd; |
listenfd = socket(runp->ai_family, runp->ai_socktype, runp->ai_protocol); |
if (listenfd == -1) { |
fprintf(stderr, "Error at socket()\n"); |
exit(1); |
} |
int opt = 1; |
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); |
if (bind(listenfd, runp->ai_addr, runp->ai_addrlen) != 0) { |
fprintf(stderr, "Error at bind()\n"); |
close(listenfd); |
exit(1); |
} else { |
if (listen(listenfd, SOMAXCONN) != 0) { |
fprintf(stderr, "Error at listen()\n"); |
exit(1); |
} |
} |
freeaddrinfo (ai); |
return listenfd; |
} |
/Classwork/CS3214 - Computer Systems/Project 5 - Web Service/sysstatwebservice-handout.pdf |
---|
0,0 → 1,1902 |
%PDF-1.4 |
5 0 obj |
<< /S /GoTo /D (section.1) >> |
endobj |
8 0 obj |
(Introduction) |
endobj |
9 0 obj |
<< /S /GoTo /D (section.2) >> |
endobj |
12 0 obj |
(Functionality) |
endobj |
13 0 obj |
<< /S /GoTo /D (subsection.2.1) >> |
endobj |
16 0 obj |
(System Status Web Service) |
endobj |
17 0 obj |
<< /S /GoTo /D (subsection.2.2) >> |
endobj |
20 0 obj |
(Serving Files) |
endobj |
21 0 obj |
<< /S /GoTo /D (subsection.2.3) >> |
endobj |
24 0 obj |
(Synthetic Load Requests) |
endobj |
25 0 obj |
<< /S /GoTo /D (subsection.2.4) >> |
endobj |
28 0 obj |
(Multiple Client Support) |
endobj |
29 0 obj |
<< /S /GoTo /D (subsection.2.5) >> |
endobj |
32 0 obj |
(Robustness) |
endobj |
33 0 obj |
<< /S /GoTo /D (subsection.2.6) >> |
endobj |
36 0 obj |
(Protocol Independence) |
endobj |
37 0 obj |
<< /S /GoTo /D (subsection.2.7) >> |
endobj |
40 0 obj |
(Relay Server) |
endobj |
41 0 obj |
<< /S /GoTo /D (subsection.2.8) >> |
endobj |
44 0 obj |
(Widgets) |
endobj |
45 0 obj |
<< /S /GoTo /D (subsection.2.9) >> |
endobj |
48 0 obj |
(Minimum Requirements) |
endobj |
49 0 obj |
<< /S /GoTo /D (subsection.2.10) >> |
endobj |
52 0 obj |
(Choice of Port Numbers and Relay Server Prefixes) |
endobj |
53 0 obj |
<< /S /GoTo /D (section.3) >> |
endobj |
56 0 obj |
(Strategy) |
endobj |
57 0 obj |
<< /S /GoTo /D (section.4) >> |
endobj |
60 0 obj |
(Grading) |
endobj |
61 0 obj |
<< /S /GoTo /D (subsection.4.1) >> |
endobj |
64 0 obj |
(Coding Style) |
endobj |
65 0 obj |
<< /S /GoTo /D (subsection.4.2) >> |
endobj |
68 0 obj |
(Submission) |
endobj |
69 0 obj |
<< /S /GoTo /D (subsection.4.3) >> |
endobj |
72 0 obj |
(Online Demonstration and Grade Breakdown) |
endobj |
73 0 obj |
<< /S /GoTo /D (subsection.4.4) >> |
endobj |
76 0 obj |
(Extra Credit) |
endobj |
77 0 obj |
<< /S /GoTo /D [78 0 R /Fit ] >> |
endobj |
80 0 obj << |
/Length 2354 |
/Filter /FlateDecode |
>> |
stream |
xÚYëoÛ8ÿ¿ÂÈ«ÄIQ¯ÅÒmî¶è>®öápÈæ"+±¶¶¤ÕÃiþûámÉ»Y´È#r8ùq^¹^]ÝøáLáPϳHÏ"S'³åêÖ{»ðµ2sHï&Ýlh¤¥Òó8ÐÆûµ¹P±Wýg}ègN?¿ií/×9MÚç¶íÒnE³ÿ^$ÚËïi²È]å°Ü\Ü-ß½[©?eEòcø2eÛ³?Înïälu&gïϤð8=ÁD |
$z¶=3aì&³ÅÙ¿÷|æÀh>ätm¯ÍIì¯/c!íõèYôÒ.Â;XÄ*ñá\ÿSµJ/RÞ%ïË3(É¥^I½eõÈ@~ Ö4½-¶ÀÅîùSßæ+«Ê0»SçZ0#{ør]ðöúØ(YZùªGy¤=}Í<ª¶5Û®ëWyÙµ jVA%ÈÙÜÄ" |
+oÂ窱ڴ`pßWÞÓºÈÖôù¡j¶4!h@ÂûmÕâUà=«|óLT´ÿÄDBZÕ|,9`õ |
+ß=or\ª×¸{xùçt¿-RE@¶×°²a²7è7]1ïÖt¿t³EEäÍEd¼¼Jͯò¦/íL7E÷ügiA¢CPb¤¥I6(£è.ösND¬$Ð |¶Øeë¨`¶"# |
+½3$åÄæóRÞÊ/bÏ¥z¹i`±r3æ´t,ÕL0#sÂà7)58NAzÁÌÛä@ ¦ï7¼ot+åÕyÓmÇst8e[í·`qþÛ²F\¸ÐʸRBÑìVß½uâ9bÀ~+Âp¾yñbqZ@Pî[NGQùH¤Q¢Øw * 0Æ[¾vÌ|ql5gÌ&Æ )ý°õqHÖ?µ3`óã¨îï7E»Î[bɬ>eÿy(Ò«vÚÇðÕ#MÚGϬ®Î>4ørÿL_È@ø^tCÄ]¿ZM5Ô5R«)mFBE¾âlÔ¸é(º 2 |
+%,Ý+©Ûãï²B%>UÑíÓ¢Ä@ÅAq#¼_üò3#P1i¥pº¥|$>`×<9<ÄÉzOÆÛº*WcHþèó¶k÷ç±od¹ÉÓ¶;z-ÕfS=a ²¯zòçyk3¹Y ( |
+ß@CâÜPâ¶y r~r¨·Eù¾"7P@çÄίíkÌRIRóLtª[¡ë±iÚÒB*.ðñæ-QÀ|ʪ¢ûBÃ@±÷?ìÖDʦ@ØIIéçUiÞ=Èf^ñÏ |
+8ò~| c°£7øpÖÓñ3erPs·S[{%·Ïe~¦±Íqò>Ý¥¬)êIѸ*æ:RSÍ©/ÔÅÁ!÷onlPsE¿ÿ¦¥MÛsª |
+¥s°§x,Ýå©Ìc:mWt9ÿºùÀÄo´±Çm(éݸêÛY£>Õ ZÛqAç¹.ÀÿÝ¡åÍïmUÖJ~ìûÂ>¤PpiÊÔ·ÛÐ18NyzÕ"°vËð |
+oöÂþA"Ø W¯ÃöZÀôTm¾å^çª {nûÿSÐïujß$ÿ§àµó¬»Nä«Þý;àeÚX¥k8ð¸h)ÞÒj^ò°ïÀÆ¡½;l¥ØA îÝC"z`àCdPÒu, Ã&O¸é[÷%'`&{Y ñ+ ÅRº¬õçjoï],¸üC!òº}endstream |
+endobj |
+78 0 obj << |
+/Type /Page |
+/Contents 80 0 R |
+/Resources 79 0 R |
+/MediaBox [0 0 612 792] |
+/Parent 109 0 R |
+/Annots [ 89 0 R 90 0 R 91 0 R 95 0 R 99 0 R ] |
+>> endobj |
+89 0 obj << |
+/Type /Annot |
+/Border[0 0 1]/H/I/C[0 1 0] |
+/Rect [363.9986 558.5216 371.9687 568.8706] |
+/Subtype /Link |
+/A << /S /GoTo /D (cite.fielding:acmtit2002) >> |
+>> endobj |
+90 0 obj << |
+/Type /Annot |
+/Border[0 0 1]/H/I/C[0 1 0] |
+/Rect [375.5617 558.6412 383.5318 569.0499] |
+/Subtype /Link |
+/A << /S /GoTo /D (cite.restful) >> |
+>> endobj |
+91 0 obj << |
+/Type /Annot |
+/Border[0 0 1]/H/I/C[0 1 0] |
+/Rect [186.2145 431.9742 194.1846 442.2036] |
+/Subtype /Link |
+/A << /S /GoTo /D (cite.rfc2616) >> |
+>> endobj |
+95 0 obj << |
+/Type /Annot |
+/Border[0 0 1]/H/I/C[0 1 0] |
+/Rect [209.4311 329.6253 217.4013 340.0341] |
+/Subtype /Link |
+/A << /S /GoTo /D (cite.json) >> |
+>> endobj |
+99 0 obj << |
+/Type /Annot |
+/Border[0 0 1]/H/I/C[0 1 1] |
+/Rect [361.2472 159.7745 484.0759 173.8475] |
+/Subtype/Link/A<</Type/Action/S/URI/URI(http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.2.2)>> |
+>> endobj |
+81 0 obj << |
+/D [78 0 R /XYZ 72 744.9066 null] |
+>> endobj |
+85 0 obj << |
+/D [78 0 R /XYZ 72 720 null] |
+>> endobj |
+6 0 obj << |
+/D [78 0 R /XYZ 72 663.3806 null] |
+>> endobj |
+10 0 obj << |
+/D [78 0 R /XYZ 72 522.2678 null] |
+>> endobj |
+14 0 obj << |
+/D [78 0 R /XYZ 72 411.4484 null] |
+>> endobj |
+79 0 obj << |
+/Font << /F36 84 0 R /F37 88 0 R /F41 94 0 R /F30 98 0 R /F29 102 0 R /F26 105 0 R /F43 108 0 R >> |
+/ProcSet [ /PDF /Text ] |
+>> endobj |
+116 0 obj << |
+/Length 2722 |
+/Filter /FlateDecode |
+>> |
+stream |
+xÚ¥Y_oÜ6÷§Xø%2à¥ùG¢¤ÜË5EshѤ½ÄAQ$yàJ\¯´]qíúÛwÈj%G¹ØÈáp8ÎüfÈ}q{qóRéHJµ\ÝnW¹\å)tyQ®në÷É÷oéÕZf<yiÚ[yµ.2&¿¯Dôlåp,ÃÏ?¤T·;áqq5ö~»*eb7Øyk÷Me=½úxûÓÅ·bÅáOTÁ2ųUµ¿øãâýG¾ª/øê§ÎTYd«èp&ÊR®ö©.b§½x{ñßQΧ^ÄíVfÙ¸}.×ÐðÛèO-¨J}Zw:v^Á©MÌY¦),ä§}úî dVä)0ð,åxÆqâÓyBi&¢ Û%Ù+ó\Ç¥ëiÚH©eZçgíÿ¥Ë |
+¿b¬Qâz*2l®R%RsùÝÑz¸|~µóäRäéåõÕZûvÛÚÜßÁ lcµ:G kpÄ4Í áý%gsvÔåÇØ;º®éî¾°ÐLs©WºÈ/þvDë©È%HPoÙ&.Á9K¥¾t÷·.¾%HÐÜÈ &¶VZ°\{[KÁ4¨%ðsÚªï(F?p.-DÀÓ |
+u=e½át8ôGÇ(óiN ¤©¼a'AÀ ÙµiA§ 0é |
+¡³EãîQ |
+"Æw¡¤Þ=CGö4Vþ;2ÅÏ(Ä«Có{pô4 Úèj¢²gmðkØ>u!æãzÐÊZDx¾Ù~íÜÖ#çlçàM ÔYò2¸Ö°C(º*ı.fã¸a)K¬dY¦&7M}gÝÍ¡=ÝÜOÚÞ±9¾±~oöÈ>-«;©`¹oÆÙTÄx>ë%ñàPãñðé! |
+¬£©ýº¯ñÎôþAÊ o9ÈâgXRʳ`VéE®Èªÿ¯9¥L<'1ÑßT.ÅoðãAçùßÂÈm°¦R©,úôû |
+±} X8ÜãÁbkµ=Þp6fð9Êô$ÍZD=fa½á´õËyâ>àx¼È¼¨QHÌÀÚZ3¸%$`;·o¯ÁÔäá\ÿ+V)í7önöÍÞ2¿·§¨n"àïÙ m·d^ª¡Ý±T9e _Ì Ð0UÕÔ`g@UØí#í~ !=ôÍ3ÈMäí`QØÉ0N2íEë>aLOÞLC±ë´ªiXH[§r0Íþ½ùfêÅ0 *ªU¡Oó#¸¹b°z'UÌÁ5*û3ÔØzóõߧÍï¼Æäþ%-Jºbî,uhøÁ³Ä!9jvCSÚQ |
+FN òp \D*ãpj¶W®ÅàM5UëÓ |
+*ú£*| R'9£Fw"UäT{ÁT>ÅÔéîÑ»+âí¬ |
+~B~ð JæÓÛù3õÀDóü aJßtÈ |
+³ù.½oM+?¶+~ÛYªAÎYPñÔÕ |
+X±(¤éîì´2òCK>{ºÌï}ç)Àrâ¯r\ZR~Ý5`?üC ѧ/OA³yGò$dtvhÔÆB~_OZ¹¡õ¿HÑÓTAýá(Þuÿúå`âb¡¢wÜm߶ýÃX{siGº¾V z7[OVr,§®íûÃVi¸ñ#H»i¯/ÞâÊPQ|VIopÓÄ©¨{Ø ô1!Óxf¦#¢÷¯ì@e¸Æj¨ìì¬ö+z<|1 ñzú=ìZå$* |
+dB¸`]¾xxÑ´rvÆþvùB Ò÷èÛbÛ8ñ¡ ï¡0²!~àµàæÃKÀ*:£$XÐÈc"bKº¿±>ÁN@BÆ«Aä$ôàØ}°ÍݦA]MÆÎÀ¥Ta2i° Ýi¿±T j|(S?y¼|S |
+jd$D^Ç`½[Éw`²¾2]ß-¾¾JfÄ»·à`á ôÙO¡½íÇ|é»tlúPØãsÄ1)f ·{ßûc»j,¸*èþ¸ïüA÷¾9ºJTQb|Úá"¼¸g÷ô±K³B»!±´©ùàÆTïïüCZRPæO`åÙ#pÁ8`+d`a使x×ؾoHШQ«ï~}úòòq. ~?ÈóâGëÄ wÇTHS¨qáPT¾þåõï¯~y÷ußZHR¸ÇøÜ |
+ËïGÒr |
+8ÖU³¥C95âg%÷÷¸Á½Iê´ô¯HLq?ÆQüàaLnFÿÃðûÂ5^3~ç£÷ßãë×~&J0.D¼åIäeü! *ø¼È#¤¾ìØ{ÿr4pô°W½oÎJ= $Çç9ÖÝba¥y|_JAºº |
+ª GÁ |
+¨Iã5ä´sÊ®'¿_²üÁendstream |
+endobj |
+115 0 obj << |
+/Type /Page |
+/Contents 116 0 R |
+/Resources 114 0 R |
+/MediaBox [0 0 612 792] |
+/Parent 109 0 R |
+/Annots [ 118 0 R 119 0 R ] |
+>> endobj |
+118 0 obj << |
+/Type /Annot |
+/Border[0 0 1]/H/I/C[0 1 1] |
+/Rect [338.2543 625.4705 540.9963 639.5436] |
+/Subtype/Link/A<</Type/Action/S/URI/URI(http://cs3214.cs.vt.edu:9011/loadavg)>> |
+>> endobj |
+119 0 obj << |
+/Type /Annot |
+/Border[0 0 1]/H/I/C[0 1 1] |
+/Rect [94.2326 611.0247 302.0912 625.0978] |
+/Subtype/Link/A<</Type/Action/S/URI/URI(http://cs3214.cs.vt.edu:9011/meminfo)>> |
+>> endobj |
+117 0 obj << |
+/D [115 0 R /XYZ 72 738.929 null] |
+>> endobj |
+18 0 obj << |
+/D [115 0 R /XYZ 72 572.1298 null] |
+>> endobj |
+22 0 obj << |
+/D [115 0 R /XYZ 72 377.3775 null] |
+>> endobj |
+120 0 obj << |
+/D [115 0 R /XYZ 72 248.4192 null] |
+>> endobj |
+121 0 obj << |
+/D [115 0 R /XYZ 72 168.9671 null] |
+>> endobj |
+114 0 obj << |
+/Font << /F36 84 0 R /F41 94 0 R /F30 98 0 R /F37 88 0 R /F29 102 0 R /F26 105 0 R /F43 108 0 R >> |
+/ProcSet [ /PDF /Text ] |
+>> endobj |
+124 0 obj << |
+/Length 2527 |
+/Filter /FlateDecode |
+>> |
+stream |
+¥zââO.M;Ñwð1}%}²ëÉɵ~îǯnk´"P¢\ñÚÈÊM¾~ËU`2©p4¯OWcÐytÃi |
+ukÐàqÌ"Æ |
+ÊÂÙÕ8Z+f]ñ ¼îxÜD¢ñ¢jȹ/w |
+Áå7]7úØ0Éâ0ùYft õ9Ù¿vz)$øûhìÂÐfí¥ïqRBd@}óÍáï Ùbù{24W^ë£KÝHFG~¨Ð¹Ái]éNÿ}àpvº,àôö ¯Ã¸¬õßn`uV2; |
+Wwú;'Wö`JCÏOïäî#Ä·ß~ËW°cB_Áòætq+_ÝX *r~`ìSí0Wp ÍTPe±°#j Wª£º(§Èg@}WË`×ÓoßÿÌ\»ê?LW^zÍåÇyê@¦dðhId²M¤lÙѾ± |
+ôYÍTq©o± çj±çtÐÑÐòµ')ÑÈwæNÀ´9R:D,G¶«øæpxOÐ:1à*s3ÍÝÏõvßÈËsH`WÝYèµk,!){ ¤N|öb¹d¡]OÓ³´HëS:ÒqJG<z ~ùO¬°ÏD27NHãM,F»b°ÃÎ|·1û f^q«ÎÆÜg¤®PFp}áÐ^7fa ÎÅ",r©vàí |
+îA¸¡ÜòZ # 3¡3>Vófã ÆÎàðuiûÙp§O>Ë'ºè{G ºÕ$W½p½³· |
+w/ñôRq¢ éקؼcSÕQ¤*Ñlèåà»_3K;g |
+Û=¡#o6(ݶÛïTÇ.æ |
+±}%¿arÿ |
+â:qÎK3Ü)¬¨¾Õu_0-+ßLÖ4{8}ë43˺ÏÜË«¶pÊw"JjÊxU°®mRtçnu߸+¹òYDMY!VÏÐ}Cרã·äÔÈ¿=9 Á¬ PK§ÜWãÌ_BìÞPvõÕßú'òÕñÊñ:=ÕDQÌ} d±®j¸Ó)]!ºlð=ôüÕc«äSý¦2ç>г`ÉIë¦îñ`AÇ¢:ù6£û4ãòáhÊ7?æJ¡BCpþ¹2Ï E_~Ñ]²ó/º)´H*GåIXÄY¶+0òèETE¥H§ J£ |
+!?*ºÍòÕýº§ùª7#BW?àÛõ8b®Äû%ôø.ÔD ,»öL´ô |
+4«`¹,KéñOÌüëßO MÐÇ Æü Ý=üvf¨ú_áÃö/X§ï«²0ÏÀ[<ÝçQZø÷*)§$1Säa*2ÄÍÌe/yµøùãO»ÕÓÞendstream |
+endobj |
+123 0 obj << |
+/Type /Page |
+/Contents 124 0 R |
+/Resources 122 0 R |
+/MediaBox [0 0 612 792] |
+/Parent 109 0 R |
+/Annots [ 127 0 R ] |
+>> endobj |
+127 0 obj << |
+/Type /Annot |
+/Border[0 0 1]/H/I/C[1 0 0] |
+/Rect [311.985 349.7717 318.4607 365.0371] |
+/Subtype /Link |
+/A << /S /GoTo /D (Hfootnote.1) >> |
+>> endobj |
+125 0 obj << |
+/D [123 0 R /XYZ 72 738.929 null] |
+>> endobj |
+126 0 obj << |
+/D [123 0 R /XYZ 72 619.2439 null] |
+>> endobj |
+26 0 obj << |
+/D [123 0 R /XYZ 72 449.3403 null] |
+>> endobj |
+30 0 obj << |
+/D [123 0 R /XYZ 72 224.2019 null] |
+>> endobj |
+128 0 obj << |
+/D [123 0 R /XYZ 89.9328 103.968 null] |
+>> endobj |
+122 0 obj << |
+/Font << /F36 84 0 R /F41 94 0 R /F37 88 0 R /F43 108 0 R /F29 102 0 R /F26 105 0 R >> |
+/ProcSet [ /PDF /Text ] |
+>> endobj |
+131 0 obj << |
+/Length 2820 |
+/Filter /FlateDecode |
+>> |
+stream |
+Ö0&ÉIï¡óÁÅ&6/üu´! ê#U4i`¿ìËq±ÐÝînÆz@Û-f]YɨRXg=Ç+ео*KjËf[:;¾3ÒaæÜù¹nhD{e)cfUeÃDÛС µyã@ö lÛãql@{àY?Úm.KmSn~jÙu°^Ú px®7öD+å7c')}¬8²69¬8rPqäL;ëÖØëLZ/è){ðêÆ*»báÒ ±´(P´(|IøníP?5·½ÃØumïdhëöH%AÇnNP$³i[áͦã@>Î{8¤ºÚÛÜ0âÔAwèâuº¾f ×ÊÀ?&èÊ|o<®ڦa×pÕ1ÁÇ9>=üóÏ7@´Å3Õ¸·Ã/ïê×±íÁôX¾eUÙÎ1:°2H,Ô©.·Á2/Ø3lÐã,àXǼÜ4yVY¬yR°o+ümÀ ÌW6¼ÎC¼96MüàiA@u¨!¢â)ÞáC¦ârÐ[C wõîeñ.RèÒÅW1"RAé¸K,A,æÌ7m9qÂOÁbKpÉ-úC,]®á± ÆYµÌniÚýL@ëñ¾PÁKW/DKZëàl{ßí¨'Åv.ªáY1=ð0ðú=784ºÌJe%2"®!.Ãæñ¾¤ëk+~V*ê>MB¯ðÂ]ÙÁö6eõÌ/tÛÐáå<üº^µ|;ÛÃe?ÚíÍÓÑ |
+ß5ÃØó5Ðj |
+} ßØ8WÃλH¼âúÎÐL$~ÄÆ>ÕMÃ8¤|k'èÒKsש˳Dw ëÍ;BÒø¶%s¿:Zpþ¦úÜ'cOíá5y|D¯è¹£n õÂy¾ï nÎò³JüiÉ qyQ®àdEwT =|¸R_ +ôrrÿ<æþºäp /`[£çØ6+B lVÝ]?"ôO#£»Ð²¸ùYZ¤p BUDÿ°µ |
+!ÈW³qþiÉåj:jà>t寣]QEVÎy3ÑÙ¶=²p @Kë\s0èÅaNúUYÝà&F|°Gxb¸yueyÀ,j`°ü ÷û>åD!)j q5LÌQÈçh ÆÍádWk !"¼«*yoÀQf¯XS5ìÙôHÒdÝiÁ¢R«(ĬC%êf×¢ÃÇäñ®Ó |
+SÈ´¶_cð'=ÝGû¢1- |
+ó8oâÑØL¨ |
+ |
+Ò/ú_÷O5$½'Í¥l$¾ß"Òì7©w¶ÍÄo%Pdúbâ_Û³Ur)wÊëfüÂä3Ö|LËr'¸R².ìnPÍ;[ºÑL¨fË9Ì·äG½X..V°GMÅ-ªFÙÉïÓñxbM'Æl|GªËXèoשøRçv,kÌvdÑ«¡äá=/ÆÅSÎKª±/É)§¢Ô ¿¢ëÍix4LͶ`ÔåÐp¸«OXE)R:¿UõnIõ9¤êaxàµxÀ(üf8¥2»+ëLiSö02óXÊXHßÆ£T)¯O"×'Z ä£xË/å|óÒìK±T< §"Li|(àÉÕP côûÈÂ5:*Bëâ÷à |
+Ì&½þÍ\Ü{ùÁ |
+ÐSè' |