xref: /illumos-gate/usr/src/cmd/cmd-inet/lib/nwamd/main.c (revision b00044a2eb43864b8718585d21949611a2ee59ef)
1d71dbb73Sjbeck /*
2d71dbb73Sjbeck  * CDDL HEADER START
3d71dbb73Sjbeck  *
4d71dbb73Sjbeck  * The contents of this file are subject to the terms of the
5d71dbb73Sjbeck  * Common Development and Distribution License (the "License").
6d71dbb73Sjbeck  * You may not use this file except in compliance with the License.
7d71dbb73Sjbeck  *
8d71dbb73Sjbeck  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9d71dbb73Sjbeck  * or http://www.opensolaris.org/os/licensing.
10d71dbb73Sjbeck  * See the License for the specific language governing permissions
11d71dbb73Sjbeck  * and limitations under the License.
12d71dbb73Sjbeck  *
13d71dbb73Sjbeck  * When distributing Covered Code, include this CDDL HEADER in each
14d71dbb73Sjbeck  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15d71dbb73Sjbeck  * If applicable, add the following below this CDDL HEADER, with the
16d71dbb73Sjbeck  * fields enclosed by brackets "[]" replaced with your own identifying
17d71dbb73Sjbeck  * information: Portions Copyright [yyyy] [name of copyright owner]
18d71dbb73Sjbeck  *
19d71dbb73Sjbeck  * CDDL HEADER END
20d71dbb73Sjbeck  */
21d71dbb73Sjbeck 
22d71dbb73Sjbeck /*
23*b00044a2SJames Carlson  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24d71dbb73Sjbeck  * Use is subject to license terms.
25d71dbb73Sjbeck  */
26d71dbb73Sjbeck 
27d71dbb73Sjbeck /*
28d71dbb73Sjbeck  * nwamd - NetWork Auto-Magic Daemon
29d71dbb73Sjbeck  */
30d71dbb73Sjbeck 
31d71dbb73Sjbeck #include <fcntl.h>
32d71dbb73Sjbeck #include <priv.h>
33d71dbb73Sjbeck #include <pthread.h>
34d71dbb73Sjbeck #include <pwd.h>
35d71dbb73Sjbeck #include <stdio.h>
36d71dbb73Sjbeck #include <stdlib.h>
37d71dbb73Sjbeck #include <string.h>
38d71dbb73Sjbeck #include <signal.h>
39d71dbb73Sjbeck #include <sys/stat.h>
40d71dbb73Sjbeck #include <sys/types.h>
41d71dbb73Sjbeck #include <sys/wait.h>
42d71dbb73Sjbeck #include <syslog.h>
43d71dbb73Sjbeck #include <unistd.h>
44d71dbb73Sjbeck #include <locale.h>
45d71dbb73Sjbeck #include <libintl.h>
46d71dbb73Sjbeck #include <errno.h>
47d71dbb73Sjbeck 
48d71dbb73Sjbeck #include "defines.h"
49d71dbb73Sjbeck #include "structures.h"
50d71dbb73Sjbeck #include "functions.h"
51d71dbb73Sjbeck #include "variables.h"
52d71dbb73Sjbeck 
53d71dbb73Sjbeck #define	TIMESPECGT(x, y)	((x.tv_sec > y.tv_sec) || \
54d71dbb73Sjbeck 	    ((x.tv_sec == y.tv_sec) && (x.tv_nsec > y.tv_nsec)))
55d71dbb73Sjbeck 
56d71dbb73Sjbeck const char *OUR_FMRI = "svc:/network/physical:nwam";
57d71dbb73Sjbeck const char *OUR_PG = "nwamd";
58d71dbb73Sjbeck 
59d71dbb73Sjbeck boolean_t fg = B_FALSE;
60d71dbb73Sjbeck boolean_t shutting_down;
61d71dbb73Sjbeck sigset_t original_sigmask;
62d71dbb73Sjbeck char zonename[ZONENAME_MAX];
63*b00044a2SJames Carlson pthread_mutex_t machine_lock = PTHREAD_MUTEX_INITIALIZER;
64d71dbb73Sjbeck 
65d71dbb73Sjbeck /*
66d71dbb73Sjbeck  * nwamd
67d71dbb73Sjbeck  *
68d71dbb73Sjbeck  * This is the Network Auto-Magic daemon.  For further high level information
69d71dbb73Sjbeck  * see the Network Auto-Magic project and the Approachability communities
70d71dbb73Sjbeck  * on opensolaris.org, and nwamd(1M).
71d71dbb73Sjbeck  *
72d71dbb73Sjbeck  * The general structure of the code is as a set of threads collecting
73d71dbb73Sjbeck  * system events which are fed into a state machine which alters system
74d71dbb73Sjbeck  * state based on configuration.
75d71dbb73Sjbeck  *
76d71dbb73Sjbeck  * signal management
77d71dbb73Sjbeck  * Due to being threaded, a simple set of signal handlers would not work
78d71dbb73Sjbeck  * very well for nwamd.  Instead nwamd blocks signals at startup and
79d71dbb73Sjbeck  * then starts a thread which sits in sigwait(2) waiting for signals.
80d71dbb73Sjbeck  * When a signal is received the signal handling thread dispatches it.
81d71dbb73Sjbeck  * It handles:
82d71dbb73Sjbeck  * - shutting down, done by creating an event which is passed through the
83d71dbb73Sjbeck  *   system allowing the various subsystems to do any necessary cleanup.
84d71dbb73Sjbeck  * - SIGALRM for timers.
85d71dbb73Sjbeck  * - SIGHUP for instance refresh, which tells us to look up various
86d71dbb73Sjbeck  *   properties from SMF(5).
87d71dbb73Sjbeck  *
88d71dbb73Sjbeck  * subprocess management
89d71dbb73Sjbeck  * nwamd starts several different subprocesses to manage the system.  Some
90d71dbb73Sjbeck  * of those start other processes (e.g. `ifconfig <if> dhcp` ends up starting
91d71dbb73Sjbeck  * dhcpagent if necessary).  Due to the way we manage signals if we started
92d71dbb73Sjbeck  * those up without doing anything special their signal mask would mostly
93d71dbb73Sjbeck  * block signals.  So we restore the signal mask when we start subprocesses.
94d71dbb73Sjbeck  * This is especially important with respect to DHCP as later when we exit
95d71dbb73Sjbeck  * we need to kill the dhcpagent process which we started; for details, see
96d71dbb73Sjbeck  * the block comment in state_machine.c in its cleanup() function.
97d71dbb73Sjbeck  */
98d71dbb73Sjbeck 
99d71dbb73Sjbeck /*
100d71dbb73Sjbeck  * In this file there are several utility functions which might otherwise
101d71dbb73Sjbeck  * belong in util.c, but since they are only called from main(), they can
102d71dbb73Sjbeck  * live here as static functions:
103d71dbb73Sjbeck  * - syslog set-up
104d71dbb73Sjbeck  * - daemonizing
105d71dbb73Sjbeck  * - looking up SMF(5) properties
106d71dbb73Sjbeck  * - signal handling
107d71dbb73Sjbeck  * - managing privileges(5)
108d71dbb73Sjbeck  */
109d71dbb73Sjbeck 
110d71dbb73Sjbeck static void
111d71dbb73Sjbeck start_logging(void)
112d71dbb73Sjbeck {
113d71dbb73Sjbeck 	openlog("nwamd", LOG_PID | LOG_NDELAY, LOG_DAEMON);
114d71dbb73Sjbeck }
115d71dbb73Sjbeck 
116d71dbb73Sjbeck static void
117d71dbb73Sjbeck daemonize(void)
118d71dbb73Sjbeck {
119d71dbb73Sjbeck 	pid_t pid;
120d71dbb73Sjbeck 
121d71dbb73Sjbeck 	/*
122d71dbb73Sjbeck 	 * A little bit of magic here.  By the first fork+setsid, we
123d71dbb73Sjbeck 	 * disconnect from our current controlling terminal and become
124d71dbb73Sjbeck 	 * a session group leader.  By forking again without calling
125d71dbb73Sjbeck 	 * setsid again, we make certain that we are not the session
126d71dbb73Sjbeck 	 * group leader and can never reacquire a controlling terminal.
127d71dbb73Sjbeck 	 */
128d71dbb73Sjbeck 	if ((pid = fork()) == (pid_t)-1) {
129d71dbb73Sjbeck 		syslog(LOG_ERR, "fork 1 failed");
130d71dbb73Sjbeck 		exit(EXIT_FAILURE);
131d71dbb73Sjbeck 	}
132d71dbb73Sjbeck 	if (pid != 0) {
133d71dbb73Sjbeck 		(void) wait(NULL);
13410e672b7Smeem 		dprintf("child %ld exited, daemonizing", pid);
135d71dbb73Sjbeck 		_exit(0);
136d71dbb73Sjbeck 	}
137d71dbb73Sjbeck 	if (setsid() == (pid_t)-1) {
138d71dbb73Sjbeck 		syslog(LOG_ERR, "setsid");
139d71dbb73Sjbeck 		exit(EXIT_FAILURE);
140d71dbb73Sjbeck 	}
141d71dbb73Sjbeck 	if ((pid = fork()) == (pid_t)-1) {
142d71dbb73Sjbeck 		syslog(LOG_ERR, "fork 2 failed");
143d71dbb73Sjbeck 		exit(EXIT_FAILURE);
144d71dbb73Sjbeck 	}
145d71dbb73Sjbeck 	if (pid != 0) {
146d71dbb73Sjbeck 		_exit(0);
147d71dbb73Sjbeck 	}
148d71dbb73Sjbeck 	(void) chdir("/");
149d71dbb73Sjbeck 	(void) umask(022);
150d71dbb73Sjbeck }
151d71dbb73Sjbeck 
152d71dbb73Sjbeck /*
153d71dbb73Sjbeck  * Look up nwamd property values and set daemon variables appropriately.
154d71dbb73Sjbeck  * This function will be called on startup and via the signal handling
155d71dbb73Sjbeck  * thread on receiving a HUP (which occurs when the nwam service is
156d71dbb73Sjbeck  * refreshed).
157d71dbb73Sjbeck  */
158d71dbb73Sjbeck static void
159d71dbb73Sjbeck lookup_daemon_properties(void)
160d71dbb73Sjbeck {
161d71dbb73Sjbeck 	boolean_t debug_set;
162d71dbb73Sjbeck 	uint64_t scan_interval;
163*b00044a2SJames Carlson 	uint64_t idle_time;
164d71dbb73Sjbeck 
165d71dbb73Sjbeck 	if (lookup_boolean_property(OUR_PG, "debug", &debug_set) == 0)
166d71dbb73Sjbeck 		debug = debug_set;
167d71dbb73Sjbeck 	if (lookup_count_property(OUR_PG, "scan_interval", &scan_interval) == 0)
168d71dbb73Sjbeck 		wlan_scan_interval = scan_interval;
169*b00044a2SJames Carlson 	if (lookup_count_property(OUR_PG, "idle_time", &idle_time) == 0)
170*b00044a2SJames Carlson 		door_idle_time = idle_time;
171d71dbb73Sjbeck 	dprintf("Read daemon configuration properties.");
172d71dbb73Sjbeck }
173d71dbb73Sjbeck 
174d71dbb73Sjbeck /* ARGSUSED */
175d71dbb73Sjbeck static void *
176d71dbb73Sjbeck sighandler(void *arg)
177d71dbb73Sjbeck {
178d71dbb73Sjbeck 	sigset_t sigset;
179d71dbb73Sjbeck 	int sig;
180d71dbb73Sjbeck 	uint32_t now;
181d71dbb73Sjbeck 
182d71dbb73Sjbeck 	(void) sigfillset(&sigset);
183d71dbb73Sjbeck 
184*b00044a2SJames Carlson 	while (!shutting_down) {
185d71dbb73Sjbeck 		sig = sigwait(&sigset);
186d71dbb73Sjbeck 		dprintf("signal %d caught", sig);
187d71dbb73Sjbeck 		switch (sig) {
188d71dbb73Sjbeck 		case SIGALRM:
189d71dbb73Sjbeck 			/*
190d71dbb73Sjbeck 			 * We may have multiple interfaces with
191d71dbb73Sjbeck 			 * scheduled timers; walk the list and
192d71dbb73Sjbeck 			 * create a timer event for each one.
193d71dbb73Sjbeck 			 */
194d71dbb73Sjbeck 			timer_expire = TIMER_INFINITY;
195d71dbb73Sjbeck 			now = NSEC_TO_SEC(gethrtime());
196*b00044a2SJames Carlson 			check_interface_timers(now);
197*b00044a2SJames Carlson 			check_door_life(now);
198d71dbb73Sjbeck 			break;
199d71dbb73Sjbeck 		case SIGHUP:
200d71dbb73Sjbeck 			/*
201d71dbb73Sjbeck 			 * Refresh action - reread configuration properties.
202d71dbb73Sjbeck 			 */
203d71dbb73Sjbeck 			lookup_daemon_properties();
204d71dbb73Sjbeck 			break;
205*b00044a2SJames Carlson 		case SIGINT:
206*b00044a2SJames Carlson 			/*
207*b00044a2SJames Carlson 			 * Undocumented "print debug status" signal.
208*b00044a2SJames Carlson 			 */
209*b00044a2SJames Carlson 			print_llp_status();
210*b00044a2SJames Carlson 			print_interface_status();
211*b00044a2SJames Carlson 			print_wireless_status();
212*b00044a2SJames Carlson 			break;
213d71dbb73Sjbeck 		default:
214d71dbb73Sjbeck 			syslog(LOG_NOTICE, "%s received, shutting down",
215d71dbb73Sjbeck 			    strsignal(sig));
216d71dbb73Sjbeck 			shutting_down = B_TRUE;
217*b00044a2SJames Carlson 			if (!np_queue_add_event(EV_SHUTDOWN, NULL)) {
218d71dbb73Sjbeck 				dprintf("could not allocate shutdown event");
219d71dbb73Sjbeck 				cleanup();
220d71dbb73Sjbeck 				exit(EXIT_FAILURE);
221d71dbb73Sjbeck 			}
222d71dbb73Sjbeck 			break;
223d71dbb73Sjbeck 		}
224d71dbb73Sjbeck 	}
225*b00044a2SJames Carlson 	return (NULL);
226d71dbb73Sjbeck }
227d71dbb73Sjbeck 
228d71dbb73Sjbeck static void
229d71dbb73Sjbeck init_signalhandling(void)
230d71dbb73Sjbeck {
231d71dbb73Sjbeck 	pthread_attr_t attr;
232d71dbb73Sjbeck 	pthread_t sighand;
233d71dbb73Sjbeck 	int err;
234d71dbb73Sjbeck 	sigset_t new;
235d71dbb73Sjbeck 
236d71dbb73Sjbeck 	(void) sigfillset(&new);
237d71dbb73Sjbeck 	(void) pthread_sigmask(SIG_BLOCK, &new, &original_sigmask);
238d71dbb73Sjbeck 	(void) pthread_attr_init(&attr);
239d71dbb73Sjbeck 	(void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
240d71dbb73Sjbeck 	if (err = pthread_create(&sighand, &attr, sighandler, NULL)) {
241d71dbb73Sjbeck 		syslog(LOG_ERR, "pthread_create system: %s", strerror(err));
242d71dbb73Sjbeck 		exit(EXIT_FAILURE);
243d71dbb73Sjbeck 	} else {
244d71dbb73Sjbeck 		dprintf("signal handler thread: %d", sighand);
245d71dbb73Sjbeck 	}
246d71dbb73Sjbeck 	(void) pthread_attr_destroy(&attr);
247d71dbb73Sjbeck }
248d71dbb73Sjbeck 
249d71dbb73Sjbeck static void
250d71dbb73Sjbeck change_user_set_privs(void)
251d71dbb73Sjbeck {
252d71dbb73Sjbeck 	priv_set_t *priv_set;
253d71dbb73Sjbeck 
254d71dbb73Sjbeck 	priv_set = priv_allocset();
255d71dbb73Sjbeck 	if (getppriv(PRIV_PERMITTED, priv_set) == -1) {
256d71dbb73Sjbeck 		dprintf("getppriv %s", strerror(errno));
257d71dbb73Sjbeck 	} else {
258d71dbb73Sjbeck 		char *p;
259d71dbb73Sjbeck 
260d71dbb73Sjbeck 		p = priv_set_to_str(priv_set, ',', 0);
261d71dbb73Sjbeck 		dprintf("started with privs %s", p != NULL ? p : "Unknown");
262d71dbb73Sjbeck 		free(p);
263d71dbb73Sjbeck 	}
2649b38c944Sokie 	priv_freeset(priv_set);
265d71dbb73Sjbeck 
2669b38c944Sokie 	/* always start with the basic set */
2679b38c944Sokie 	priv_set = priv_str_to_set("basic", ",", NULL);
2689b38c944Sokie 	if (priv_set == NULL) {
2699b38c944Sokie 		syslog(LOG_ERR, "converting basic privilege set: %m");
2709b38c944Sokie 		exit(EXIT_FAILURE);
2719b38c944Sokie 	}
2729b38c944Sokie 	(void) priv_addset(priv_set, PRIV_FILE_CHOWN_SELF);
2739b38c944Sokie 	(void) priv_addset(priv_set, PRIV_FILE_DAC_READ);
2749b38c944Sokie 	(void) priv_addset(priv_set, PRIV_FILE_DAC_WRITE);
2759b38c944Sokie 	(void) priv_addset(priv_set, PRIV_NET_PRIVADDR);
2769b38c944Sokie 	(void) priv_addset(priv_set, PRIV_NET_RAWACCESS);
277*b00044a2SJames Carlson 	(void) priv_addset(priv_set, PRIV_PROC_AUDIT);
2789b38c944Sokie 	(void) priv_addset(priv_set, PRIV_PROC_OWNER);
2799b38c944Sokie 	(void) priv_addset(priv_set, PRIV_PROC_SETID);
280*b00044a2SJames Carlson 	(void) priv_addset(priv_set, PRIV_SYS_CONFIG);
2819b38c944Sokie 	(void) priv_addset(priv_set, PRIV_SYS_IP_CONFIG);
2829b38c944Sokie 	(void) priv_addset(priv_set, PRIV_SYS_IPC_CONFIG);
2839b38c944Sokie 	(void) priv_addset(priv_set, PRIV_SYS_NET_CONFIG);
2849b38c944Sokie 	(void) priv_addset(priv_set, PRIV_SYS_RES_CONFIG);
2859b38c944Sokie 	(void) priv_addset(priv_set, PRIV_SYS_RESOURCE);
286d71dbb73Sjbeck 
287d71dbb73Sjbeck 	if (setppriv(PRIV_SET, PRIV_INHERITABLE, priv_set) == -1) {
288d71dbb73Sjbeck 		syslog(LOG_ERR, "setppriv inheritable: %m");
289d71dbb73Sjbeck 		priv_freeset(priv_set);
290d71dbb73Sjbeck 		exit(EXIT_FAILURE);
291d71dbb73Sjbeck 	}
292d71dbb73Sjbeck 
293d71dbb73Sjbeck 	if (setppriv(PRIV_SET, PRIV_PERMITTED, priv_set) == -1) {
294d71dbb73Sjbeck 		syslog(LOG_ERR, "setppriv permitted: %m");
295d71dbb73Sjbeck 		priv_freeset(priv_set);
296d71dbb73Sjbeck 		exit(EXIT_FAILURE);
297d71dbb73Sjbeck 	}
298d71dbb73Sjbeck 
299d71dbb73Sjbeck 	if (setppriv(PRIV_SET, PRIV_EFFECTIVE, priv_set) == -1) {
300d71dbb73Sjbeck 		syslog(LOG_ERR, "setppriv effective: %m");
301d71dbb73Sjbeck 		priv_freeset(priv_set);
302d71dbb73Sjbeck 		exit(EXIT_FAILURE);
303d71dbb73Sjbeck 	}
304d71dbb73Sjbeck 
305d71dbb73Sjbeck 	priv_freeset(priv_set);
306d71dbb73Sjbeck }
307d71dbb73Sjbeck 
308*b00044a2SJames Carlson static void
309*b00044a2SJames Carlson init_machine_mutex(void)
310*b00044a2SJames Carlson {
311*b00044a2SJames Carlson 	pthread_mutexattr_t attrs;
312*b00044a2SJames Carlson 
313*b00044a2SJames Carlson 	(void) pthread_mutexattr_init(&attrs);
314*b00044a2SJames Carlson 	(void) pthread_mutexattr_settype(&attrs, PTHREAD_MUTEX_ERRORCHECK);
315*b00044a2SJames Carlson 	if (pthread_mutex_init(&machine_lock, &attrs) != 0) {
316*b00044a2SJames Carlson 		syslog(LOG_ERR, "unable to set up machine lock");
317*b00044a2SJames Carlson 		exit(EXIT_FAILURE);
318*b00044a2SJames Carlson 	}
319*b00044a2SJames Carlson 	(void) pthread_mutexattr_destroy(&attrs);
320*b00044a2SJames Carlson }
321*b00044a2SJames Carlson 
322d71dbb73Sjbeck int
323d71dbb73Sjbeck main(int argc, char *argv[])
324d71dbb73Sjbeck {
325d71dbb73Sjbeck 	int c;
326d71dbb73Sjbeck 	int scan_lev;
327d71dbb73Sjbeck 	struct np_event *e;
328*b00044a2SJames Carlson 	enum np_event_type etype;
329d71dbb73Sjbeck 
330d71dbb73Sjbeck 	(void) setlocale(LC_ALL, "");
331d71dbb73Sjbeck 	(void) textdomain(TEXT_DOMAIN);
332d71dbb73Sjbeck 
333d71dbb73Sjbeck 	shutting_down = B_FALSE;
334d71dbb73Sjbeck 	start_logging();
335d71dbb73Sjbeck 	syslog(LOG_INFO, "nwamd pid %d started", getpid());
336d71dbb73Sjbeck 
337d71dbb73Sjbeck 	while ((c = getopt(argc, argv, "fs:")) != -1) {
338d71dbb73Sjbeck 		switch (c) {
339d71dbb73Sjbeck 			case 'f':
340d71dbb73Sjbeck 				fg = B_TRUE;
341d71dbb73Sjbeck 				break;
342d71dbb73Sjbeck 			case 's':
343d71dbb73Sjbeck 				scan_lev = atoi(optarg);
344d71dbb73Sjbeck 				if (scan_lev >= DLADM_WLAN_STRENGTH_VERY_WEAK &&
345d71dbb73Sjbeck 				    scan_lev <= DLADM_WLAN_STRENGTH_EXCELLENT) {
346d71dbb73Sjbeck 					wireless_scan_level = scan_lev;
347d71dbb73Sjbeck 				} else {
348d71dbb73Sjbeck 					syslog(LOG_ERR, "invalid signal "
349d71dbb73Sjbeck 					    "strength: %s", optarg);
350d71dbb73Sjbeck 				}
351d71dbb73Sjbeck 				break;
352d71dbb73Sjbeck 			default:
353d71dbb73Sjbeck 				syslog(LOG_ERR, "unrecognized option %c",
354d71dbb73Sjbeck 				    optopt);
355d71dbb73Sjbeck 				break;
356d71dbb73Sjbeck 		}
357d71dbb73Sjbeck 	}
358d71dbb73Sjbeck 
359d71dbb73Sjbeck 	lookup_daemon_properties();
360d71dbb73Sjbeck 
361d71dbb73Sjbeck 	change_user_set_privs();
362d71dbb73Sjbeck 
363d71dbb73Sjbeck 	if (!fg)
364d71dbb73Sjbeck 		daemonize();
365d71dbb73Sjbeck 
366*b00044a2SJames Carlson 	initialize_llp();
367*b00044a2SJames Carlson 
368d71dbb73Sjbeck 	init_signalhandling();
369d71dbb73Sjbeck 
370*b00044a2SJames Carlson 	initialize_wireless();
371d71dbb73Sjbeck 
372d71dbb73Sjbeck 	lookup_zonename(zonename, sizeof (zonename));
373d71dbb73Sjbeck 
374*b00044a2SJames Carlson 	init_machine_mutex();
375*b00044a2SJames Carlson 
376d71dbb73Sjbeck 	initialize_interfaces();
377d71dbb73Sjbeck 
378d71dbb73Sjbeck 	llp_parse_config();
379d71dbb73Sjbeck 
380*b00044a2SJames Carlson 	initialize_door();
381*b00044a2SJames Carlson 
382d71dbb73Sjbeck 	(void) start_event_collection();
383d71dbb73Sjbeck 
384*b00044a2SJames Carlson 	while ((e = np_queue_get_event()) != NULL) {
385d71dbb73Sjbeck 
386*b00044a2SJames Carlson 		etype = e->npe_type;
387*b00044a2SJames Carlson 		syslog(LOG_INFO, "got event type %s", npe_type_str(etype));
388*b00044a2SJames Carlson 		if (etype == EV_SHUTDOWN)
389*b00044a2SJames Carlson 			terminate_door();
390*b00044a2SJames Carlson 		if (pthread_mutex_lock(&machine_lock) != 0) {
391*b00044a2SJames Carlson 			syslog(LOG_ERR, "mutex lock");
392*b00044a2SJames Carlson 			exit(EXIT_FAILURE);
393*b00044a2SJames Carlson 		}
394d71dbb73Sjbeck 		state_machine(e);
395*b00044a2SJames Carlson 		(void) pthread_mutex_unlock(&machine_lock);
396d71dbb73Sjbeck 		free_event(e);
397*b00044a2SJames Carlson 		if (etype == EV_SHUTDOWN)
398d71dbb73Sjbeck 			break;
399*b00044a2SJames Carlson 	}
400*b00044a2SJames Carlson 	syslog(LOG_DEBUG, "terminating routing and scanning threads");
401d71dbb73Sjbeck 	(void) pthread_cancel(routing);
402d71dbb73Sjbeck 	(void) pthread_cancel(scan);
403d71dbb73Sjbeck 	(void) pthread_join(routing, NULL);
404d71dbb73Sjbeck 	(void) pthread_join(scan, NULL);
405d71dbb73Sjbeck 	syslog(LOG_INFO, "nwamd shutting down");
406*b00044a2SJames Carlson 	return (EXIT_SUCCESS);
407d71dbb73Sjbeck }
408