Index: projects/sv/usr.sbin/netdumpsrv/netdump_server.c =================================================================== --- projects/sv/usr.sbin/netdumpsrv/netdump_server.c (revision 211710) +++ projects/sv/usr.sbin/netdumpsrv/netdump_server.c (nonexistent) @@ -1,903 +0,0 @@ -/*- - * 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 -#include -#include - -#define MAX_DUMPS 256 /* Dumps per IP before to be cleaned out. */ -#define CLIENT_TIMEOUT 120 /* Clients timeout (secs). */ -#define CLIENT_TPASS 10 /* Clients timeout pass (secs). */ - -#define PFLAGS_ABIND 0x01 -#define PFLAGS_DDIR 0x02 -#define PFLAGS_DEBUG 0x04 -#define PFLAGS_SCRIPT 0x08 - -#define LOGERR(m, ...) \ - (*phook)(LOG_ERR | LOG_DAEMON, (m), ## __VA_ARGS__) -#define LOGERR_PERROR(m) \ - (*phook)(LOG_ERR | LOG_DAEMON, "%s: %s\n", m, strerror(errno)) -#define LOGINFO(m, ...) \ - (*phook)(LOG_INFO | LOG_DAEMON, (m), ## __VA_ARGS__) -#define LOGWARN(m, ...) \ - (*phook)(LOG_WARNING | LOG_DAEMON, (m), ## __VA_ARGS__) - -#define client_ntoa(cl) \ - inet_ntoa((cl)->ip) -#define client_pinfo(cl, f, ...) \ - fprintf((cl)->infofile, (f), ## __VA_ARGS__) - -struct netdump_client { - char infofilename[MAXPATHLEN]; - char corefilename[MAXPATHLEN]; - char hostname[NI_MAXHOST]; - time_t last_msg; - SLIST_ENTRY(netdump_client) iter; - struct in_addr ip; - FILE *infofile; - int corefd; - int sock; - unsigned short printed_port_warning: 1; - unsigned short any_data_rcvd: 1; -}; - -/* Clients list. */ -static SLIST_HEAD(, netdump_client) clients = SLIST_HEAD_INITIALIZER(clients); - -/* Program arguments handlers. */ -static uint32_t pflags; -static char dumpdir[MAXPATHLEN]; -static char *handler_script; -static struct in_addr bindip; - -/* Miscellaneous handlers. */ -static struct pidfh *pfh; -static time_t now; -static time_t last_timeout_check; -static int do_shutdown; -static int sock; - -/* Daemon print functions hook. */ -static void (*phook)(int, const char *, ...); - -static struct netdump_client *alloc_client(struct sockaddr_in *sip); -static void eventloop(void); -static void exec_handler(struct netdump_client *client, - const char *reason); -static void free_client(struct netdump_client *client); -static void handle_finish(struct netdump_client *client, - struct netdump_msg *msg); -static void handle_herald(struct sockaddr_in *from, - struct netdump_client *client, - struct netdump_msg *msg); -static void handle_kdh(struct netdump_client *client, - struct netdump_msg *msg); -static void handle_packet(struct netdump_client *client, - struct sockaddr_in *from, const char *fromstr, - struct netdump_msg *msg); -static void handle_timeout(struct netdump_client *client); -static void handle_vmcore(struct netdump_client *client, - struct netdump_msg *msg); -static void phook_printf(int priority, const char *message, ...); -static int receive_message(int isock, struct sockaddr_in *from, - char *fromstr, size_t fromstrlen, - struct netdump_msg *msg); -static void send_ack(struct netdump_client *client, - struct netdump_msg *msg); -static void signal_shutdown(int sig __unused); -static void timeout_clients(void); -static void usage(const char *cmd); - -static void -usage(const char *cmd) -{ - - fprintf(stderr, "Usage: %s [-a bind_addr] [-d dump_dir] [-i script]\n", - cmd); -} - -static void -phook_printf(int priority, const char *message, ...) -{ - va_list ap; - - va_start(ap, message); - if ((priority & LOG_INFO) != 0) { - assert((priority & (LOG_WARNING | LOG_ERR)) == 0); - vprintf(message, ap); - } else - vfprintf(stderr, message, ap); -} - -static struct netdump_client * -alloc_client(struct sockaddr_in *sip) -{ - struct sockaddr_in saddr; - struct netdump_client *client; - struct in_addr *ip; - char *firstdot; - int i, ecode, fd, bufsz; - - assert(sip != NULL); - - client = calloc(1, sizeof(*client)); - if (client == NULL) { - LOGERR_PERROR("calloc()"); - return (NULL); - } - ip = &sip->sin_addr; - bcopy(ip, &client->ip, sizeof(*ip)); - client->corefd = -1; - client->sock = -1; - client->last_msg = now; - - ecode = getnameinfo((struct sockaddr *)sip, sip->sin_len, - client->hostname, sizeof(client->hostname), NULL, 0, NI_NAMEREQD); - if (ecode != 0) { - - /* Can't resolve, try with a numeric IP. */ - ecode = getnameinfo((struct sockaddr *)sip, sip->sin_len, - client->hostname, sizeof(client->hostname), NULL, 0, 0); - if (ecode != 0) { - LOGERR("getnameinfo(): %s\n", gai_strerror(ecode)); - free(client); - return (NULL); - } - } else { - - /* Strip off the domain name */ - firstdot = strchr(client->hostname, '.'); - if (firstdot) - *firstdot = '\0'; - } - - client->sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); - if (client->sock == -1) { - LOGERR_PERROR("socket()"); - free(client); - return (NULL); - } - if (fcntl(client->sock, F_SETFL, O_NONBLOCK) == -1) { - LOGERR_PERROR("fcntl()"); - 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))) { - LOGERR_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))) { - LOGERR_PERROR("connect()"); - close(client->sock); - free(client); - return (NULL); - } - - /* It should be enough to hold approximatively twize the chunk size. */ - bufsz = 131072; - if (setsockopt(client->sock, SOL_SOCKET, SO_RCVBUF, &bufsz, - sizeof(bufsz))) { - LOGERR_PERROR("setsockopt()"); - LOGWARN("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. */ - fd = open(client->infofilename, O_WRONLY|O_CREAT|O_EXCL, - DEFFILEMODE); - if (fd == -1) { - if (errno != EEXIST) - LOGERR("open(\"%s\"): %s\n", - client->infofilename, strerror(errno)); - continue; - } - client->infofile = fdopen(fd, "w"); - if (client->infofile == NULL) { - LOGERR_PERROR("fdopen()"); - close(fd); - unlink(client->infofilename); - continue; - } - - /* Next make the core file. */ - fd = open(client->corefilename, O_RDWR|O_CREAT|O_EXCL, - DEFFILEMODE); - if (fd == -1) { - - /* Failed. Keep the numbers in sync. */ - fclose(client->infofile); - unlink(client->infofilename); - client->infofile = NULL; - if (errno != EEXIST) - LOGERR("open(\"%s\"): %s\n", - client->corefilename, strerror(errno)); - continue; - } - client->corefd = fd; - break; - } - - if (client->infofile == NULL || client->corefd == -1) { - LOGERR("Can't create output files for new client %s [%s]\n", - client->hostname, client_ntoa(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); - } - SLIST_INSERT_HEAD(&clients, client, iter); - return (client); -} - -static void -free_client(struct netdump_client *client) -{ - - assert(client != NULL); - - /* Remove from the list. Ignore errors from close() routines. */ - SLIST_REMOVE(&clients, client, netdump_client, iter); - fclose(client->infofile); - close(client->corefd); - close(client->sock); - free(client); -} - -static void -exec_handler(struct netdump_client *client, const char *reason) -{ - int pid; - - assert(client != NULL); - - /* If no script is specified this is a no-op. */ - if ((pflags & PFLAGS_SCRIPT) == 0) - return; - - pid = fork(); - - /* - * The function is invoked in critical conditions, thus just exiting - * without reporting errors is fine. - */ - if (pid == -1) { - LOGERR_PERROR("fork()"); - return; - } else if (pid != 0) { - close(sock); - pidfile_close(pfh); - if (execl(handler_script, handler_script, reason, - client_ntoa(client), client->hostname, - client->infofilename, client->corefilename, NULL) == -1) { - LOGERR_PERROR("fork()"); - _exit(1); - } - } -} - -static void -handle_timeout(struct netdump_client *client) -{ - - assert(client != NULL); - - LOGINFO("Client %s timed out\n", client_ntoa(client)); - client_pinfo(client, "Dump incomplete: client timed out\n"); - exec_handler(client, "timeout"); - free_client(client); -} - -static void -timeout_clients() -{ - struct netdump_client *client, *tmp; - - /* Only time out clients every 10 seconds. */ - if (now - last_timeout_check < CLIENT_TPASS) - return; - last_timeout_check = now; - - /* Traverse the list looking for stale clients. */ - SLIST_FOREACH_SAFE(client, &clients, iter, tmp) - if (client->last_msg + CLIENT_TIMEOUT < now) - handle_timeout(client); -} - -static void -send_ack(struct netdump_client *client, struct netdump_msg *msg) -{ - struct netdump_ack ack; - int tryagain; - - assert(client != NULL && msg != NULL); - - 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 is too much effort, since it is mostly - * harmless to wait for the client to retransmit. - */ - LOGERR_PERROR("send()"); - } - } while (tryagain); -} - -static void -handle_herald(struct sockaddr_in *from, struct netdump_client *client, - struct netdump_msg *msg) -{ - - assert(from != NULL && msg != NULL); - - if (client != NULL) { - if (client->any_data_rcvd == 0) { - - /* Must be a retransmit of the herald packet. */ - send_ack(client, msg); - return; - } - - /* An old connection must have timed out. Clean it up first. */ - handle_timeout(client); - } - - client = alloc_client(from); - if (client == NULL) { - LOGERR("handle_herald(): new client allocation failure\n"); - return; - } - client_pinfo(client, "Dump from %s [%s]\n", client->hostname, - client_ntoa(client)); - LOGINFO("New dump from client %s [%s] (to %s)\n", client->hostname, - client_ntoa(client), client->corefilename); - send_ack(client, msg); -} - -static void -handle_kdh(struct netdump_client *client, struct netdump_msg *msg) -{ - time_t t; - uint64_t dumplen; - struct kerneldumpheader *h; - int parity_check; - - assert(msg != NULL); - - if (client == NULL) - return; - - client->any_data_rcvd = 1; - h = (struct kerneldumpheader *)msg->data; - if (msg->hdr.len < sizeof(struct kerneldumpheader)) { - LOGERR("Bad KDH from %s [%s]: packet too small\n", - client->hostname, client_ntoa(client)); - client_pinfo(client, "Bad KDH: packet too small\n"); - fflush(client->infofile); - send_ack(client, msg); - return; - } - parity_check = kerneldump_parity(h); - - /* Make sure all the strings are null-terminated. */ - 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'; - - client_pinfo(client, " Architecture: %s\n", h->architecture); - client_pinfo(client, " Architecture version: %d\n", - dtoh32(h->architectureversion)); - dumplen = dtoh64(h->dumplength); - client_pinfo(client, " Dump length: %lldB (%lld MB)\n", - (long long)dumplen, (long long)(dumplen >> 20)); - client_pinfo(client, " blocksize: %d\n", dtoh32(h->blocksize)); - t = dtoh64(h->dumptime); - client_pinfo(client, " Dumptime: %s", ctime(&t)); - client_pinfo(client, " Hostname: %s\n", h->hostname); - client_pinfo(client, " Versionstring: %s", h->versionstring); - client_pinfo(client, " Panicstring: %s\n", h->panicstring); - client_pinfo(client, " Header parity check: %s\n", - parity_check ? "Fail" : "Pass"); - fflush(client->infofile); - - LOGINFO("(KDH from %s [%s])", client->hostname, client_ntoa(client)); - send_ack(client, msg); -} - -static void -handle_vmcore(struct netdump_client *client, struct netdump_msg *msg) -{ - - assert(msg != NULL); - - if (client == NULL) - return; - - client->any_data_rcvd = 1; - if (msg->hdr.seqno % 11523 == 0) { - - /* Approximately every 16MB with MTU of 1500 */ - LOGINFO("."); - } - if (pwrite(client->corefd, msg->data, msg->hdr.len, - msg->hdr.offset) == -1) { - LOGERR("pwrite (for client %s [%s]): %s\n", client->hostname, - client_ntoa(client), strerror(errno)); - client_pinfo(client, - "Dump unsuccessful: write error @ offset %08"PRIx64": %s\n", - msg->hdr.offset, strerror(errno)); - exec_handler(client, "error"); - free_client(client); - return; - } - send_ack(client, msg); -} - -static void -handle_finish(struct netdump_client *client, struct netdump_msg *msg) -{ - - assert(msg != NULL); - - if (client == NULL) - return; - - LOGINFO("\nCompleted dump from client %s [%s]\n", client->hostname, - client_ntoa(client)); - client_pinfo(client, "Dump complete\n"); - send_ack(client, msg); - exec_handler(client, "success"); - free_client(client); -} - - -static int -receive_message(int isock, struct sockaddr_in *from, char *fromstr, - size_t fromstrlen, struct netdump_msg *msg) -{ - socklen_t fromlen; - ssize_t len; - - assert(from != NULL && fromstr != NULL && msg != NULL); - - 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; - - len = recvfrom(isock, msg, sizeof(*msg), 0, (struct sockaddr *)from, - &fromlen); - if (len == -1) { - - /* - * As long as some callers may discard the errors printing - * in defined circumstances, leave them the choice and avoid - * any error reporting. - */ - return (-1); - } - - snprintf(fromstr, fromstrlen, "%s:%hu", inet_ntoa(from->sin_addr), - ntohs(from->sin_port)); - if ((size_t)len < sizeof(struct netdump_msg_hdr)) { - LOGERR("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 = be64toh(msg->hdr.offset); - msg->hdr.len = ntohl(msg->hdr.len); - - if ((size_t)len < sizeof(struct netdump_msg_hdr) + msg->hdr.len) { - LOGERR("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); -} - -static void -handle_packet(struct netdump_client *client, struct sockaddr_in *from, - const char *fromstr, struct netdump_msg *msg) -{ - - assert(from != NULL && fromstr != NULL && msg != NULL); - - if (client != NULL) - client->last_msg = time(NULL); - - switch (msg->hdr.type) { - case NETDUMP_HERALD: - handle_herald(from, client, msg); - break; - case NETDUMP_KDH: - handle_kdh(client, msg); - break; - case NETDUMP_VMCORE: - handle_vmcore(client, msg); - break; - case NETDUMP_FINISHED: - handle_finish(client, msg); - break; - default: - LOGERR("Received unknown message type %d from %s\n", - msg->hdr.type, fromstr); - } -} - -static void -eventloop() -{ - struct netdump_msg msg; - char fromstr[INET_ADDRSTRLEN + 6]; - fd_set readfds; - struct sockaddr_in from; - struct timeval tv; - struct netdump_client *client, *tmp; - int len, maxfd; - - while (do_shutdown == 0) { - maxfd = sock + 1; - FD_ZERO(&readfds); - FD_SET(sock, &readfds); - SLIST_FOREACH(client, &clients, iter) { - FD_SET(client->sock, &readfds); - if (maxfd <= client->sock) - maxfd = client->sock+1; - } - - /* So that we time out clients regularly. */ - tv.tv_sec = CLIENT_TPASS; - tv.tv_usec = 0; - if (select(maxfd, &readfds, NULL, NULL, &tv) == -1) { - if (errno == EINTR) - continue; - LOGERR_PERROR("select()"); - - /* - * Errors with select() probably will not go away - * with simple retrying. - */ - pidfile_remove(pfh); - exit(1); - } - now = time(NULL); - if (FD_ISSET(sock, &readfds)) { - len = receive_message(sock, &from, fromstr, - sizeof(fromstr), &msg); - if (len == -1) { - if (errno == EINTR) - continue; - if (errno != EAGAIN) { - pidfile_remove(pfh); - LOGERR_PERROR("recvfrom()"); - exit(1); - } - } else if (len != 0) { - - /* - * With len == 0 the packet was rejected - * (probably because it was too small) so just - * ignore this case. - */ - - /* Check if they are on the clients list. */ - SLIST_FOREACH(client, &clients, iter) - if (client->ip.s_addr == - from.sin_addr.s_addr) - break; - - /* - * Technically, clients should not 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 != NULL && - msg.hdr.type != NETDUMP_HERALD && - client->printed_port_warning == 0) { - LOGWARN("Client %s responding on server port\n", - client->hostname); - client->printed_port_warning = 1; - } - handle_packet(client, &from, fromstr, &msg); - } - } - - /* - * handle_packet() and handle_timeout() may free the client, - * handle stale pointers. - */ - SLIST_FOREACH_SAFE(client, &clients, iter, tmp) { - if (FD_ISSET(client->sock, &readfds)) { - len = receive_message(client->sock, &from, - fromstr, sizeof(fromstr), &msg); - if (len == -1) { - if (errno == EINTR || errno == EAGAIN) - continue; - LOGERR_PERROR("recvfrom()"); - - /* - * Client socket is broken for - * some reason. - */ - handle_timeout(client); - } else if (len != 0) { - - /* - * With len == 0 the packet was - * rejected (probably because it was - * too small) so just ignore this case. - */ - - FD_CLR(client->sock, &readfds); - handle_packet(client, &from, fromstr, - &msg); - } - } - } - timeout_clients(); - } - LOGINFO("Shutting down..."); - - /* - * Clients is the head of the list, so clients != NULL iff the list - * is not empty. Call it a timeout so that the scripts get run. - */ - while (!SLIST_EMPTY(&clients)) - handle_timeout(SLIST_FIRST(&clients)); -} - -static void -signal_shutdown(int sig __unused) -{ - - do_shutdown = 1; -} - -int -main(int argc, char **argv) -{ - struct stat statbuf; - struct sockaddr_in bindaddr; - struct sigaction sa; - int ch; - - 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); - } - - while ((ch = getopt(argc, argv, "a:Dd:i:")) != -1) { - switch (ch) { - case 'a': - pflags |= PFLAGS_ABIND; - if (!inet_aton(optarg, &bindip)) { - pidfile_remove(pfh); - fprintf(stderr, "Invalid bind IP specified\n"); - exit(1); - } - printf("Listening on IP %s\n", optarg); - break; - case 'D': - pflags |= PFLAGS_DEBUG; - break; - case 'd': - pflags |= PFLAGS_DDIR; - assert(dumpdir[0] == '\0'); - strncpy(dumpdir, optarg, sizeof(dumpdir) - 1); - break; - case 'i': - pflags |= PFLAGS_SCRIPT; - - /* - * When suddenly closing the process for an error, - * it is unuseful to take care of handler_script - * deallocation as long as the process will _exit(2) - * anyway. - */ - handler_script = strdup(optarg); - if (handler_script == NULL) { - pidfile_remove(pfh); - perror("strdup()"); - fprintf(stderr, "Unable to set script file\n"); - exit(1); - } - if (access(handler_script, F_OK | X_OK)) { - pidfile_remove(pfh); - perror("access()"); - fprintf(stderr, - "Unable to access script file\n"); - exit(1); - } - break; - default: - pidfile_remove(pfh); - usage(argv[0]); - exit(1); - } - } - if ((pflags & PFLAGS_ABIND) == 0) { - bindip.s_addr = INADDR_ANY; - printf("Default: listening on all interfaces\n"); - } - if ((pflags & PFLAGS_DDIR) == 0) { - strcpy(dumpdir, "/var/crash"); - printf("Default: dumping on /var/crash/\n"); - } - if ((pflags & PFLAGS_DEBUG) == 0) - phook = syslog; - else - phook = phook_printf; - - /* Further sanity checks on dump location. */ - if (stat(dumpdir, &statbuf)) { - pidfile_remove(pfh); - perror("stat()"); - fprintf(stderr, "Invalid dump location specified\n"); - exit(1); - } - if ((statbuf.st_mode & S_IFMT) != S_IFDIR) { - pidfile_remove(pfh); - fprintf(stderr, "Dump location is not a directory\n"); - exit(1); - } - if (access(dumpdir, F_OK | W_OK)) { - fprintf(stderr, - "Warning: May be unable to write into dump location: %s\n", - strerror(errno)); - } - - if ((pflags & PFLAGS_DEBUG) == 0 && daemon(0, 0) == -1) { - pidfile_remove(pfh); - perror("daemon()"); - fprintf(stderr, "Impossible to demonize the process\n"); - exit(1); - } - pidfile_write(pfh); - - /* Set up the server socket. */ - sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); - if (sock == -1) { - pidfile_remove(pfh); - LOGERR_PERROR("socket()"); - exit(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); - close(sock); - LOGERR_PERROR("bind()"); - exit(1); - } - if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1) { - pidfile_remove(pfh); - close(sock); - LOGERR_PERROR("fcntl()"); - exit(1); - } - - /* Override some signal handlers. */ - bzero(&sa, sizeof(sa)); - sa.sa_handler = signal_shutdown; - if (sigaction(SIGINT, &sa, NULL) || sigaction(SIGTERM, &sa, NULL)) { - pidfile_remove(pfh); - close(sock); - LOGERR_PERROR("sigaction(SIGINT | SIGTERM)"); - exit(1); - } - bzero(&sa, sizeof(sa)); - sa.sa_handler = SIG_IGN; - sa.sa_flags = SA_NOCLDWAIT; - if (sigaction(SIGCHLD, &sa, NULL)) { - pidfile_remove(pfh); - LOGERR_PERROR("sigaction(SIGCHLD)"); - close(sock); - exit(1); - } - - LOGINFO("Waiting for clients.\n"); - eventloop(); - - pidfile_remove(pfh); - return (0); -} Property changes on: projects/sv/usr.sbin/netdumpsrv/netdump_server.c ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: projects/sv/usr.sbin/netdumpsrv/netdumpsrv.c =================================================================== --- projects/sv/usr.sbin/netdumpsrv/netdumpsrv.c (nonexistent) +++ projects/sv/usr.sbin/netdumpsrv/netdumpsrv.c (revision 211711) @@ -0,0 +1,903 @@ +/*- + * 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 +#include +#include + +#define MAX_DUMPS 256 /* Dumps per IP before to be cleaned out. */ +#define CLIENT_TIMEOUT 120 /* Clients timeout (secs). */ +#define CLIENT_TPASS 10 /* Clients timeout pass (secs). */ + +#define PFLAGS_ABIND 0x01 +#define PFLAGS_DDIR 0x02 +#define PFLAGS_DEBUG 0x04 +#define PFLAGS_SCRIPT 0x08 + +#define LOGERR(m, ...) \ + (*phook)(LOG_ERR | LOG_DAEMON, (m), ## __VA_ARGS__) +#define LOGERR_PERROR(m) \ + (*phook)(LOG_ERR | LOG_DAEMON, "%s: %s\n", m, strerror(errno)) +#define LOGINFO(m, ...) \ + (*phook)(LOG_INFO | LOG_DAEMON, (m), ## __VA_ARGS__) +#define LOGWARN(m, ...) \ + (*phook)(LOG_WARNING | LOG_DAEMON, (m), ## __VA_ARGS__) + +#define client_ntoa(cl) \ + inet_ntoa((cl)->ip) +#define client_pinfo(cl, f, ...) \ + fprintf((cl)->infofile, (f), ## __VA_ARGS__) + +struct netdump_client { + char infofilename[MAXPATHLEN]; + char corefilename[MAXPATHLEN]; + char hostname[NI_MAXHOST]; + time_t last_msg; + SLIST_ENTRY(netdump_client) iter; + struct in_addr ip; + FILE *infofile; + int corefd; + int sock; + unsigned short printed_port_warning: 1; + unsigned short any_data_rcvd: 1; +}; + +/* Clients list. */ +static SLIST_HEAD(, netdump_client) clients = SLIST_HEAD_INITIALIZER(clients); + +/* Program arguments handlers. */ +static uint32_t pflags; +static char dumpdir[MAXPATHLEN]; +static char *handler_script; +static struct in_addr bindip; + +/* Miscellaneous handlers. */ +static struct pidfh *pfh; +static time_t now; +static time_t last_timeout_check; +static int do_shutdown; +static int sock; + +/* Daemon print functions hook. */ +static void (*phook)(int, const char *, ...); + +static struct netdump_client *alloc_client(struct sockaddr_in *sip); +static void eventloop(void); +static void exec_handler(struct netdump_client *client, + const char *reason); +static void free_client(struct netdump_client *client); +static void handle_finish(struct netdump_client *client, + struct netdump_msg *msg); +static void handle_herald(struct sockaddr_in *from, + struct netdump_client *client, + struct netdump_msg *msg); +static void handle_kdh(struct netdump_client *client, + struct netdump_msg *msg); +static void handle_packet(struct netdump_client *client, + struct sockaddr_in *from, const char *fromstr, + struct netdump_msg *msg); +static void handle_timeout(struct netdump_client *client); +static void handle_vmcore(struct netdump_client *client, + struct netdump_msg *msg); +static void phook_printf(int priority, const char *message, ...); +static int receive_message(int isock, struct sockaddr_in *from, + char *fromstr, size_t fromstrlen, + struct netdump_msg *msg); +static void send_ack(struct netdump_client *client, + struct netdump_msg *msg); +static void signal_shutdown(int sig __unused); +static void timeout_clients(void); +static void usage(const char *cmd); + +static void +usage(const char *cmd) +{ + + fprintf(stderr, "Usage: %s [-a bind_addr] [-d dump_dir] [-i script]\n", + cmd); +} + +static void +phook_printf(int priority, const char *message, ...) +{ + va_list ap; + + va_start(ap, message); + if ((priority & LOG_INFO) != 0) { + assert((priority & (LOG_WARNING | LOG_ERR)) == 0); + vprintf(message, ap); + } else + vfprintf(stderr, message, ap); +} + +static struct netdump_client * +alloc_client(struct sockaddr_in *sip) +{ + struct sockaddr_in saddr; + struct netdump_client *client; + struct in_addr *ip; + char *firstdot; + int i, ecode, fd, bufsz; + + assert(sip != NULL); + + client = calloc(1, sizeof(*client)); + if (client == NULL) { + LOGERR_PERROR("calloc()"); + return (NULL); + } + ip = &sip->sin_addr; + bcopy(ip, &client->ip, sizeof(*ip)); + client->corefd = -1; + client->sock = -1; + client->last_msg = now; + + ecode = getnameinfo((struct sockaddr *)sip, sip->sin_len, + client->hostname, sizeof(client->hostname), NULL, 0, NI_NAMEREQD); + if (ecode != 0) { + + /* Can't resolve, try with a numeric IP. */ + ecode = getnameinfo((struct sockaddr *)sip, sip->sin_len, + client->hostname, sizeof(client->hostname), NULL, 0, 0); + if (ecode != 0) { + LOGERR("getnameinfo(): %s\n", gai_strerror(ecode)); + free(client); + return (NULL); + } + } else { + + /* Strip off the domain name */ + firstdot = strchr(client->hostname, '.'); + if (firstdot) + *firstdot = '\0'; + } + + client->sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (client->sock == -1) { + LOGERR_PERROR("socket()"); + free(client); + return (NULL); + } + if (fcntl(client->sock, F_SETFL, O_NONBLOCK) == -1) { + LOGERR_PERROR("fcntl()"); + 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))) { + LOGERR_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))) { + LOGERR_PERROR("connect()"); + close(client->sock); + free(client); + return (NULL); + } + + /* It should be enough to hold approximatively twize the chunk size. */ + bufsz = 131072; + if (setsockopt(client->sock, SOL_SOCKET, SO_RCVBUF, &bufsz, + sizeof(bufsz))) { + LOGERR_PERROR("setsockopt()"); + LOGWARN("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. */ + fd = open(client->infofilename, O_WRONLY|O_CREAT|O_EXCL, + DEFFILEMODE); + if (fd == -1) { + if (errno != EEXIST) + LOGERR("open(\"%s\"): %s\n", + client->infofilename, strerror(errno)); + continue; + } + client->infofile = fdopen(fd, "w"); + if (client->infofile == NULL) { + LOGERR_PERROR("fdopen()"); + close(fd); + unlink(client->infofilename); + continue; + } + + /* Next make the core file. */ + fd = open(client->corefilename, O_RDWR|O_CREAT|O_EXCL, + DEFFILEMODE); + if (fd == -1) { + + /* Failed. Keep the numbers in sync. */ + fclose(client->infofile); + unlink(client->infofilename); + client->infofile = NULL; + if (errno != EEXIST) + LOGERR("open(\"%s\"): %s\n", + client->corefilename, strerror(errno)); + continue; + } + client->corefd = fd; + break; + } + + if (client->infofile == NULL || client->corefd == -1) { + LOGERR("Can't create output files for new client %s [%s]\n", + client->hostname, client_ntoa(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); + } + SLIST_INSERT_HEAD(&clients, client, iter); + return (client); +} + +static void +free_client(struct netdump_client *client) +{ + + assert(client != NULL); + + /* Remove from the list. Ignore errors from close() routines. */ + SLIST_REMOVE(&clients, client, netdump_client, iter); + fclose(client->infofile); + close(client->corefd); + close(client->sock); + free(client); +} + +static void +exec_handler(struct netdump_client *client, const char *reason) +{ + int pid; + + assert(client != NULL); + + /* If no script is specified this is a no-op. */ + if ((pflags & PFLAGS_SCRIPT) == 0) + return; + + pid = fork(); + + /* + * The function is invoked in critical conditions, thus just exiting + * without reporting errors is fine. + */ + if (pid == -1) { + LOGERR_PERROR("fork()"); + return; + } else if (pid != 0) { + close(sock); + pidfile_close(pfh); + if (execl(handler_script, handler_script, reason, + client_ntoa(client), client->hostname, + client->infofilename, client->corefilename, NULL) == -1) { + LOGERR_PERROR("fork()"); + _exit(1); + } + } +} + +static void +handle_timeout(struct netdump_client *client) +{ + + assert(client != NULL); + + LOGINFO("Client %s timed out\n", client_ntoa(client)); + client_pinfo(client, "Dump incomplete: client timed out\n"); + exec_handler(client, "timeout"); + free_client(client); +} + +static void +timeout_clients() +{ + struct netdump_client *client, *tmp; + + /* Only time out clients every 10 seconds. */ + if (now - last_timeout_check < CLIENT_TPASS) + return; + last_timeout_check = now; + + /* Traverse the list looking for stale clients. */ + SLIST_FOREACH_SAFE(client, &clients, iter, tmp) + if (client->last_msg + CLIENT_TIMEOUT < now) + handle_timeout(client); +} + +static void +send_ack(struct netdump_client *client, struct netdump_msg *msg) +{ + struct netdump_ack ack; + int tryagain; + + assert(client != NULL && msg != NULL); + + 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 is too much effort, since it is mostly + * harmless to wait for the client to retransmit. + */ + LOGERR_PERROR("send()"); + } + } while (tryagain); +} + +static void +handle_herald(struct sockaddr_in *from, struct netdump_client *client, + struct netdump_msg *msg) +{ + + assert(from != NULL && msg != NULL); + + if (client != NULL) { + if (client->any_data_rcvd == 0) { + + /* Must be a retransmit of the herald packet. */ + send_ack(client, msg); + return; + } + + /* An old connection must have timed out. Clean it up first. */ + handle_timeout(client); + } + + client = alloc_client(from); + if (client == NULL) { + LOGERR("handle_herald(): new client allocation failure\n"); + return; + } + client_pinfo(client, "Dump from %s [%s]\n", client->hostname, + client_ntoa(client)); + LOGINFO("New dump from client %s [%s] (to %s)\n", client->hostname, + client_ntoa(client), client->corefilename); + send_ack(client, msg); +} + +static void +handle_kdh(struct netdump_client *client, struct netdump_msg *msg) +{ + time_t t; + uint64_t dumplen; + struct kerneldumpheader *h; + int parity_check; + + assert(msg != NULL); + + if (client == NULL) + return; + + client->any_data_rcvd = 1; + h = (struct kerneldumpheader *)msg->data; + if (msg->hdr.len < sizeof(struct kerneldumpheader)) { + LOGERR("Bad KDH from %s [%s]: packet too small\n", + client->hostname, client_ntoa(client)); + client_pinfo(client, "Bad KDH: packet too small\n"); + fflush(client->infofile); + send_ack(client, msg); + return; + } + parity_check = kerneldump_parity(h); + + /* Make sure all the strings are null-terminated. */ + 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'; + + client_pinfo(client, " Architecture: %s\n", h->architecture); + client_pinfo(client, " Architecture version: %d\n", + dtoh32(h->architectureversion)); + dumplen = dtoh64(h->dumplength); + client_pinfo(client, " Dump length: %lldB (%lld MB)\n", + (long long)dumplen, (long long)(dumplen >> 20)); + client_pinfo(client, " blocksize: %d\n", dtoh32(h->blocksize)); + t = dtoh64(h->dumptime); + client_pinfo(client, " Dumptime: %s", ctime(&t)); + client_pinfo(client, " Hostname: %s\n", h->hostname); + client_pinfo(client, " Versionstring: %s", h->versionstring); + client_pinfo(client, " Panicstring: %s\n", h->panicstring); + client_pinfo(client, " Header parity check: %s\n", + parity_check ? "Fail" : "Pass"); + fflush(client->infofile); + + LOGINFO("(KDH from %s [%s])", client->hostname, client_ntoa(client)); + send_ack(client, msg); +} + +static void +handle_vmcore(struct netdump_client *client, struct netdump_msg *msg) +{ + + assert(msg != NULL); + + if (client == NULL) + return; + + client->any_data_rcvd = 1; + if (msg->hdr.seqno % 11523 == 0) { + + /* Approximately every 16MB with MTU of 1500 */ + LOGINFO("."); + } + if (pwrite(client->corefd, msg->data, msg->hdr.len, + msg->hdr.offset) == -1) { + LOGERR("pwrite (for client %s [%s]): %s\n", client->hostname, + client_ntoa(client), strerror(errno)); + client_pinfo(client, + "Dump unsuccessful: write error @ offset %08"PRIx64": %s\n", + msg->hdr.offset, strerror(errno)); + exec_handler(client, "error"); + free_client(client); + return; + } + send_ack(client, msg); +} + +static void +handle_finish(struct netdump_client *client, struct netdump_msg *msg) +{ + + assert(msg != NULL); + + if (client == NULL) + return; + + LOGINFO("\nCompleted dump from client %s [%s]\n", client->hostname, + client_ntoa(client)); + client_pinfo(client, "Dump complete\n"); + send_ack(client, msg); + exec_handler(client, "success"); + free_client(client); +} + + +static int +receive_message(int isock, struct sockaddr_in *from, char *fromstr, + size_t fromstrlen, struct netdump_msg *msg) +{ + socklen_t fromlen; + ssize_t len; + + assert(from != NULL && fromstr != NULL && msg != NULL); + + 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; + + len = recvfrom(isock, msg, sizeof(*msg), 0, (struct sockaddr *)from, + &fromlen); + if (len == -1) { + + /* + * As long as some callers may discard the errors printing + * in defined circumstances, leave them the choice and avoid + * any error reporting. + */ + return (-1); + } + + snprintf(fromstr, fromstrlen, "%s:%hu", inet_ntoa(from->sin_addr), + ntohs(from->sin_port)); + if ((size_t)len < sizeof(struct netdump_msg_hdr)) { + LOGERR("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 = be64toh(msg->hdr.offset); + msg->hdr.len = ntohl(msg->hdr.len); + + if ((size_t)len < sizeof(struct netdump_msg_hdr) + msg->hdr.len) { + LOGERR("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); +} + +static void +handle_packet(struct netdump_client *client, struct sockaddr_in *from, + const char *fromstr, struct netdump_msg *msg) +{ + + assert(from != NULL && fromstr != NULL && msg != NULL); + + if (client != NULL) + client->last_msg = time(NULL); + + switch (msg->hdr.type) { + case NETDUMP_HERALD: + handle_herald(from, client, msg); + break; + case NETDUMP_KDH: + handle_kdh(client, msg); + break; + case NETDUMP_VMCORE: + handle_vmcore(client, msg); + break; + case NETDUMP_FINISHED: + handle_finish(client, msg); + break; + default: + LOGERR("Received unknown message type %d from %s\n", + msg->hdr.type, fromstr); + } +} + +static void +eventloop() +{ + struct netdump_msg msg; + char fromstr[INET_ADDRSTRLEN + 6]; + fd_set readfds; + struct sockaddr_in from; + struct timeval tv; + struct netdump_client *client, *tmp; + int len, maxfd; + + while (do_shutdown == 0) { + maxfd = sock + 1; + FD_ZERO(&readfds); + FD_SET(sock, &readfds); + SLIST_FOREACH(client, &clients, iter) { + FD_SET(client->sock, &readfds); + if (maxfd <= client->sock) + maxfd = client->sock+1; + } + + /* So that we time out clients regularly. */ + tv.tv_sec = CLIENT_TPASS; + tv.tv_usec = 0; + if (select(maxfd, &readfds, NULL, NULL, &tv) == -1) { + if (errno == EINTR) + continue; + LOGERR_PERROR("select()"); + + /* + * Errors with select() probably will not go away + * with simple retrying. + */ + pidfile_remove(pfh); + exit(1); + } + now = time(NULL); + if (FD_ISSET(sock, &readfds)) { + len = receive_message(sock, &from, fromstr, + sizeof(fromstr), &msg); + if (len == -1) { + if (errno == EINTR) + continue; + if (errno != EAGAIN) { + pidfile_remove(pfh); + LOGERR_PERROR("recvfrom()"); + exit(1); + } + } else if (len != 0) { + + /* + * With len == 0 the packet was rejected + * (probably because it was too small) so just + * ignore this case. + */ + + /* Check if they are on the clients list. */ + SLIST_FOREACH(client, &clients, iter) + if (client->ip.s_addr == + from.sin_addr.s_addr) + break; + + /* + * Technically, clients should not 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 != NULL && + msg.hdr.type != NETDUMP_HERALD && + client->printed_port_warning == 0) { + LOGWARN("Client %s responding on server port\n", + client->hostname); + client->printed_port_warning = 1; + } + handle_packet(client, &from, fromstr, &msg); + } + } + + /* + * handle_packet() and handle_timeout() may free the client, + * handle stale pointers. + */ + SLIST_FOREACH_SAFE(client, &clients, iter, tmp) { + if (FD_ISSET(client->sock, &readfds)) { + len = receive_message(client->sock, &from, + fromstr, sizeof(fromstr), &msg); + if (len == -1) { + if (errno == EINTR || errno == EAGAIN) + continue; + LOGERR_PERROR("recvfrom()"); + + /* + * Client socket is broken for + * some reason. + */ + handle_timeout(client); + } else if (len != 0) { + + /* + * With len == 0 the packet was + * rejected (probably because it was + * too small) so just ignore this case. + */ + + FD_CLR(client->sock, &readfds); + handle_packet(client, &from, fromstr, + &msg); + } + } + } + timeout_clients(); + } + LOGINFO("Shutting down..."); + + /* + * Clients is the head of the list, so clients != NULL iff the list + * is not empty. Call it a timeout so that the scripts get run. + */ + while (!SLIST_EMPTY(&clients)) + handle_timeout(SLIST_FIRST(&clients)); +} + +static void +signal_shutdown(int sig __unused) +{ + + do_shutdown = 1; +} + +int +main(int argc, char **argv) +{ + struct stat statbuf; + struct sockaddr_in bindaddr; + struct sigaction sa; + int ch; + + 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); + } + + while ((ch = getopt(argc, argv, "a:Dd:i:")) != -1) { + switch (ch) { + case 'a': + pflags |= PFLAGS_ABIND; + if (!inet_aton(optarg, &bindip)) { + pidfile_remove(pfh); + fprintf(stderr, "Invalid bind IP specified\n"); + exit(1); + } + printf("Listening on IP %s\n", optarg); + break; + case 'D': + pflags |= PFLAGS_DEBUG; + break; + case 'd': + pflags |= PFLAGS_DDIR; + assert(dumpdir[0] == '\0'); + strncpy(dumpdir, optarg, sizeof(dumpdir) - 1); + break; + case 'i': + pflags |= PFLAGS_SCRIPT; + + /* + * When suddenly closing the process for an error, + * it is unuseful to take care of handler_script + * deallocation as long as the process will _exit(2) + * anyway. + */ + handler_script = strdup(optarg); + if (handler_script == NULL) { + pidfile_remove(pfh); + perror("strdup()"); + fprintf(stderr, "Unable to set script file\n"); + exit(1); + } + if (access(handler_script, F_OK | X_OK)) { + pidfile_remove(pfh); + perror("access()"); + fprintf(stderr, + "Unable to access script file\n"); + exit(1); + } + break; + default: + pidfile_remove(pfh); + usage(argv[0]); + exit(1); + } + } + if ((pflags & PFLAGS_ABIND) == 0) { + bindip.s_addr = INADDR_ANY; + printf("Default: listening on all interfaces\n"); + } + if ((pflags & PFLAGS_DDIR) == 0) { + strcpy(dumpdir, "/var/crash"); + printf("Default: dumping on /var/crash/\n"); + } + if ((pflags & PFLAGS_DEBUG) == 0) + phook = syslog; + else + phook = phook_printf; + + /* Further sanity checks on dump location. */ + if (stat(dumpdir, &statbuf)) { + pidfile_remove(pfh); + perror("stat()"); + fprintf(stderr, "Invalid dump location specified\n"); + exit(1); + } + if ((statbuf.st_mode & S_IFMT) != S_IFDIR) { + pidfile_remove(pfh); + fprintf(stderr, "Dump location is not a directory\n"); + exit(1); + } + if (access(dumpdir, F_OK | W_OK)) { + fprintf(stderr, + "Warning: May be unable to write into dump location: %s\n", + strerror(errno)); + } + + if ((pflags & PFLAGS_DEBUG) == 0 && daemon(0, 0) == -1) { + pidfile_remove(pfh); + perror("daemon()"); + fprintf(stderr, "Impossible to demonize the process\n"); + exit(1); + } + pidfile_write(pfh); + + /* Set up the server socket. */ + sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (sock == -1) { + pidfile_remove(pfh); + LOGERR_PERROR("socket()"); + exit(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); + close(sock); + LOGERR_PERROR("bind()"); + exit(1); + } + if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1) { + pidfile_remove(pfh); + close(sock); + LOGERR_PERROR("fcntl()"); + exit(1); + } + + /* Override some signal handlers. */ + bzero(&sa, sizeof(sa)); + sa.sa_handler = signal_shutdown; + if (sigaction(SIGINT, &sa, NULL) || sigaction(SIGTERM, &sa, NULL)) { + pidfile_remove(pfh); + close(sock); + LOGERR_PERROR("sigaction(SIGINT | SIGTERM)"); + exit(1); + } + bzero(&sa, sizeof(sa)); + sa.sa_handler = SIG_IGN; + sa.sa_flags = SA_NOCLDWAIT; + if (sigaction(SIGCHLD, &sa, NULL)) { + pidfile_remove(pfh); + LOGERR_PERROR("sigaction(SIGCHLD)"); + close(sock); + exit(1); + } + + LOGINFO("Waiting for clients.\n"); + eventloop(); + + pidfile_remove(pfh); + return (0); +} Property changes on: projects/sv/usr.sbin/netdumpsrv/netdumpsrv.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property