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