From owner-svn-src-projects@FreeBSD.ORG Mon Aug 9 22:29:31 2010 Return-Path: Delivered-To: svn-src-projects@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id B8935106564A; Mon, 9 Aug 2010 22:29:31 +0000 (UTC) (envelope-from attilio@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:4f8:fff6::2c]) by mx1.freebsd.org (Postfix) with ESMTP id A65788FC13; Mon, 9 Aug 2010 22:29:31 +0000 (UTC) Received: from svn.freebsd.org (localhost [127.0.0.1]) by svn.freebsd.org (8.14.3/8.14.3) with ESMTP id o79MTV3L025510; Mon, 9 Aug 2010 22:29:31 GMT (envelope-from attilio@svn.freebsd.org) Received: (from attilio@localhost) by svn.freebsd.org (8.14.3/8.14.3/Submit) id o79MTVvn025508; Mon, 9 Aug 2010 22:29:31 GMT (envelope-from attilio@svn.freebsd.org) Message-Id: <201008092229.o79MTVvn025508@svn.freebsd.org> From: Attilio Rao Date: Mon, 9 Aug 2010 22:29:31 +0000 (UTC) To: src-committers@freebsd.org, svn-src-projects@freebsd.org X-SVN-Group: projects MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Subject: svn commit: r211124 - projects/sv/usr.sbin/netdumpsrv X-BeenThere: svn-src-projects@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: "SVN commit messages for the src " projects" tree" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 09 Aug 2010 22:29:31 -0000 Author: attilio Date: Mon Aug 9 22:29:31 2010 New Revision: 211124 URL: http://svn.freebsd.org/changeset/base/211124 Log: Add the server implementation of netdumps. Added: projects/sv/usr.sbin/netdumpsrv/ projects/sv/usr.sbin/netdumpsrv/netdump_server.c (contents, props changed) Added: projects/sv/usr.sbin/netdumpsrv/netdump_server.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ projects/sv/usr.sbin/netdumpsrv/netdump_server.c Mon Aug 9 22:29:31 2010 (r211124) @@ -0,0 +1,887 @@ +/*- + * Copyright (c) 2005-2006 Sandvine Incorporated. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* How many dumps to allow per IP before they need to be cleaned out */ +#define MAX_DUMPS 256 +/* Clients time out after two minutes */ +#define CLIENT_TIMEOUT 120 +/* Host name length (keep at least as big as INET_ADDRSTRLEN) */ +#define MAXHOSTNAMELEN 256 + +struct netdump_client +{ + char infofilename[MAXPATHLEN]; + char corefilename[MAXPATHLEN]; + char hostname[MAXHOSTNAMELEN]; + struct in_addr ip; + FILE *infofile; + int corefd; + int sock; + time_t last_msg; + unsigned int printed_port_warning : 1; + unsigned int any_data_rcvd : 1; + + struct netdump_client *next; +}; + +struct netdump_client *clients; +char dumpdir[MAXPATHLEN]; +char *handler_script=NULL; +time_t now; +int do_shutdown; +struct in_addr bindip; +struct pidfh *pfh; +int sock; + +char * client_ipstr(struct netdump_client *client) +{ + return addr2ascii(AF_INET, &client->ip, sizeof(client->ip), NULL); +} + +struct netdump_client * alloc_client(struct in_addr *ip) +{ + struct sockaddr_in saddr; + struct netdump_client *client; + struct hostent *hp; + int i, fd, bufsz; + + client = malloc(sizeof(*client)); + if (!client) + { + perror("malloc"); + return NULL; + } + bzero(client, sizeof(*client)); + bcopy(ip, &client->ip, sizeof(*ip)); + client->corefd = -1; + client->sock = -1; + client->last_msg = now; + + /* Get the hostname */ + if ((hp = gethostbyaddr((const char *)ip, sizeof(*ip), AF_INET)) == NULL || + !hp->h_name || strlen(hp->h_name) == 0) + { + /* Can't resolve; use IP */ + addr2ascii(AF_INET, ip, sizeof(*ip), client->hostname); + } + else + { + char *firstdot; + + /* Grab the hostname */ + strncpy(client->hostname, hp->h_name, MAXHOSTNAMELEN); + hp->h_name[MAXHOSTNAMELEN-1]='\0'; + + /* Strip off the domain name */ + firstdot = strchr(client->hostname, '.'); + if (firstdot) + { + *firstdot='\0'; + } + } + + /* Set up the client socket */ + if ((client->sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) + { + perror("socket"); + free(client); + return NULL; + } + if (fcntl(client->sock, F_SETFL, O_NONBLOCK) == -1) { + perror("fcntl(client->sock, F_SETFL, O_NONBLOCK)"); + close(client->sock); + free(client); + return NULL; + } + bzero(&saddr, sizeof(saddr)); + saddr.sin_len = sizeof(saddr); + saddr.sin_family = AF_INET; + saddr.sin_addr.s_addr = bindip.s_addr; + saddr.sin_port = htons(0); + if (bind(client->sock, (struct sockaddr *)&saddr, sizeof(saddr))) { + perror("bind"); + close(client->sock); + free(client); + return NULL; + } + bzero(&saddr, sizeof(saddr)); + saddr.sin_len = sizeof(saddr); + saddr.sin_family = AF_INET; + saddr.sin_addr.s_addr = ip->s_addr; + saddr.sin_port = htons(NETDUMP_ACKPORT); + if (connect(client->sock, (struct sockaddr *)&saddr, sizeof(saddr))) { + perror("connect"); + close(client->sock); + free(client); + return NULL; + } + bufsz=131072; /* Enough to hold approx twice the chunk size. Should be + * plenty for any 1 client. */ + if (setsockopt(client->sock, SOL_SOCKET, SO_RCVBUF, &bufsz, sizeof(bufsz))) + { + perror("setsockopt(SOL_SOCKET, SO_RCVBUF)"); + fprintf(stderr, "Warning: May drop packets from %s due to small receive" + " buffer\n", client->hostname); + } + + /* Try info.host.0 through info.host.255 in sequence */ + for (i=0; i < MAX_DUMPS; i++) + { + snprintf(client->infofilename, sizeof(client->infofilename), + "%s/info.%s.%d", dumpdir, client->hostname, i); + snprintf(client->corefilename, sizeof(client->corefilename), + "%s/vmcore.%s.%d", dumpdir, client->hostname, i); + + /* Try the info file first */ + if ((fd = open(client->infofilename, O_WRONLY|O_CREAT|O_EXCL, + DEFFILEMODE)) == -1) + { + if (errno == EEXIST) + { + continue; + } + + fprintf(stderr, "open(\"%s\"): %s\n", client->infofilename, + strerror(errno)); + continue; + } + if (!(client->infofile = fdopen(fd, "w"))) + { + perror("fdopen"); + close(fd); + unlink(client->infofilename); + continue; + } + + /* Next make the core file */ + if ((fd = open(client->corefilename, O_RDWR|O_CREAT|O_EXCL, + DEFFILEMODE)) == -1) + { + /* Failed. Keep the numbers in sync. */ + fclose(client->infofile); + unlink(client->infofilename); + client->infofile = NULL; + + if (errno == EEXIST) + { + continue; + } + + fprintf(stderr, "open(\"%s\"): %s\n", client->corefilename, + strerror(errno)); + continue; + } + client->corefd = fd; + break; + } + + if (!client->infofile || client->corefd == -1) + { + fprintf(stderr, "Can't create output files for new client %s [%s]\n", + client->hostname, client_ipstr(client)); + if (client->infofile) + { + fclose(client->infofile); + } + if (client->corefd != -1) + { + close(client->corefd); + } + if (client->sock != -1) + { + close(client->sock); + } + free(client); + return NULL; + } + + client->next = clients; + clients = client; + return client; +} + +void free_client(struct netdump_client *client) +{ + /* Remove from the list */ + if (clients == client) + { + clients = client->next; + } + else + { + struct netdump_client *parent; + for (parent=clients; parent; parent = parent->next) + { + if (parent->next == client) + { + break; + } + } + parent->next = client->next; + } + + fclose(client->infofile); + close(client->corefd); + close(client->sock); + free(client); +} + +void exec_handler(struct netdump_client *client, const char *reason) +{ + int pid=fork(); + + if (pid == -1) + { + perror("fork"); + return; + } + else if (pid) + { + return; + } + else + { + close(sock); + pidfile_close(pfh); + execl(handler_script, handler_script, reason, client_ipstr(client), + client->hostname, client->infofilename, client->corefilename, + NULL); + perror("execl"); + _exit(1); + } +} + +void handle_timeout(struct netdump_client *client) +{ + printf("Client %s timed out\n", client_ipstr(client)); + fputs("Dump incomplete: client timed out\n", client->infofile); + exec_handler(client, "timeout"); + free_client(client); +} + +void timeout_clients() +{ + static time_t last_timeout_check; + struct netdump_client **node; + + /* Only time out clients every 10 seconds */ + if (now - last_timeout_check < 10) + { + return; + } + + last_timeout_check = now; + + /* Traverse the list looking for stale clients */ + for (node=&clients; *node; node = &(*node)->next) + { + while (*node && (*node)->last_msg+CLIENT_TIMEOUT < now) + { + handle_timeout(*node); + } + + if (*node == NULL) + { + break; + } + } +} + +void send_ack(struct netdump_client *client, struct netdump_msg *msg) +{ + struct netdump_ack ack; + int tryagain; + + bzero(&ack, sizeof(ack)); + ack.seqno = htonl(msg->hdr.seqno); + + do + { + tryagain=0; + + if (send(client->sock, &ack, sizeof(ack), 0) == -1) + { + if (errno == EINTR) + { + tryagain=1; + continue; + } + + /* XXX: On EAGAIN, we should probably queue the packet to be sent + * when the socket is writable... but that's too much effort, since + * it's mostly harmless to wait for the client to retransmit. */ + perror("sendto"); + } + } + while (tryagain); +} + +int handle_herald(struct sockaddr_in *from, struct netdump_client *client, + struct netdump_msg *msg) +{ + int freed_client=0; + + if (client) + { + if (!client->any_data_rcvd) + { + /* Must be a retransmit of the herald packet. */ + send_ack(client, msg); + return 0; + } + + /* An old connection must have timed out. Clean it up first */ + handle_timeout(client); + freed_client=1; + } + + client = alloc_client(&from->sin_addr); + + if (!client) + { + /* alloc_client would have printed an error message already */ + return freed_client; + } + + fprintf(client->infofile, "Dump from %s [%s]\n", client->hostname, + client_ipstr(client)); + + printf("New dump from client %s [%s] (to %s)\n", client->hostname, + client_ipstr(client), client->corefilename); + + send_ack(client, msg); + + return freed_client; +} + +int handle_kdh(struct netdump_client *client, struct netdump_msg *msg) +{ + struct kerneldumpheader *h=(void *)msg->data; + uint64_t dumplen; + FILE *f; + time_t t; + int parity_check; + + if (!client) + { + return 0; + } + + client->any_data_rcvd = 1; + + f = client->infofile; + + if (msg->hdr.len < sizeof(struct kerneldumpheader)) + { + fprintf(stderr, "Bad KDH from %s [%s]: packet too small\n", + client->hostname, client_ipstr(client)); + fputs("Bad KDH: packet too small\n", f); + fflush(f); + send_ack(client, msg); + + return 0; + } + + parity_check = kerneldump_parity(h); + + /* Make sure we null terminate all the strings */ + h->architecture[sizeof(h->architecture)-1] = '\0'; + h->hostname[sizeof(h->hostname)-1] = '\0'; + h->versionstring[sizeof(h->versionstring)-1] = '\0'; + h->panicstring[sizeof(h->panicstring)-1] = '\0'; + + fprintf(f, " Architecture: %s\n", h->architecture); + fprintf(f, " Architecture version: %d\n", dtoh32(h->architectureversion)); + dumplen = dtoh64(h->dumplength); + fprintf(f, " Dump length: %lldB (%lld MB)\n", (long long)dumplen, + (long long)(dumplen >> 20)); + fprintf(f, " Blocksize: %d\n", dtoh32(h->blocksize)); + t = dtoh64(h->dumptime); + fprintf(f, " Dumptime: %s", ctime(&t)); + fprintf(f, " Hostname: %s\n", h->hostname); + fprintf(f, " Versionstring: %s", h->versionstring); + fprintf(f, " Panicstring: %s\n", h->panicstring); + fprintf(f, " Header parity check: %s\n", parity_check ? "Fail" : "Pass"); + fflush(f); + + fprintf(stdout, "(KDH from %s [%s])", client->hostname, + client_ipstr(client)); + fflush(stdout); + + send_ack(client, msg); + + return 0; +} + +int handle_vmcore(struct netdump_client *client, struct netdump_msg *msg) +{ + if (!client) + { + return 0; + } + + client->any_data_rcvd = 1; + + if (msg->hdr.seqno % 11523 == 0) + { + /* Approximately every 16MB with MTU of 1500 */ + putc('.', stdout); + fflush(stdout); + } + + if (pwrite(client->corefd, msg->data, msg->hdr.len, msg->hdr.offset) == -1) + { + fprintf(stderr, "pwrite (for client %s [%s]): %s\n", client->hostname, + client_ipstr(client), strerror(errno)); + fprintf(client->infofile, "Dump unsuccessful: write error at offset %08"PRIx64": %s\n", + msg->hdr.offset, strerror(errno)); + exec_handler(client, "error"); + free_client(client); + return 1; + } + + send_ack(client, msg); + + return 0; +} + +int handle_finish(struct netdump_client *client, struct netdump_msg *msg) +{ + if (!client) + { + return 0; + } + + + printf("\nCompleted dump from client %s [%s]\n", client->hostname, + client_ipstr(client)); + fflush(stdout); + fputs("Dump complete\n", client->infofile); + + /* Do this before we free the client */ + send_ack(client, msg); + + exec_handler(client, "success"); + free_client(client); + + return 1; +} + + +int receive_message(int sock, struct sockaddr_in *from, char *fromstr, + size_t fromstrlen, struct netdump_msg *msg) +{ + socklen_t fromlen; + ssize_t len; + + bzero(from, sizeof(*from)); + from->sin_family = AF_INET; + from->sin_len = fromlen = sizeof(*from); + from->sin_port = 0; + from->sin_addr.s_addr = INADDR_ANY; + + if ((len = recvfrom(sock, msg, sizeof(*msg), 0, + (struct sockaddr *)from, &fromlen)) == -1) + { + /* The caller can use errno to find the error */ + return -1; + } + + snprintf(fromstr, fromstrlen, "%s:%hu", + addr2ascii(AF_INET, &from->sin_addr, sizeof(from->sin_addr), NULL), + ntohs(from->sin_port)); + + if ((size_t)len < sizeof(struct netdump_msg_hdr)) + { + fprintf(stderr, "Ignoring runt packet from %s (got %zu)\n", fromstr, + (size_t)len); + return 0; + } + + /* Convert byte order */ + msg->hdr.type = ntohl(msg->hdr.type); + msg->hdr.seqno = ntohl(msg->hdr.seqno); + msg->hdr.offset = ntohll(msg->hdr.offset); + msg->hdr.len = ntohl(msg->hdr.len); + + if ((size_t)len < sizeof(struct netdump_msg_hdr) + msg->hdr.len) + { + fprintf(stderr, "Packet too small from %s (got %zu, expected %zu)\n", + fromstr, (size_t)len, sizeof(struct netdump_msg_hdr) + msg->hdr.len); + return 0; + } + + return len; +} + +int handle_packet(struct netdump_client *client, struct sockaddr_in *from, + const char *fromstr, struct netdump_msg *msg) +{ + int freed_client; + + if (client) + { + client->last_msg = time(NULL); + } + + switch (msg->hdr.type) + { + case NETDUMP_HERALD: + freed_client = handle_herald(from, client, msg); + break; + case NETDUMP_KDH: + freed_client = handle_kdh(client, msg); + break; + case NETDUMP_VMCORE: + freed_client = handle_vmcore(client, msg); + break; + case NETDUMP_FINISHED: + freed_client = handle_finish(client, msg); + break; + default: + freed_client=0; + fprintf(stderr, "Received unknown message type %d from %s\n", + msg->hdr.type, fromstr); + } + + return freed_client; +} + +void eventloop() +{ + struct netdump_msg msg; + + while (!do_shutdown) + { + fd_set readfds; + int maxfd=sock+1; + struct netdump_client *client; + struct timeval tv; + struct sockaddr_in from; + char fromstr[INET_ADDRSTRLEN+6]; /* Long enough for IP+':'+port+'\0' */ + + FD_ZERO(&readfds); + FD_SET(sock, &readfds); + for (client=clients; client; client = client->next) + { + FD_SET(client->sock, &readfds); + if (maxfd <= client->sock) + { + maxfd = client->sock+1; + } + } + + /* So that we time out clients regularly */ + tv.tv_sec = 10; + tv.tv_usec = 0; + + if (select(maxfd, &readfds, NULL, NULL, &tv) == -1) + { + if (errno == EINTR) + { + continue; + } + + perror("select"); + /* Errors with select() probably won't go away if we just try to + * select() again */ + exit(1); + } + + now = time(NULL); + + if (FD_ISSET(sock, &readfds)) { + int len; + + len = receive_message(sock, &from, fromstr, sizeof(fromstr), &msg); + + if (len == -1) + { + if (errno == EINTR) + { + continue; + } + + if (errno != EAGAIN) + { + /* Server socket is broken for some reason */ + perror("recvfrom"); + exit(1); + } + } + else if (len == 0) + { + /* The packet was rejected (probably because it was too small). + * Just ignore it. */ + } + else + { + /* Check if they're on the clients list */ + for (client=clients; client; client = client->next) + { + if (client->ip.s_addr == from.sin_addr.s_addr) + { + break; + } + } + + /* Technically, clients shouldn't be responding on the server + * port, so client should be NULL, however, if they insist on + * doing so, it's not really going to hurt anything (except + * maybe fill up the server socket's receive buffer), so still + * accept it. The only possibly legitimate case is if there's + * a new dump starting and the previous one didn't finish + * cleanly. Handle this by suppressing the error on HERALD + * packets. + */ + if (client && msg.hdr.type != NETDUMP_HERALD && + !client->printed_port_warning) + { + printf("Warning: Client %s responding on server port\n", + client->hostname); + client->printed_port_warning = 1; + } + + handle_packet(client, &from, fromstr, &msg); + } + } + + for (client=clients; client; client = client->next) + { + if (FD_ISSET(client->sock, &readfds)) + { + int len = receive_message(client->sock, &from, fromstr, + sizeof(fromstr), &msg); + if (len == -1) + { + if (errno == EINTR || errno == EAGAIN) + { + continue; + } + + perror("recvfrom"); + /* Client socket is broken for some reason */ + handle_timeout(client); + /* The client pointer is now invalid */ + client = clients; + if (!client) + { + break; + } + } + else if (len == 0) + { + /* The packet was rejected (probably because it was too + * small). Just ignore it. */ + } + else + { + FD_CLR(client->sock, &readfds); + if (handle_packet(client, &from, fromstr, &msg)) + { + /* Client was freed; we have a stale pointer */ + client = clients; + if (!client) + { + break; + } + } + } + } + } + + timeout_clients(); + } + + puts("Shutting down..."); + /* Clients is the head of the list, so clients != NULL iff the list isn't + * empty. Call it a timeout so that the scripts get run. */ + while (clients) + { + handle_timeout(clients); + } +} + +void signal_shutdown(int sig) +{ + do_shutdown=1; +} + +int main(int argc, char **argv) +{ + struct stat statbuf; + struct sockaddr_in bindaddr; + struct sigaction sa; + + pfh = pidfile_open(NULL, 0600, NULL); + if (pfh == NULL) { + if (errno == EEXIST) + printf("Instance of netdump already running\n"); + else + printf("Impossible to open the pid file\n"); + exit(1); + } + pidfile_write(pfh); + + /* Check argc and set the bindaddr and handler_script */ + switch (argc) + { + case 4: + handler_script = strdup(argv[3]); + if (access(handler_script, F_OK|X_OK)) + { + pidfile_remove(pfh); + fputs("Warning: may be unable to execute handler script\n", + stderr); + free(handler_script); + return 1; + } + case 3: + if (ascii2addr(AF_INET, argv[2], &bindip) == -1) + { + pidfile_remove(pfh); + fputs("Invalid bind IP specified\n", stderr); + return 1; + } + printf("Listening on IP %s\n", + addr2ascii(AF_INET, &bindip, + sizeof(bindip), NULL)); + break; + case 2: + bindip.s_addr = INADDR_ANY; + puts("Listening on all interfaces"); + break; + default: + pidfile_remove(pfh); + fprintf(stderr, "Usage: %s " + "[bind IP [handler script]]\n", argv[0]); + return 1; + } + + /* Check dump location for sanity */ + if (stat(argv[1], &statbuf)) + { + pidfile_remove(pfh); + perror("stat"); + fputs("Invalid dump location specified\n", stderr); + return 1; + } + if ((statbuf.st_mode & S_IFMT) != S_IFDIR) + { + pidfile_remove(pfh); + fputs("Dump location is not a directory\n", stderr); + return 1; + } + if (access(argv[1], F_OK|W_OK)) + { + fprintf(stderr, "Warning: May be unable to write into dump " + "location: %s\n", strerror(errno)); + } + strncpy(dumpdir, argv[1], sizeof(dumpdir)-1); + dumpdir[sizeof(dumpdir)-1]='\0'; + + /* Set up the server socket */ + if ((sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) + { + pidfile_remove(pfh); + perror("socket"); + return 1; + } + bzero(&bindaddr, sizeof(bindaddr)); + bindaddr.sin_len = sizeof(bindaddr); + bindaddr.sin_family = AF_INET; + bindaddr.sin_addr.s_addr = bindip.s_addr; + bindaddr.sin_port = htons(NETDUMP_PORT); + if (bind(sock, (struct sockaddr *)&bindaddr, sizeof(bindaddr))) + { + pidfile_remove(pfh); + perror("bind"); + close(sock); + return 1; + } + if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1) { + pidfile_remove(pfh); + perror("fcntl(sock, F_SETFL, O_NONBLOCK)"); + close(sock); + return 1; + } + + /* Signal handlers */ + bzero(&sa, sizeof(sa)); + sa.sa_handler = signal_shutdown; + if (sigaction(SIGINT, &sa, NULL) || sigaction(SIGTERM, &sa, NULL)) + { + pidfile_remove(pfh); + perror("sigaction"); + close(sock); + return 1; + } + bzero(&sa, sizeof(sa)); + sa.sa_handler = SIG_IGN; + sa.sa_flags = SA_NOCLDWAIT; + if (sigaction(SIGCHLD, &sa, NULL)) + { + pidfile_remove(pfh); + perror("sigaction"); + close(sock); + return 1; + } + + printf("Waiting for clients.\n"); + fflush(stdout); + + do_shutdown=0; + eventloop(); + + if (handler_script) + { + free(handler_script); + } + pidfile_remove(pfh); + + return 0; +} +