/* * $Id: srv2.c,v 1.3 2006/02/16 19:19:40 ca Exp $ */ #include "sm/generic.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "st.h" #include "error.h" #include #if !HAVE_SNPRINTF # define snprintf sm_snprintf # include "sm/string.h" #endif /* !HAVE_SNPRINTF */ /* Default server port */ #define SERV_PORT_DEFAULT 8000 #define REPS 10 #define REPS 10 #define IOBUFSIZE (16*1024) /* Socket listen queue size */ #define LISTENQ_SIZE_DEFAULT 256 /* Request read timeout (in seconds) */ #define SEC2USEC(s) ((s)*1000000LL) #define REQUEST_TIMEOUT SEC2USEC(30) struct socket_info { st_netfd_t nfd; /* Listening socket */ char *addr; /* Bind address */ int port; /* Port */ } srv_socket; /* listening socket */ static int serialize_accept = 0; static int listenq_size = LISTENQ_SIZE_DEFAULT; static int errfd = STDERR_FILENO; static int debug = 0; static int retrd = -1; static int retwr = -1; #ifndef INADDR_NONE #define INADDR_NONE 0xffffffff #endif /******************************************************************/ static void usage(const char *progname) { fprintf(stderr, "Usage: %s -l []\n\n" "Possible options:\n\n" "\t-b : Bind to specified address." "\t-q Set max length of pending connections" " queue.\n" "\t-S Serialize all accept() calls.\n" "\t-h Print this message.\n", progname); exit(1); } /******************************************************************/ static void parse_arguments(int argc, char *argv[]) { extern char *optarg; int opt; char *c; while ((opt = getopt(argc, argv, "b:d:D:p:l:t:u:q:aiShw:")) != -1) { switch (opt) { case 'b': if ((c = strdup(optarg)) == NULL) err_sys_quit(errfd, "ERROR: strdup"); srv_socket.addr = c; break; case 'd': debug = atoi(optarg); break; case 'q': listenq_size = atoi(optarg); if (listenq_size < 1) err_quit(errfd, "ERROR: invalid listen queue size: %s", optarg); break; case 'S': serialize_accept = 1; break; case 'h': case '?': usage(argv[0]); } } } /******************************************************************/ static void create_listeners(void) { int n, sock; char *c; struct sockaddr_in serv_addr; struct hostent *hp; short port; port = 0; if (srv_socket.addr != NULL && (c = strchr(srv_socket.addr, ':')) != NULL) { *c++ = '\0'; port = (short) atoi(c); } if (srv_socket.addr == NULL || srv_socket.addr[0] == '\0') srv_socket.addr = "0.0.0.0"; if (port == 0) port = SERV_PORT_DEFAULT; /* Create server socket */ if ((sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) err_sys_quit(errfd, "ERROR: can't create socket: socket"); n = 1; if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&n, sizeof(n)) < 0) err_sys_quit(errfd, "ERROR: can't set SO_REUSEADDR: setsockopt"); memset(&serv_addr, 0, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(port); serv_addr.sin_addr.s_addr = inet_addr(srv_socket.addr); if (serv_addr.sin_addr.s_addr == INADDR_NONE) { /* not dotted-decimal */ if ((hp = gethostbyname(srv_socket.addr)) == NULL) err_quit(errfd, "ERROR: can't resolve address: %s", srv_socket.addr); memcpy(&serv_addr.sin_addr, hp->h_addr, hp->h_length); } srv_socket.port = port; /* Do bind and listen */ if (bind(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) err_sys_quit(errfd, "ERROR: can't bind to address %s, port %d", srv_socket.addr, port); if (listen(sock, listenq_size) < 0) err_sys_quit(errfd, "ERROR: listen"); /* Create file descriptor object from OS socket */ if ((srv_socket.nfd = st_netfd_open_socket(sock)) == NULL) err_sys_quit(errfd, "ERROR: st_netfd_open_socket"); if (serialize_accept && st_netfd_serialize_accept(srv_socket.nfd) < 0) err_sys_quit(errfd, "ERROR: st_netfd_serialize_accept"); } /******************************************************************/ static void * wrsrv(void *arg) { int n, r, l; st_netfd_t rmt_nfd; char buf[IOBUFSIZE]; rmt_nfd = (st_netfd_t) arg; for (n = 0; n < REPS; n++) { if (n < REPS - 1) snprintf(buf, sizeof(buf), "SRV %d\n", n); else snprintf(buf, sizeof(buf), "QUIT %d\n", n); l = strlen(buf); r = st_write(rmt_nfd, buf, l, REQUEST_TIMEOUT); if (r != l) { fprintf(stderr, "error write s='%s' n=%d, l=%d, r=%d, errno=%d\n", buf, n, l, r, errno); retwr = -1; return (void *) &retwr; } st_usleep(1); } retwr = 1; return (void *) &retwr; } static void * rdsrv(void *arg) { int n; st_netfd_t rmt_nfd; char buf[IOBUFSIZE]; st_utime_t t1, t2; rmt_nfd = (st_netfd_t) arg; while (1) { t1 = st_utime(); n = st_read(rmt_nfd, buf, IOBUFSIZE, REQUEST_TIMEOUT); t2 = st_utime(); if (n == 0) { retrd = 0; return (void *) &retrd; } if (n < 0) { fprintf(stderr, "error read n=%d, errno=%d\n", n, errno); fprintf(stderr, "error read t1=%llu, t2=%llu, t2-t1=%llu\n", t1, t2, t2-t1); goto fail; } if (debug > 3) { fprintf(stderr, "rcvd: \""); write(STDERR_FILENO, buf, n); fprintf(stderr, "\"\n"); } while (n > 0) { if (buf[--n] == 'Q') { retrd = 1; return (void *) &retrd; } } st_usleep(1); } fail: retrd = -1; return (void *) &retrd; } static void * handle_conn(void *arg) { int fromlen, r, *rv; st_netfd_t srv_nfd, cli_nfd; st_thread_t thr1; struct sockaddr_in from; srv_nfd = srv_socket.nfd; fromlen = sizeof(from); while (1) { cli_nfd = st_accept(srv_nfd, (struct sockaddr *)&from, &fromlen, -1); if (NULL == cli_nfd) { err_sys_report(errfd, "ERROR: can't accept connection: st_accept"); break; } /* Save peer address, so we can retrieve it later */ st_netfd_setspecific(cli_nfd, &from.sin_addr, NULL); thr1 = st_thread_create(rdsrv, (void *) cli_nfd, 1, 0); if (NULL == thr1) { err_sys_report(errfd, "st_thread_create"); exit(1); } wrsrv((void *)cli_nfd); r = st_thread_join(thr1, (void **) &rv); st_netfd_close(cli_nfd); if (*rv == -1) break; } return NULL; } int main(int argc, char *argv[]) { if (getuid() == 0 || geteuid() == 0) { err_report(errfd, "WARNING: running as super-user!"); exit(1); } /* Parse command-line options */ parse_arguments(argc, argv); /* Initialize the ST library */ if (st_init() < 0) err_sys_quit(errfd, "ERROR: initialization failed: st_init"); /* Create listening sockets */ create_listeners(); /* Turn time caching on */ /* st_timecache_set(1); */ handle_conn((void *)1); return 0; }