Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 23 Aug 2010 17:22:46 +0000 (UTC)
From:      Attilio Rao <attilio@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-projects@freebsd.org
Subject:   svn commit: r211711 - projects/sv/usr.sbin/netdumpsrv
Message-ID:  <201008231722.o7NHMkrC047429@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
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);
+}



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201008231722.o7NHMkrC047429>