svn commit: r211711 - projects/sv/usr.sbin/netdumpsrv
Attilio Rao
attilio at FreeBSD.org
Mon Aug 23 17:22:46 UTC 2010
Author: attilio
Date: Mon Aug 23 17:22:45 2010
New Revision: 211711
URL: http://svn.freebsd.org/changeset/base/211711
Log:
Rename netdump_server.c into netdumpsrv.c
Added:
projects/sv/usr.sbin/netdumpsrv/netdumpsrv.c
- copied unchanged from r211710, projects/sv/usr.sbin/netdumpsrv/netdump_server.c
Deleted:
projects/sv/usr.sbin/netdumpsrv/netdump_server.c
Copied: projects/sv/usr.sbin/netdumpsrv/netdumpsrv.c (from r211710, 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/netdumpsrv.c Mon Aug 23 17:22:45 2010 (r211711, copy of r211710, projects/sv/usr.sbin/netdumpsrv/netdump_server.c)
@@ -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 <sys/param.h>
+#include <sys/endian.h>
+#include <sys/errno.h>
+#include <sys/kerneldump.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <netinet/netdump.h>
+
+#include <fcntl.h>
+#include <libutil.h>
+#include <netdb.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include <assert.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#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);
+}
More information about the svn-src-projects
mailing list