
wrapsock.c
Go to the documentation of this file.
00001 /* wrapsock.c */ 00002 /* Functions wrapper module */ 00003 /* To be used with tcpcli.c and tcpserv.c 00004 /* Author: Michèle Garoche */ 00005 /* Date: February 26th 2003 00006 /* Feel free to do what you want with it */ 00007 00012 /* Includes */ 00013 #include "wrapsock.h" 00014 00018 /* Global variables */ 00019 int daemon_proc; /* set to nonzero by daemon_init() */ 00020 00021 /* Static functions */ 00022 00030 /* Main function to handle error */ 00031 static void err_doit(int errnoflag, int level, const char *fmt, va_list ap) 00032 { 00033 int errno_save; /* for storing the errno before printing it */ 00034 int n; /* length of the message to print */ 00035 char buf[MAXLINE + 1]; /* buffer for printing */ 00036 00037 /* Save the error number */ 00038 errno_save = errno; 00039 /* write the message into buf */ 00040 vsnprintf(buf, MAXLINE, fmt, ap); 00041 /* Retrieve the length of buf */ 00042 n = strlen(buf); 00043 00044 /* There was an error 00045 Write the message + the error number to buf */ 00046 if (errnoflag) 00047 { 00048 snprintf(buf + n, MAXLINE - n, ": %s", strerror(errno_save)); 00049 } 00050 00051 /* Append a new line character to buf */ 00052 strcat(buf, "\n"); 00053 00054 /* If the process was launched as a daemon, write the message 00055 to the console with level LOG_ERR */ 00056 if (daemon_proc) 00057 { 00058 syslog(level, "%s", buf); 00059 } 00060 /* The process was launched as normal process */ 00061 else 00062 { 00063 /* Empty the std out */ 00064 fflush(stdout); 00065 /* Write the message to stderr */ 00066 fputs(buf, stderr); 00067 /* Empty stderr */ 00068 fflush(stderr); 00069 } 00070 00071 return; 00072 } 00073 00080 /* Handle the errors when reading a stream */ 00081 static ssize_t mg_read(int fd, char *ptr) 00082 { 00083 static int read_cnt = 0; 00084 static char *read_ptr; 00085 static char read_buf[MAXLINE]; 00086 00087 if (read_cnt <= 0) 00088 { 00089 again: 00090 if ( (read_cnt = read(fd, read_buf, sizeof(read_buf))) < 0) 00091 { 00092 if (errno == EINTR) 00093 { 00094 goto again; 00095 } 00096 return -1; 00097 } 00098 else if (read_cnt == 0) 00099 { 00100 return 0; 00101 } 00102 00103 read_ptr = read_buf; 00104 } 00105 00106 read_cnt--; 00107 *ptr = *read_ptr++; 00108 return 1; 00109 } 00110 00111 /* Other functions */ 00112 00117 /* Display an help message and quit */ 00118 void cliusage(char *progname) 00119 { 00120 err_quit("\nusage: %s\n" 00121 " -a manage all type of requests\n" 00122 " -b compute numbers requests\n" 00123 " -c convert word from uppercase to lowercase requests\n" 00124 " -h this help\n\n" 00125 " address\n\n" 00126 "You can launch as many clients as your system supports with\n" 00127 "with different types of request.\n\n" 00128 "To terminate a client, just issue:\n" 00129 " ctrl-D\n" 00130 "in the corresponding terminal.\n\n" 00131 "To erase an entry before sending it, type:\n" 00132 " ctrl-U\n" 00133 "in the terminal (useful when the buffer is full).\n\n", progname); 00134 } 00135 00142 /* Check the validity of address */ 00143 void Inet_pton(int family, const char *strptr, void *addrptr) 00144 { 00145 int n; /* value returned by inet_pton */ 00146 00147 /* Some system error occurs */ 00148 if ( (n = inet_pton(family, strptr, addrptr)) < 0) 00149 { 00150 err_sys("inet_pton error for %s", strptr); 00151 } 00152 /* The address is not parseable in the specified address family */ 00153 else if (n == 0) 00154 { 00155 err_quit("inet_pton error for %s", strptr); 00156 } 00157 } 00158 00165 /* Connect the client to the listening socket */ 00166 void Connect(int fd, const struct sockaddr *sa, socklen_t salen) 00167 { 00168 if (connect(fd, sa, salen) < 0) 00169 { 00170 err_sys("connect error"); 00171 } 00172 } 00173 00179 void str_cli(FILE *fp, int sockfd) 00180 { 00181 int maxfdp1; /* maximum of file descriptors */ 00182 int stdineof; /* flag for EOF on stdin */ 00183 fd_set rset; /* the set of descriptors */ 00184 char sendline[MAXLINE]; /* buffer for sending */ 00185 char recvline[MAXLINE]; /* buffer for receiving */ 00186 00187 /* NO EOF */ 00188 stdineof = 0; 00189 /* Clear all bits on the set of fd */ 00190 FD_ZERO(&rset); 00191 00192 for ( ; ; ) 00193 { 00194 if (stdineof == 0) 00195 { 00196 /*Turn on the bit for stdio */ 00197 FD_SET(fileno(fp), &rset); 00198 } 00199 /* Turn on the bit for sockfd */ 00200 FD_SET(sockfd, &rset); 00201 /* Compute the max of the two descriptors 00202 + 1 as the descriptor begins with 0 */ 00203 maxfdp1 = MGMAX(fileno(fp), sockfd) + 1; 00204 /* Call select with the set and no timeout 00205 so that it blocks until a descriptor 00206 in the set is ready */ 00207 Select(maxfdp1, &rset, NULL, NULL, NULL); 00208 00209 /* The socket is ready */ 00210 if (FD_ISSET(sockfd, &rset)) 00211 { 00212 /* Read the line */ 00213 if (Readline(sockfd, recvline, MAXLINE) == 0) 00214 { 00215 /* No data to read */ 00216 if (stdineof == 1) 00217 { 00218 return; 00219 } 00220 /* The server has terminated */ 00221 else 00222 { 00223 /* Attention: this leaves the server refused to bind */ 00224 /* when relaunching it till it receives ETIMEOUT */ 00225 /* < 1 mn on Mac OS 10.2.4 */ 00228 err_quit("str_cli: server terminated prematurely"); 00229 } 00230 } 00231 00232 /* Write it to stdout */ 00233 Fputs(recvline, stdout); 00234 } 00235 00236 /* Stdin is ready */ 00237 if (FD_ISSET(fileno(fp), &rset)) 00238 { 00239 /* Read the line */ 00240 if (Fgets(sendline, MAXLINE, fp) == NULL) 00241 { 00242 /* No more date to read */ 00243 stdineof = 1; 00244 /* Half close the socket */ 00245 Shutdown(sockfd, SHUT_WR); 00246 /* Clear the set */ 00247 FD_CLR(fileno(fp), &rset); 00248 continue; 00249 } 00250 00251 /* Write it to the socket */ 00252 Writen(sockfd, sendline, strlen(sendline)); 00253 } 00254 } 00255 } 00256 00257 00262 /* Display an help message and quit */ 00263 void servusage(char *progname) 00264 { 00265 err_quit("\nusage: %s\n" 00266 " -a manage all type of requests\n" 00267 " -b compute numbers requests\n" 00268 " -c convert word from uppercase to lowercase requests\n" 00269 " -h this help\n\n" 00270 "You can launch the server in the background like this:\n" 00271 " %s -a &\n" 00272 "Then you have to type return before launching any client.\n" 00273 "When finishing with the server, you will have to kill the server like this:\n" 00274 " kill - 9 %d\n\n" 00275 "Or launch the server in the foreground like this:\n" 00276 " %s -a\n" 00277 "Then open a new terminal to launch client.\n", progname, progname, (int)getpid(), progname); 00278 } 00279 00286 /* Bind the socket */ 00287 void Bind(int fd, const struct sockaddr *sa, socklen_t salen) 00288 { 00289 if (bind(fd, sa, salen) < 0) 00290 { 00291 err_sys("bind error"); 00292 } 00293 } 00294 00300 /* Listen for connection on the listening socket */ 00301 void Listen(int fd, int backlog) 00302 { 00303 if (listen(fd, backlog) < 0) 00304 { 00305 err_sys("listen error"); 00306 } 00307 } 00308 00314 /* Function to call to catch a SIGCHLD */ 00315 Sigfunc *Signal(int signo, Sigfunc *func) 00316 { 00317 Sigfunc *sigfunc; /* function to call when the signal raises */ 00318 00319 if ( (sigfunc = mg_signal(signo, func)) == SIG_ERR) 00320 { 00321 err_sys("signal error"); 00322 } 00323 00324 return sigfunc; 00325 } 00326 00331 /* Fork a child process */ 00332 pid_t Fork(void) 00333 { 00334 pid_t pid; 00335 00336 if ( (pid = fork()) == -1) 00337 { 00338 err_sys("fork error"); 00339 } 00340 00341 return pid; 00342 } 00343 00348 /* Close the socket */ 00349 void Close(int fd) 00350 { 00351 if (close(fd) == -1) 00352 { 00353 err_sys("close error"); 00354 } 00355 } 00356 00361 /* Handle all requests */ 00362 void str_serv1(int sockfd) 00363 { 00364 int arg1; /* first numeric argument */ 00365 int arg2; /* second numeric argument */ 00366 int i; /* counter for reading a word */ 00367 char result2[MAXLINE]; /* intermediate buffer */ 00368 int result; /* numeric result */ 00369 ssize_t n; /* length of the line */ 00370 char line[MAXLINE]; /* buffer for the line */ 00371 00372 for ( ; ; ) 00373 { 00374 /* Discard any data if the client has terminated */ 00375 if ( (n = Readline(sockfd, line, MAXLINE)) == 0) 00376 { 00377 return; /* connection closed by other end */ 00378 } 00379 00380 /* There are two numeric arguments */ 00381 if (sscanf(line, "%d%d", &arg1, &arg2) == 2) 00382 { 00383 /* Compute x pow 2 + y pow 2 */ 00384 result = (int)(pow((float)arg1, 2.) + pow((float)arg2, 2.)); 00385 00386 /* Send an overflow message if the result is greater than an int */ 00387 if ((result == INT_MAX) || (result == INT_MIN)) 00388 { 00389 strcpy(result2, "Overflow"); 00390 snprintf(line, sizeof(line), "%s\n", result2); 00391 } 00392 /* write the result into line */ 00393 else 00394 { 00395 snprintf(line, sizeof(line), "%d\n", result); 00396 } 00397 } 00398 /* There is one numeric argument */ 00399 else if (sscanf(line, "%d", &arg1) == 1) 00400 { 00401 /* Compute x pow 3 */ 00402 result = (int)pow((float)arg1, 3.); 00403 00404 /* Send an overflow message if the result is greater than an int */ 00405 if ((result == INT_MAX) || (result == INT_MIN)) 00406 { 00407 strcpy(result2, "Overflow"); 00408 snprintf(line, sizeof(line), "%s\n", result2); 00409 } 00410 /* write the result into line */ 00411 else 00412 { 00413 snprintf(line, sizeof(line), "%d\n", result); 00414 } 00415 } 00416 /* There is a word */ 00417 else if (sscanf(line, "%s", result2) == 1) 00418 { 00419 for (i = 0; i < MAXLINE; i++) 00420 { 00421 /* Attempt to convert the accentuated characters in uppercase */ 00422 if ( ((unsigned char)result2[i] >= 192) && ((unsigned char)result2[i] <= 223) ) 00423 { 00424 result2[i] = (unsigned char)result2[i] + 32; 00425 } 00426 /* Convert the word to uppercase */ 00427 else 00428 { 00429 result2[i] = (isupper ((unsigned char)result2[i])? tolower ((unsigned char)result2[i]) : result2[i]); 00430 } 00431 } 00432 00433 /* Write the converted word to line */ 00434 snprintf(line, sizeof(line), "%s\n", result2); 00435 } 00436 00437 /* Get the length of line */ 00438 n = strlen(line); 00439 /* Write line to socket */ 00440 Writen(sockfd, line, n); 00441 } 00442 } 00443 00448 /* Handle numeric requests */ 00449 void str_serv2(int sockfd) 00450 { 00451 /* Idem str_serv1 */ 00452 int arg1, arg2, i; 00453 int result; 00454 char result2[MAXLINE]; 00455 int digitflag = 0; /* flag for digits */ 00456 ssize_t n; 00457 char line[MAXLINE]; 00458 00459 for ( ; ; ) 00460 { 00461 if ( (n = Readline(sockfd, line, MAXLINE)) == 0) 00462 { 00463 return; /* connection closed by other end */ 00464 } 00465 00466 if (sscanf(line, "%d%d", &arg1, &arg2) == 2) 00467 { 00468 result = (int)(pow((float)arg1, 2.) + pow((float)arg2, 2.)); 00469 00470 if ((result == INT_MAX) || (result == INT_MIN)) 00471 { 00472 strcpy(result2, "Overflow"); 00473 snprintf(line, sizeof(line), "%s\n", result2); 00474 } 00475 else 00476 { 00477 snprintf(line, sizeof(line), "%d\n", result); 00478 } 00479 } 00480 else if (sscanf(line, "%d", &arg1) == 1) 00481 { 00482 result = (int)pow((float)arg1, 3.); 00483 00484 if ((result == INT_MAX) || (result == INT_MIN)) 00485 { 00486 strcpy(result2, "Overflow"); 00487 snprintf(line, sizeof(line), "%s\n", result2); 00488 } 00489 else 00490 { 00491 snprintf(line, sizeof(line), "%d\n", result); 00492 } 00493 } 00494 else if (sscanf(line, "%s", result2) == 1) 00495 { 00496 /* Set the flag for digits */ 00497 digitflag = 0; 00498 00499 for (i = 0; i < MAXLINE; i++) 00500 { 00501 /* There is a non digit character */ 00502 if ( isdigit((unsigned char)result2[i]) == 0) 00503 { 00504 digitflag +=1; 00505 } 00506 } 00507 00508 /* Write a message to line */ 00509 if (digitflag > 0) 00510 { 00511 strcpy(result2, "This server only deals with numbers."); 00512 snprintf(line, sizeof(line), "%s\n", result2); 00513 } 00514 } 00515 00516 n = strlen(line); 00517 Writen(sockfd, line, n); 00518 } 00519 } 00520 00525 /* Handle word requests */ 00526 void str_serv3(int sockfd) 00527 { 00528 /* Idem str_serv1 */ 00529 int arg1, i; 00530 char result2[MAXLINE]; 00531 ssize_t n; 00532 char line[MAXLINE]; 00533 00534 for ( ; ; ) 00535 { 00536 if ( (n = Readline(sockfd, line, MAXLINE)) == 0) 00537 { 00538 return; /* connection closed by other end */ 00539 } 00540 00541 /* There is one digit */ 00542 if (sscanf(line, "%d", &arg1) == 1) 00543 { 00544 /* Write a message to result 2 */ 00545 strcpy(result2, "This server only deals with words."); 00546 } 00547 else if (sscanf(line, "%s", result2) == 1) 00548 { 00549 for (i = 0; i < MAXLINE; i++) 00550 { 00551 if ( ((unsigned char)result2[i] >= 192) && ((unsigned char)result2[i] <= 223) ) 00552 { 00553 result2[i] = (unsigned char)result2[i] + 32; 00554 } 00555 else 00556 { 00557 result2[i] = (isupper ((unsigned char)result2[i])? tolower ((unsigned char)result2[i]) : result2[i]); 00558 } 00559 } 00560 00561 snprintf(line, sizeof(line), "%s\n", result2); 00562 } 00563 00564 n = strlen(line); 00565 Writen(sockfd, line, n); 00566 } 00567 } 00568 00576 /* Create a socket */ 00577 int Socket(int family, int type, int protocol) 00578 { 00579 int n; /* the socket to return */ 00580 00581 if ( (n = socket(family, type, protocol)) < 0) 00582 { 00583 err_sys("socket error"); 00584 } 00585 00586 return n; 00587 } 00588 00596 /* Wrapper for fgets */ 00597 char * Fgets(char *ptr, int n, FILE *stream) 00598 { 00599 char *rptr; 00600 00601 if ( (rptr = fgets(ptr, n, stream)) == NULL && ferror(stream)) 00602 { 00603 err_sys("fgets error"); 00604 } 00605 00606 return rptr; 00607 } 00608 00616 /* Read a string character after character from a stream */ 00617 ssize_t mg_readline(int fd, void *vptr, size_t maxlen) 00618 { 00619 int n, rc; 00620 char c, *ptr; 00621 00622 ptr = vptr; 00623 for (n = 1; n < maxlen; n++) 00624 { 00625 if ( (rc = mg_read(fd, &c)) == 1) 00626 { 00627 *ptr++ = c; 00628 00629 if (c == '\n') 00630 { 00631 break; /* newline is stored, like fgets() */ 00632 } 00633 } 00634 else if (rc == 0) 00635 { 00636 if (n == 1) 00637 { 00638 return 0; /* EOF, no data read */ 00639 } 00640 else 00641 { 00642 break; /* EOF, some data was read */ 00643 } 00644 } 00645 else 00646 { 00647 return -1; /* error, errno set by read() */ 00648 } 00649 } 00650 00651 *ptr = 0; /* null terminate like fgets() */ 00652 return n; /* return the number of bytes read */ 00653 } 00654 00662 /* Wrapper for mg_readline */ 00663 ssize_t Readline(int fd, void *ptr, size_t maxlen) 00664 { 00665 ssize_t n; 00666 00667 if ( (n = mg_readline(fd, ptr, maxlen)) < 0) 00668 { 00669 err_sys("mg_readline error"); 00670 } 00671 00672 return n; 00673 } 00674 00680 /* Wrapper for fputs */ 00681 void Fputs(const char *ptr, FILE *stream) 00682 { 00683 if (fputs(ptr, stream) == EOF) 00684 { 00685 err_sys("fputs error"); 00686 } 00687 } 00688 00696 /* Write a string character after character to a stream */ 00697 ssize_t writen(int fd, const void *vptr, size_t n) 00698 { 00699 size_t nleft; 00700 ssize_t nwritten; 00701 const char *ptr; 00702 00703 ptr = vptr; 00704 nleft = n; 00705 while (nleft > 0) 00706 { 00707 if ( (nwritten = write(fd, ptr, nleft)) <= 0) 00708 { 00709 if (errno == EINTR) 00710 { 00711 nwritten = 0; /* and call write() again */ 00712 } 00713 else 00714 { 00715 return -1; /* error */ 00716 } 00717 } 00718 00719 nleft -= nwritten; 00720 ptr += nwritten; 00721 } 00722 00723 return n; /* the number of bytes written */ 00724 } 00725 00732 /* Wrapper for writen */ 00733 void Writen(int fd, void *ptr, size_t nbytes) 00734 { 00735 if (writen(fd, ptr, nbytes) != nbytes) 00736 { 00737 err_sys("writen error"); 00738 } 00739 } 00740 00750 /* Wait for read and write descriptors ready */ 00751 int Select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout) 00752 { 00753 int n; 00754 00755 if ( (n = select(nfds, readfds, writefds, exceptfds, timeout)) < 0) 00756 { 00757 err_sys("select error"); 00758 } 00759 00760 return n; /* can return 0 on timeout */ 00761 } 00762 00768 /* Initiate a normal termination sequence */ 00769 void Shutdown(int fd, int how) 00770 { 00771 /* Make a half-close on write end */ 00772 if (shutdown(fd, how) < 0) 00773 { 00774 err_sys("shutdown error"); 00775 } 00776 } 00777 00782 /* Action to take when child terminates */ 00783 void sig_chld(int signo) 00784 { 00785 pid_t pid; 00786 int stat; 00787 00788 /* Wait for the child to terminate */ 00789 while ( (pid = waitpid(-1, &stat, WNOHANG)) > 0) 00790 { 00791 printf("child %d terminated\n", pid); 00792 } 00793 00794 return; 00795 } 00796 00803 /* Handle signals */ 00804 Sigfunc * mg_signal(int signo, Sigfunc *func) 00805 { 00806 struct sigaction act, oact; 00807 00808 /* Set handler to func */ 00809 act.sa_handler = func; 00810 /* No other signal is blocked */ 00811 sigemptyset(&act.sa_mask); 00812 /* Clean up options */ 00813 act.sa_flags = 0; 00814 00815 /* If signal if SIGALARM, allow the kernel to restart it */ 00816 if (signo == SIGALRM) 00817 { 00818 act.sa_flags |= SA_RESTART; 00819 } 00820 00821 /* Call sigaction */ 00822 if (sigaction(signo, &act, &oact) < 0) 00823 { 00824 return SIG_ERR; 00825 } 00826 00827 return oact.sa_handler; 00828 } 00829 00834 /* Handle errors generated by the system */ 00835 void err_sys(const char *fmt, ...) 00836 { 00837 /* Use the variable-length argument list facility */ 00838 va_list ap; 00839 00840 va_start(ap, fmt); 00841 /* Write an error message to stderr or to console and quit */ 00842 err_doit(1, LOG_ERR, fmt, ap); 00843 va_end(ap); 00844 00845 exit(EXIT_FAILURE); 00846 } 00847 00852 /* Handle errors not generated by the system, 00853 i.e. there was no errno generated by the caller */ 00854 void err_quit(const char *fmt, ...) 00855 { 00856 /* Use the variable-length argument list facility */ 00857 va_list ap; 00858 00859 va_start(ap, fmt); 00860 /* Write an error message to stderr or to console and quit */ 00861 err_doit(0, LOG_ERR, fmt, ap); 00862 va_end(ap); 00863 00864 exit(EXIT_FAILURE); 00865 }