CSAPP-Tiny Web服务器【2】源码解析
上篇博客:CSAPP-Tiny Web服务器【1】编译搭建 阐述Tiny编译搭建过程,接下来将对其源码进行深入解析~
将一个实际的浏览器指向我们自己的服务器,看着它显示一个复杂的带有文本和图片的Web页面,真是颇具成就感~
下面就来看看它的实现全过程:
1. Tiny-main程序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
int main( int argc, char **argv) { int listenfd, connfd, port, clientlen; struct sockaddr_in clientaddr; /* Check command line args */ if (argc != 2) { fprintf (stderr, "usage: %s <port>\n" , argv[0]); exit (1); } port = atoi (argv[1]); listenfd = Open_listenfd(port); while (1) { clientlen = sizeof (clientaddr); connfd = Accept(listenfd, (SA *)&clientaddr, &clientlen); //line:netp:tiny:accept doit(connfd); //line:netp:tiny:doit Close(connfd); //line:netp:tiny:close } } |
Tiny是一个迭代服务器,监听在命令行中确定的端口上的连接请求。在通过open_listenedfd函数打开一个监听套接字以后,Tiny执行典型的无限服务循环,反复地接受一个连接(accept)请求,执行事务(doit),最后关闭连接描述符(close)。
2. Tiny-doit函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
|
/* * doit - handle one HTTP request/response transaction */ /* $begin doit */ void doit( int fd) { int is_static; struct stat sbuf; char buf[MAXLINE], method[MAXLINE], uri[MAXLINE], version[MAXLINE]; char filename[MAXLINE], cgiargs[MAXLINE]; rio_t rio; /* Read request line and headers */ Rio_readinitb(&rio, fd); //Associate a descriptor with a read buffer and reset buffer Rio_readlineb(&rio, buf, MAXLINE); //line:netp:doit:readrequest sscanf (buf, "%s %s %s" , method, uri, version); //line:netp:doit:parserequest if (strcasecmp(method, "GET" )) //ignore case //line:netp:doit:beginrequesterr { clienterror(fd, method, "501" , "Not Implemented" , "Tiny does not implement this method" ); return ; } //line:netp:doit:endrequesterr read_requesthdrs(&rio); //line:netp:doit:readrequesthdrs /* Parse URI from GET request */ is_static = parse_uri(uri, filename, cgiargs); //line:netp:doit:staticcheck if (stat(filename, &sbuf) < 0) //line:netp:doit:beginnotfound { clienterror(fd, filename, "404" , "Not found" , "Tiny couldn't find this file" ); return ; } //line:netp:doit:endnotfound if (is_static) /* Serve static content */ { if (!(S_ISREG(sbuf.st_mode)) || !(S_IRUSR & sbuf.st_mode)) //line:netp:doit:readable { clienterror(fd, filename, "403" , "Forbidden" , "Tiny couldn't read the file" ); return ; } serve_static(fd, filename, sbuf.st_size); //line:netp:doit:servestatic } else /* Serve dynamic content */ { if (!(S_ISREG(sbuf.st_mode)) || !(S_IXUSR & sbuf.st_mode)) //line:netp:doit:executable { clienterror(fd, filename, "403" , "Forbidden" , "Tiny couldn't run the CGI program" ); return ; } serve_dynamic(fd, filename, cgiargs); //line:netp:doit:servedynamic } } /* $end doit */ |
解析:
rio_readinitb(&rio,fd) :将程序的内部缓存区与描述符相关联。
rio_readlineb(&rio,buf,MAXLINE) :从内部缓存区读出一个文本行至buf中,以null字符来结束这个文本行。当然,每行最大的字符数量不能超过MAXLINE。
sscanf(buf,"%s %s %s",method,uri,version) :作为例子,一般此时buf中存放的是“GET / HTTP/1.1”,所以可知method为“GET”,uri为“/”,version为“HTTP/1.1”。其中sscanf的功能:把buf中的字符串以空格为分隔符分别传送到method、uri及version中。
strcasecmp(method,"GET") :忽略大小写比较method与“GET”的大小,相等的话返回0。
read_requesthdrs(&rio) :读并忽略请求报头。
parse_uri(uri,filename,cgiargs) :解析uri,得文件名存入filename中,参数存入cgiargs中。
stat(filename,&sbuf) :将文件filename中的各个元数据填写进sbuf中,如果找不到文件返回0。
S_ISREG(sbuf,st_mode) :此文件为普通文件。
S_IRUSR & sbuf.st_mode :有读取权限。
serve_static(fd,filename,sbuf.st_size) :提供静态服务。
serve_dynamic(fd,filename,cgiargs) :提供动态服务。
(1)从doit函数中可知,我们的Tiny Web服务器只支持“GET”方法,其他方法请求的话则会发送一条错误消息,主程序返回,并等待下一个请求。否则,我们读并忽略请求报头。(其实,我们在请求服务时,直接不用写请求报头即可,写上只是为了符合HTTP协议标准)。
(2)然后,我们将uri解析为一个文件名和一个可能为空的CGI参数,并且设置一个标志位,表明请求的是静态内容还是动态内容。通过stat函数判断文件是否存在。
(3)最后,如果请求的是静态内容,我们需要检验它是否是一个普通文件,并且可读。条件通过,则我们服务器向客服端发送静态内容;相似的,如果请求的是动态内容,我就核实该文件是否是可执行文件,如果是则执行该文件,并提供动态功能。
3. Tiny的clienterror函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
/* * clienterror - returns an error message to the client */ /* $begin clienterror */ 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>Tiny 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 Tiny Web server</em>\r\n" , body); /* Print the HTTP response */ sprintf (buf, "HTTP/1.0 %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)); } /* $end clienterror */ |
向客户端返回错误信息。
sprintf(buf,"------------"):将字符串“------------”输送到buf中。
rio_writen(fd,buf,strlen(buf)):将buf中的字符串写入fd描述符中。
4. Tiny的read_requesthdrs函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
/* * read_requesthdrs - read and parse HTTP request headers */ /* $begin read_requesthdrs */ void read_requesthdrs(rio_t *rp) { char buf[MAXLINE]; Rio_readlineb(rp, buf, MAXLINE); while ( strcmp (buf, "\r\n" )) //line:netp:readhdrs:checkterm { Rio_readlineb(rp, buf, MAXLINE); printf ( "%s" , buf); } return ; } /* $end read_requesthdrs */ |
Tiny不需要请求报头中的任何信息,所以我们这个函数就是来跳过这些请求报头的。具体做法就是读这些请求报头,直到空行,然后返回。OK!
5. Tiny的parse_uri函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
/* * parse_uri - parse URI into filename and CGI args * return 0 if dynamic content, 1 if static */ /* $begin parse_uri */ int parse_uri( char *uri, char *filename, char *cgiargs) { char *ptr; if (! strstr (uri, "cgi-bin" )) { /* Static content */ //line:netp:parseuri:isstatic strcpy (cgiargs, "" ); //line:netp:parseuri:clearcgi strcpy (filename, "." ); //line:netp:parseuri:beginconvert1 strcat (filename, uri); //line:netp:parseuri:endconvert1 if (uri[ strlen (uri)-1] == '/' ) //line:netp:parseuri:slashcheck strcat (filename, "home.html" ); //line:netp:parseuri:appenddefault return 1; } else { /* Dynamic content */ //line:netp:parseuri:isdynamic ptr = index(uri, '?' ); //line:netp:parseuri:beginextract if (ptr) { strcpy (cgiargs, ptr+1); *ptr = '\0' ; } else strcpy (cgiargs, "" ); //line:netp:parseuri:endextract strcpy (filename, "." ); //line:netp:parseuri:beginconvert2 strcat (filename, uri); //line:netp:parseuri:endconvert2 return 0; } } /* $end parse_uri */ |
根据uri中是否含有cgi-bin来判断请求的是静态内容还是动态内容。如果没有cgi-bin,则说明请求的是静态内容。那么,我们需把cgiargs置NULL,然后获得文件名,如果我们请求的uri最后为 “/”,则自动添加上home.html。比如说,我们请求的是“/”,则返回的文件名为“./home.html”,而我们请求“/logo.gif”,则返回的文件名为“./logo.gif”。如果uri中含有cgi-bin,则说明请求的是动态内容。那么,我们需要把参数拷贝到cgiargs中,把要执行的文件路径写入filename。举例来说,uri为/cgi-bin/adder?3&5,则cigargs中存放的是3&5,filename中存放的是“./cgi-bin/adder”,OK!
index(uri,'?') : 找出uri字符串中第一个出现参数‘?’的地址,并将此地址返回。
6. Tiny的serve_static函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
/* * serve_static - copy a file back to the client */ /* $begin serve_static */ void serve_static( int fd, char *filename, int filesize) { int srcfd; char *srcp, filetype[MAXLINE], buf[MAXBUF]; /* Send response headers to client */ get_filetype(filename, filetype); //line:netp:servestatic:getfiletype sprintf (buf, "HTTP/1.0 200 OK\r\n" ); //line:netp:servestatic:beginserve sprintf (buf, "%sServer: Tiny 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)); //line:netp:servestatic:endserve /* Send response body to client */ srcfd = Open(filename, O_RDONLY, 0); //line:netp:servestatic:open srcp = Mmap(0, filesize, PROT_READ, MAP_PRIVATE, srcfd, 0); //line:netp:servestatic:mmap Close(srcfd); //line:netp:servestatic:close Rio_writen(fd, srcp, filesize); //line:netp:servestatic:write Munmap(srcp, filesize); //line:netp:servestatic:munmap } /* * 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 strcpy (filetype, "text/plain" ); } /* $end serve_static */ |
打开文件名为filename的文件,把它映射到一个虚拟存储器空间,将文件的前filesize字节映射到从地址srcp开始的虚拟存储区域。关闭文件描述符srcfd,把虚拟存储区的数据写入fd描述符,最后释放虚拟存储器区域。
7. Tiny的serve_dynamic函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
/* * serve_dynamic - run a CGI program on behalf of the client */ /* $begin serve_dynamic */ void serve_dynamic( int fd, char *filename, char *cgiargs) { char buf[MAXLINE], *emptylist[] = { NULL }; /* Return first part of HTTP response */ sprintf (buf, "HTTP/1.0 200 OK\r\n" ); Rio_writen(fd, buf, strlen (buf)); sprintf (buf, "Server: Tiny Web Server\r\n" ); Rio_writen(fd, buf, strlen (buf)); if (Fork() == 0) { /* child */ //line:netp:servedynamic:fork /* Real server would set all CGI vars here */ setenv( "QUERY_STRING" , cgiargs, 1); //line:netp:servedynamic:setenv Dup2(fd, STDOUT_FILENO); /* Redirect stdout to client */ //line:netp:servedynamic:dup2 Execve(filename, emptylist, environ); /* Run CGI program */ //line:netp:servedynamic:execve } Wait(NULL); /* Parent waits for and reaps child */ //line:netp:servedynamic:wait } /* $end serve_dynamic */ |
Tiny在发送了响应的第一部分后,通过派生一个子进程并在子进程的上下文中运行一个cgi程序(可执行文件),来提供各种类型的动态内容。
setenv("QUERY_STRING",cgiargs,1) :设置QUERY_STRING环境变量。
dup2 (fd,STDOUT_FILENO) :重定向它的标准输出到已连接描述符。此时,任何写到标准输出的东西都直接写到客户端。
execve(filename,emptylist,environ) :加载运行cgi程序。
8. Tiny的准备-CSAPP 封装 csapp.h csapp.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
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 */ |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
|
/* $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 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 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 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" ); else dns_error( "Open_clientfd DNS error" ); } return rc; } int Open_listenfd( int port) { int rc; if ((rc = open_listenfd(port)) < 0) unix_error( "Open_listenfd error" ); return rc; } /* $end csapp.c */ |