xref: /freebsd/contrib/pf/pflogd/pflogd.c (revision 13b9f610)
113b9f610SMax Laier /*	$OpenBSD: pflogd.c,v 1.21 2003/08/22 21:50:34 david Exp $	*/
213b9f610SMax Laier 
313b9f610SMax Laier /*
413b9f610SMax Laier  * Copyright (c) 2001 Theo de Raadt
513b9f610SMax Laier  * Copyright (c) 2001 Can Erkin Acar
613b9f610SMax Laier  * All rights reserved.
713b9f610SMax Laier  *
813b9f610SMax Laier  * Redistribution and use in source and binary forms, with or without
913b9f610SMax Laier  * modification, are permitted provided that the following conditions
1013b9f610SMax Laier  * are met:
1113b9f610SMax Laier  *
1213b9f610SMax Laier  *    - Redistributions of source code must retain the above copyright
1313b9f610SMax Laier  *      notice, this list of conditions and the following disclaimer.
1413b9f610SMax Laier  *    - Redistributions in binary form must reproduce the above
1513b9f610SMax Laier  *      copyright notice, this list of conditions and the following
1613b9f610SMax Laier  *      disclaimer in the documentation and/or other materials provided
1713b9f610SMax Laier  *      with the distribution.
1813b9f610SMax Laier  *
1913b9f610SMax Laier  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
2013b9f610SMax Laier  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
2113b9f610SMax Laier  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
2213b9f610SMax Laier  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
2313b9f610SMax Laier  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
2413b9f610SMax Laier  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
2513b9f610SMax Laier  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
2613b9f610SMax Laier  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
2713b9f610SMax Laier  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2813b9f610SMax Laier  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
2913b9f610SMax Laier  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
3013b9f610SMax Laier  * POSSIBILITY OF SUCH DAMAGE.
3113b9f610SMax Laier  */
3213b9f610SMax Laier 
3313b9f610SMax Laier #include <sys/types.h>
3413b9f610SMax Laier #include <sys/file.h>
3513b9f610SMax Laier #include <sys/stat.h>
3613b9f610SMax Laier #include <stdio.h>
3713b9f610SMax Laier #include <stdlib.h>
3813b9f610SMax Laier #include <string.h>
3913b9f610SMax Laier #include <unistd.h>
4013b9f610SMax Laier #include <pcap-int.h>
4113b9f610SMax Laier #include <pcap.h>
4213b9f610SMax Laier #include <syslog.h>
4313b9f610SMax Laier #include <signal.h>
4413b9f610SMax Laier #include <errno.h>
4513b9f610SMax Laier #include <stdarg.h>
4613b9f610SMax Laier #include <fcntl.h>
4713b9f610SMax Laier #include <util.h>
4813b9f610SMax Laier 
4913b9f610SMax Laier #define DEF_SNAPLEN 116		/* default plus allow for larger header of pflog */
5013b9f610SMax Laier #define PCAP_TO_MS 500		/* pcap read timeout (ms) */
5113b9f610SMax Laier #define PCAP_NUM_PKTS 1000	/* max number of packets to process at each loop */
5213b9f610SMax Laier #define PCAP_OPT_FIL 0		/* filter optimization */
5313b9f610SMax Laier #define FLUSH_DELAY 60		/* flush delay */
5413b9f610SMax Laier 
5513b9f610SMax Laier #define PFLOGD_LOG_FILE		"/var/log/pflog"
5613b9f610SMax Laier #define PFLOGD_DEFAULT_IF	"pflog0"
5713b9f610SMax Laier 
5813b9f610SMax Laier pcap_t *hpcap;
5913b9f610SMax Laier pcap_dumper_t *dpcap;
6013b9f610SMax Laier 
6113b9f610SMax Laier int Debug = 0;
6213b9f610SMax Laier int snaplen = DEF_SNAPLEN;
6313b9f610SMax Laier 
6413b9f610SMax Laier volatile sig_atomic_t gotsig_close, gotsig_alrm, gotsig_hup;
6513b9f610SMax Laier 
6613b9f610SMax Laier char *filename = PFLOGD_LOG_FILE;
6713b9f610SMax Laier char *interface = PFLOGD_DEFAULT_IF;
6813b9f610SMax Laier char *filter = NULL;
6913b9f610SMax Laier 
7013b9f610SMax Laier char errbuf[PCAP_ERRBUF_SIZE];
7113b9f610SMax Laier 
7213b9f610SMax Laier int log_debug = 0;
7313b9f610SMax Laier unsigned int delay = FLUSH_DELAY;
7413b9f610SMax Laier 
7513b9f610SMax Laier char *copy_argv(char * const *argv);
7613b9f610SMax Laier int   init_pcap(void);
7713b9f610SMax Laier void  logmsg(int priority, const char *message, ...);
7813b9f610SMax Laier int   reset_dump(void);
7913b9f610SMax Laier void  sig_alrm(int);
8013b9f610SMax Laier void  sig_close(int);
8113b9f610SMax Laier void  sig_hup(int);
8213b9f610SMax Laier void  usage(void);
8313b9f610SMax Laier 
8413b9f610SMax Laier 
8513b9f610SMax Laier char *
8613b9f610SMax Laier copy_argv(char * const *argv)
8713b9f610SMax Laier {
8813b9f610SMax Laier 	size_t len = 0, n;
8913b9f610SMax Laier 	char *buf;
9013b9f610SMax Laier 
9113b9f610SMax Laier 	if (argv == NULL)
9213b9f610SMax Laier 		return (NULL);
9313b9f610SMax Laier 
9413b9f610SMax Laier 	for (n = 0; argv[n]; n++)
9513b9f610SMax Laier 		len += strlen(argv[n])+1;
9613b9f610SMax Laier 	if (len == 0)
9713b9f610SMax Laier 		return (NULL);
9813b9f610SMax Laier 
9913b9f610SMax Laier 	buf = malloc(len);
10013b9f610SMax Laier 	if (buf == NULL)
10113b9f610SMax Laier 		return (NULL);
10213b9f610SMax Laier 
10313b9f610SMax Laier 	strlcpy(buf, argv[0], len);
10413b9f610SMax Laier 	for (n = 1; argv[n]; n++) {
10513b9f610SMax Laier 		strlcat(buf, " ", len);
10613b9f610SMax Laier 		strlcat(buf, argv[n], len);
10713b9f610SMax Laier 	}
10813b9f610SMax Laier 	return (buf);
10913b9f610SMax Laier }
11013b9f610SMax Laier 
11113b9f610SMax Laier void
11213b9f610SMax Laier logmsg(int pri, const char *message, ...)
11313b9f610SMax Laier {
11413b9f610SMax Laier 	va_list ap;
11513b9f610SMax Laier 	va_start(ap, message);
11613b9f610SMax Laier 
11713b9f610SMax Laier 	if (log_debug) {
11813b9f610SMax Laier 		vfprintf(stderr, message, ap);
11913b9f610SMax Laier 		fprintf(stderr, "\n");
12013b9f610SMax Laier 	} else
12113b9f610SMax Laier 		vsyslog(pri, message, ap);
12213b9f610SMax Laier 	va_end(ap);
12313b9f610SMax Laier }
12413b9f610SMax Laier 
12513b9f610SMax Laier __dead void
12613b9f610SMax Laier usage(void)
12713b9f610SMax Laier {
12813b9f610SMax Laier 	fprintf(stderr, "usage: pflogd [-D] [-d delay] [-f filename] ");
12913b9f610SMax Laier 	fprintf(stderr, "[-s snaplen] [expression]\n");
13013b9f610SMax Laier 	exit(1);
13113b9f610SMax Laier }
13213b9f610SMax Laier 
13313b9f610SMax Laier void
13413b9f610SMax Laier sig_close(int sig)
13513b9f610SMax Laier {
13613b9f610SMax Laier 	gotsig_close = 1;
13713b9f610SMax Laier }
13813b9f610SMax Laier 
13913b9f610SMax Laier void
14013b9f610SMax Laier sig_hup(int sig)
14113b9f610SMax Laier {
14213b9f610SMax Laier 	gotsig_hup = 1;
14313b9f610SMax Laier }
14413b9f610SMax Laier 
14513b9f610SMax Laier void
14613b9f610SMax Laier sig_alrm(int sig)
14713b9f610SMax Laier {
14813b9f610SMax Laier 	gotsig_alrm = 1;
14913b9f610SMax Laier }
15013b9f610SMax Laier 
15113b9f610SMax Laier int
15213b9f610SMax Laier init_pcap(void)
15313b9f610SMax Laier {
15413b9f610SMax Laier 	struct bpf_program bprog;
15513b9f610SMax Laier 	pcap_t *oldhpcap = hpcap;
15613b9f610SMax Laier 
15713b9f610SMax Laier 	hpcap = pcap_open_live(interface, snaplen, 1, PCAP_TO_MS, errbuf);
15813b9f610SMax Laier 	if (hpcap == NULL) {
15913b9f610SMax Laier 		logmsg(LOG_ERR, "Failed to initialize: %s", errbuf);
16013b9f610SMax Laier 		hpcap = oldhpcap;
16113b9f610SMax Laier 		return (-1);
16213b9f610SMax Laier 	}
16313b9f610SMax Laier 
16413b9f610SMax Laier 	if (pcap_compile(hpcap, &bprog, filter, PCAP_OPT_FIL, 0) < 0)
16513b9f610SMax Laier 		logmsg(LOG_WARNING, "%s", pcap_geterr(hpcap));
16613b9f610SMax Laier 	else if (pcap_setfilter(hpcap, &bprog) < 0)
16713b9f610SMax Laier 		logmsg(LOG_WARNING, "%s", pcap_geterr(hpcap));
16813b9f610SMax Laier 	if (filter != NULL)
16913b9f610SMax Laier 		free(filter);
17013b9f610SMax Laier 
17113b9f610SMax Laier 	if (pcap_datalink(hpcap) != DLT_PFLOG) {
17213b9f610SMax Laier 		logmsg(LOG_ERR, "Invalid datalink type");
17313b9f610SMax Laier 		pcap_close(hpcap);
17413b9f610SMax Laier 		hpcap = oldhpcap;
17513b9f610SMax Laier 		return (-1);
17613b9f610SMax Laier 	}
17713b9f610SMax Laier 
17813b9f610SMax Laier 	if (oldhpcap)
17913b9f610SMax Laier 		pcap_close(oldhpcap);
18013b9f610SMax Laier 
18113b9f610SMax Laier 	snaplen = pcap_snapshot(hpcap);
18213b9f610SMax Laier 	return (0);
18313b9f610SMax Laier }
18413b9f610SMax Laier 
18513b9f610SMax Laier int
18613b9f610SMax Laier reset_dump(void)
18713b9f610SMax Laier {
18813b9f610SMax Laier 	struct pcap_file_header hdr;
18913b9f610SMax Laier 	struct stat st;
19013b9f610SMax Laier 	int tmpsnap;
19113b9f610SMax Laier 	FILE *fp;
19213b9f610SMax Laier 
19313b9f610SMax Laier 	if (hpcap == NULL)
19413b9f610SMax Laier 		return (1);
19513b9f610SMax Laier 	if (dpcap) {
19613b9f610SMax Laier 		pcap_dump_close(dpcap);
19713b9f610SMax Laier 		dpcap = 0;
19813b9f610SMax Laier 	}
19913b9f610SMax Laier 
20013b9f610SMax Laier 	/*
20113b9f610SMax Laier 	 * Basically reimplement pcap_dump_open() because it truncates
20213b9f610SMax Laier 	 * files and duplicates headers and such.
20313b9f610SMax Laier 	 */
20413b9f610SMax Laier 	fp = fopen(filename, "a+");
20513b9f610SMax Laier 	if (fp == NULL) {
20613b9f610SMax Laier 		snprintf(hpcap->errbuf, PCAP_ERRBUF_SIZE, "%s: %s",
20713b9f610SMax Laier 		    filename, pcap_strerror(errno));
20813b9f610SMax Laier 		logmsg(LOG_ERR, "Error: %s", pcap_geterr(hpcap));
20913b9f610SMax Laier 		return (1);
21013b9f610SMax Laier 	}
21113b9f610SMax Laier 	if (fstat(fileno(fp), &st) == -1) {
21213b9f610SMax Laier 		snprintf(hpcap->errbuf, PCAP_ERRBUF_SIZE, "%s: %s",
21313b9f610SMax Laier 		    filename, pcap_strerror(errno));
21413b9f610SMax Laier 		logmsg(LOG_ERR, "Error: %s", pcap_geterr(hpcap));
21513b9f610SMax Laier 		return (1);
21613b9f610SMax Laier 	}
21713b9f610SMax Laier 
21813b9f610SMax Laier 	dpcap = (pcap_dumper_t *)fp;
21913b9f610SMax Laier 
22013b9f610SMax Laier #define TCPDUMP_MAGIC 0xa1b2c3d4
22113b9f610SMax Laier 
22213b9f610SMax Laier 	if (st.st_size == 0) {
22313b9f610SMax Laier 		if (snaplen != pcap_snapshot(hpcap)) {
22413b9f610SMax Laier 			logmsg(LOG_NOTICE, "Using snaplen %d", snaplen);
22513b9f610SMax Laier 			if (init_pcap()) {
22613b9f610SMax Laier 				logmsg(LOG_ERR, "Failed to initialize");
22713b9f610SMax Laier 				if (hpcap == NULL) return (-1);
22813b9f610SMax Laier 				logmsg(LOG_NOTICE, "Using old settings");
22913b9f610SMax Laier 			}
23013b9f610SMax Laier 		}
23113b9f610SMax Laier 		hdr.magic = TCPDUMP_MAGIC;
23213b9f610SMax Laier 		hdr.version_major = PCAP_VERSION_MAJOR;
23313b9f610SMax Laier 		hdr.version_minor = PCAP_VERSION_MINOR;
23413b9f610SMax Laier 		hdr.thiszone = hpcap->tzoff;
23513b9f610SMax Laier 		hdr.snaplen = hpcap->snapshot;
23613b9f610SMax Laier 		hdr.sigfigs = 0;
23713b9f610SMax Laier 		hdr.linktype = hpcap->linktype;
23813b9f610SMax Laier 
23913b9f610SMax Laier 		if (fwrite((char *)&hdr, sizeof(hdr), 1, fp) != 1) {
24013b9f610SMax Laier 			dpcap = NULL;
24113b9f610SMax Laier 			fclose(fp);
24213b9f610SMax Laier 			return (-1);
24313b9f610SMax Laier 		}
24413b9f610SMax Laier 		return (0);
24513b9f610SMax Laier 	}
24613b9f610SMax Laier 
24713b9f610SMax Laier 	/*
24813b9f610SMax Laier 	 * XXX Must read the file, compare the header against our new
24913b9f610SMax Laier 	 * options (in particular, snaplen) and adjust our options so
25013b9f610SMax Laier 	 * that we generate a correct file.
25113b9f610SMax Laier 	 */
25213b9f610SMax Laier 	(void) fseek(fp, 0L, SEEK_SET);
25313b9f610SMax Laier 	if (fread((char *)&hdr, sizeof(hdr), 1, fp) == 1) {
25413b9f610SMax Laier 		if (hdr.magic != TCPDUMP_MAGIC ||
25513b9f610SMax Laier 		    hdr.version_major != PCAP_VERSION_MAJOR ||
25613b9f610SMax Laier 		    hdr.version_minor != PCAP_VERSION_MINOR ||
25713b9f610SMax Laier 		    hdr.linktype != hpcap->linktype) {
25813b9f610SMax Laier 			logmsg(LOG_ERR,
25913b9f610SMax Laier 			    "Invalid/incompatible log file, move it away");
26013b9f610SMax Laier 			fclose(fp);
26113b9f610SMax Laier 			return (1);
26213b9f610SMax Laier 		    }
26313b9f610SMax Laier 		if (hdr.snaplen != snaplen) {
26413b9f610SMax Laier 			logmsg(LOG_WARNING,
26513b9f610SMax Laier 			    "Existing file specifies a snaplen of %u, using it",
26613b9f610SMax Laier 			    hdr.snaplen);
26713b9f610SMax Laier 			tmpsnap = snaplen;
26813b9f610SMax Laier 			snaplen = hdr.snaplen;
26913b9f610SMax Laier 			if (init_pcap()) {
27013b9f610SMax Laier 				logmsg(LOG_ERR, "Failed to re-initialize");
27113b9f610SMax Laier 				if (hpcap == 0)
27213b9f610SMax Laier 					return (-1);
27313b9f610SMax Laier 				logmsg(LOG_NOTICE,
27413b9f610SMax Laier 					"Using old settings, offset: %llu",
27513b9f610SMax Laier 					(unsigned long long)st.st_size);
27613b9f610SMax Laier 			}
27713b9f610SMax Laier 			snaplen = tmpsnap;
27813b9f610SMax Laier 		}
27913b9f610SMax Laier 	}
28013b9f610SMax Laier 
28113b9f610SMax Laier 	(void) fseek(fp, 0L, SEEK_END);
28213b9f610SMax Laier 	return (0);
28313b9f610SMax Laier }
28413b9f610SMax Laier 
28513b9f610SMax Laier int
28613b9f610SMax Laier main(int argc, char **argv)
28713b9f610SMax Laier {
28813b9f610SMax Laier 	struct pcap_stat pstat;
28913b9f610SMax Laier 	int ch, np;
29013b9f610SMax Laier 
29113b9f610SMax Laier 	while ((ch = getopt(argc, argv, "Dd:s:f:")) != -1) {
29213b9f610SMax Laier 		switch (ch) {
29313b9f610SMax Laier 		case 'D':
29413b9f610SMax Laier 			Debug = 1;
29513b9f610SMax Laier 			break;
29613b9f610SMax Laier 		case 'd':
29713b9f610SMax Laier 			delay = atoi(optarg);
29813b9f610SMax Laier 			if (delay < 5 || delay > 60*60)
29913b9f610SMax Laier 				usage();
30013b9f610SMax Laier 			break;
30113b9f610SMax Laier 		case 'f':
30213b9f610SMax Laier 			filename = optarg;
30313b9f610SMax Laier 			break;
30413b9f610SMax Laier 		case 's':
30513b9f610SMax Laier 			snaplen = atoi(optarg);
30613b9f610SMax Laier 			if (snaplen <= 0)
30713b9f610SMax Laier 				snaplen = DEF_SNAPLEN;
30813b9f610SMax Laier 			break;
30913b9f610SMax Laier 		default:
31013b9f610SMax Laier 			usage();
31113b9f610SMax Laier 		}
31213b9f610SMax Laier 
31313b9f610SMax Laier 	}
31413b9f610SMax Laier 
31513b9f610SMax Laier 	log_debug = Debug;
31613b9f610SMax Laier 	argc -= optind;
31713b9f610SMax Laier 	argv += optind;
31813b9f610SMax Laier 
31913b9f610SMax Laier 	if (!Debug) {
32013b9f610SMax Laier 		openlog("pflogd", LOG_PID | LOG_CONS, LOG_DAEMON);
32113b9f610SMax Laier 		if (daemon(0, 0)) {
32213b9f610SMax Laier 			logmsg(LOG_WARNING, "Failed to become daemon: %s",
32313b9f610SMax Laier 			    strerror(errno));
32413b9f610SMax Laier 		}
32513b9f610SMax Laier 		pidfile(NULL);
32613b9f610SMax Laier 	}
32713b9f610SMax Laier 
32813b9f610SMax Laier 	(void)umask(S_IRWXG | S_IRWXO);
32913b9f610SMax Laier 
33013b9f610SMax Laier 	signal(SIGTERM, sig_close);
33113b9f610SMax Laier 	signal(SIGINT, sig_close);
33213b9f610SMax Laier 	signal(SIGQUIT, sig_close);
33313b9f610SMax Laier 	signal(SIGALRM, sig_alrm);
33413b9f610SMax Laier 	signal(SIGHUP, sig_hup);
33513b9f610SMax Laier 	alarm(delay);
33613b9f610SMax Laier 
33713b9f610SMax Laier 	if (argc) {
33813b9f610SMax Laier 		filter = copy_argv(argv);
33913b9f610SMax Laier 		if (filter == NULL)
34013b9f610SMax Laier 			logmsg(LOG_NOTICE, "Failed to form filter expression");
34113b9f610SMax Laier 	}
34213b9f610SMax Laier 
34313b9f610SMax Laier 	if (init_pcap()) {
34413b9f610SMax Laier 		logmsg(LOG_ERR, "Exiting, init failure");
34513b9f610SMax Laier 		exit(1);
34613b9f610SMax Laier 	}
34713b9f610SMax Laier 
34813b9f610SMax Laier 	if (reset_dump()) {
34913b9f610SMax Laier 		logmsg(LOG_ERR, "Failed to open log file %s", filename);
35013b9f610SMax Laier 		pcap_close(hpcap);
35113b9f610SMax Laier 		exit(1);
35213b9f610SMax Laier 	}
35313b9f610SMax Laier 
35413b9f610SMax Laier 	while (1) {
35513b9f610SMax Laier 		np = pcap_dispatch(hpcap, PCAP_NUM_PKTS, pcap_dump, (u_char *)dpcap);
35613b9f610SMax Laier 		if (np < 0)
35713b9f610SMax Laier 			logmsg(LOG_NOTICE, "%s", pcap_geterr(hpcap));
35813b9f610SMax Laier 
35913b9f610SMax Laier 		if (gotsig_close)
36013b9f610SMax Laier 			break;
36113b9f610SMax Laier 		if (gotsig_hup) {
36213b9f610SMax Laier 			if (reset_dump()) {
36313b9f610SMax Laier 				logmsg(LOG_ERR, "Failed to open log file!");
36413b9f610SMax Laier 				break;
36513b9f610SMax Laier 			}
36613b9f610SMax Laier 			logmsg(LOG_NOTICE, "Reopened logfile");
36713b9f610SMax Laier 			gotsig_hup = 0;
36813b9f610SMax Laier 		}
36913b9f610SMax Laier 
37013b9f610SMax Laier 		if (gotsig_alrm) {
37113b9f610SMax Laier 			/* XXX pcap_dumper is an incomplete type which libpcap
37213b9f610SMax Laier 			 * casts to a FILE* currently.  For now it is safe to
37313b9f610SMax Laier 			 * make the same assumption, however this may change
37413b9f610SMax Laier 			 * in the future.
37513b9f610SMax Laier 			 */
37613b9f610SMax Laier 			if (dpcap) {
37713b9f610SMax Laier 				if (fflush((FILE *)dpcap) == EOF) {
37813b9f610SMax Laier 					break;
37913b9f610SMax Laier 				}
38013b9f610SMax Laier 			}
38113b9f610SMax Laier 			gotsig_alrm = 0;
38213b9f610SMax Laier 			alarm(delay);
38313b9f610SMax Laier 		}
38413b9f610SMax Laier 	}
38513b9f610SMax Laier 
38613b9f610SMax Laier 	logmsg(LOG_NOTICE, "Exiting due to signal");
38713b9f610SMax Laier 	if (dpcap)
38813b9f610SMax Laier 		pcap_dump_close(dpcap);
38913b9f610SMax Laier 
39013b9f610SMax Laier 	if (pcap_stats(hpcap, &pstat) < 0)
39113b9f610SMax Laier 		logmsg(LOG_WARNING, "Reading stats: %s", pcap_geterr(hpcap));
39213b9f610SMax Laier 	else
39313b9f610SMax Laier 		logmsg(LOG_NOTICE, "%u packets received, %u dropped",
39413b9f610SMax Laier 		    pstat.ps_recv, pstat.ps_drop);
39513b9f610SMax Laier 
39613b9f610SMax Laier 	pcap_close(hpcap);
39713b9f610SMax Laier 	if (!Debug)
39813b9f610SMax Laier 		closelog();
39913b9f610SMax Laier 	return (0);
40013b9f610SMax Laier }
401