Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 9 Aug 2010 22:29:31 +0000 (UTC)
From:      Attilio Rao <attilio@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-projects@freebsd.org
Subject:   svn commit: r211124 - projects/sv/usr.sbin/netdumpsrv
Message-ID:  <201008092229.o79MTVvn025508@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: attilio
Date: Mon Aug  9 22:29:31 2010
New Revision: 211124
URL: http://svn.freebsd.org/changeset/base/211124

Log:
  Add the server implementation of netdumps.

Added:
  projects/sv/usr.sbin/netdumpsrv/
  projects/sv/usr.sbin/netdumpsrv/netdump_server.c   (contents, props changed)

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



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