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