xref: /illumos-gate/usr/src/cmd/cmd-inet/lib/nwamd/main.c (revision 2e0fe3efe5f9d579d4e44b3532d8e342c68b40ca)
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 (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
24  */
25 
26 #include <errno.h>
27 #include <fcntl.h>
28 #include <inetcfg.h>
29 #include <libdllink.h>
30 #include <libintl.h>
31 #include <libnwam.h>
32 #include <locale.h>
33 #include <priv.h>
34 #include <pthread.h>
35 #include <signal.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <sys/stat.h>
40 #include <sys/types.h>
41 #include <sys/wait.h>
42 #include <unistd.h>
43 
44 #include <libnwam.h>
45 #include "conditions.h"
46 #include "events.h"
47 #include "llp.h"
48 #include "ncp.h"
49 #include "objects.h"
50 #include "util.h"
51 
52 /*
53  * nwamd - NetWork Auto-Magic Daemon
54  */
55 
56 boolean_t fg = B_FALSE;
57 dladm_handle_t dld_handle = NULL;
58 boolean_t shutting_down = B_FALSE;
59 
60 sigset_t original_sigmask;
61 static sigset_t sigwaitset;
62 
63 static void nwamd_refresh(void);
64 static void graceful_shutdown(void);
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, nwamd(1M), and the README in the source directory.
72  *
73  * The general structure of the code is as a set of event source threads
74  * which feed events into the event handling thread. Some of these events
75  * are internal-only (e.g UPGRADE), but some also involve propogation
76  * to external listeners (who register via a door call into the daemon).
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 in all but the
81  * signal handling thread at startup.
82  *
83  */
84 
85 /*
86  * In this file there are several utility functions which might otherwise
87  * belong in util.c, but since they are only called from main(), they can
88  * live here as static functions:
89  * - nlog set-up
90  * - daemonizing
91  * - looking up SMF(5) properties
92  * - signal handling
93  * - managing privileges(5)
94  */
95 
96 static void
97 start_logging(void)
98 {
99 	openlog("nwamd", LOG_PID | LOG_NDELAY, LOG_DAEMON);
100 }
101 
102 static void
103 daemonize(void)
104 {
105 	pid_t pid;
106 
107 	/*
108 	 * A little bit of magic here.  By the first fork+setsid, we
109 	 * disconnect from our current controlling terminal and become
110 	 * a session group leader.  By forking again without calling
111 	 * setsid again, we make certain that we are not the session
112 	 * group leader and can never reacquire a controlling terminal.
113 	 */
114 	if ((pid = fork()) == (pid_t)-1)
115 		pfail("fork 1 failed");
116 	if (pid != 0) {
117 		(void) wait(NULL);
118 		nlog(LOG_DEBUG, "child %ld exited, daemonizing", pid);
119 		_exit(0);
120 	}
121 	if (setsid() == (pid_t)-1)
122 		pfail("setsid");
123 	if ((pid = fork()) == (pid_t)-1)
124 		pfail("fork 2 failed");
125 	if (pid != 0) {
126 		_exit(0);
127 	}
128 	(void) chdir("/");
129 	(void) umask(022);
130 }
131 
132 /* ARGSUSED */
133 static void *
134 sighandler(void *arg)
135 {
136 	int sig;
137 
138 	while (!shutting_down) {
139 		sig = sigwait(&sigwaitset);
140 		nlog(LOG_DEBUG, "signal %s caught", strsignal(sig));
141 
142 		switch (sig) {
143 		case SIGTHAW:
144 		case SIGHUP:
145 			/*
146 			 * Resumed from suspend or refresh.  Clear up all
147 			 * objects so their states start from scratch;
148 			 * then refresh().
149 			 */
150 			nwamd_fini_enms();
151 			nwamd_fini_ncus();
152 			nwamd_fini_locs();
153 			nwamd_refresh();
154 			break;
155 		case SIGUSR1:
156 			/*
157 			 * Undocumented "log ncu list" signal.
158 			 */
159 			nwamd_log_ncus();
160 			break;
161 		case SIGTERM:
162 			nlog(LOG_DEBUG, "%s received, shutting down",
163 			    strsignal(sig));
164 			graceful_shutdown();
165 			break;
166 		default:
167 			nlog(LOG_DEBUG, "unexpected signal %s received, "
168 			    "ignoring", strsignal(sig));
169 			break;
170 		}
171 	}
172 	return (NULL);
173 }
174 
175 static void
176 init_signalhandling(void)
177 {
178 	pthread_attr_t attr;
179 	pthread_t sighand;
180 	int err;
181 
182 	(void) pthread_attr_init(&attr);
183 	(void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
184 	if (err = pthread_create(&sighand, &attr, sighandler, NULL)) {
185 		nlog(LOG_ERR, "pthread_create system: %s", strerror(err));
186 		exit(EXIT_FAILURE);
187 	} else {
188 		nlog(LOG_DEBUG, "signal handler thread: %d", sighand);
189 	}
190 	(void) pthread_attr_destroy(&attr);
191 }
192 
193 /*
194  * Construct the set of signals that we explicitly want to deal with.
195  * We block these while we're still single-threaded; this block will
196  * be inherited by all the threads we create.  When we are ready to
197  * start handling signals, we will start the signal handling thread,
198  * which will sigwait() this same set of signals, and will thus receive
199  * and handle any that are sent to the process.
200  */
201 static void
202 block_signals(void)
203 {
204 	(void) sigemptyset(&sigwaitset);
205 	(void) sigaddset(&sigwaitset, SIGHUP);
206 	(void) sigaddset(&sigwaitset, SIGUSR1);
207 	(void) sigaddset(&sigwaitset, SIGUSR2);
208 	(void) sigaddset(&sigwaitset, SIGTERM);
209 	(void) sigaddset(&sigwaitset, SIGTHAW);
210 	(void) pthread_sigmask(SIG_BLOCK, &sigwaitset, &original_sigmask);
211 }
212 
213 /*
214  * Look up nwamd property values and set daemon variables appropriately.
215  * This function will be called on startup and via the signal handling
216  * thread on receiving a HUP (which occurs when the nwam service is
217  * refreshed).
218  */
219 static void
220 lookup_daemon_properties(void)
221 {
222 	char *active_ncp_tmp;
223 	char *scan_level_tmp;
224 
225 	(void) nwamd_lookup_boolean_property(OUR_FMRI, OUR_PG,
226 	    OUR_DEBUG_PROP_NAME, &debug);
227 	(void) nwamd_lookup_boolean_property(OUR_FMRI, OUR_PG,
228 	    OUR_AUTOCONF_PROP_NAME, &wireless_autoconf);
229 	(void) nwamd_lookup_boolean_property(OUR_FMRI, OUR_PG,
230 	    OUR_STRICT_BSSID_PROP_NAME, &wireless_strict_bssid);
231 
232 	(void) pthread_mutex_lock(&active_ncp_mutex);
233 	if ((active_ncp_tmp = malloc(NWAM_MAX_NAME_LEN)) == NULL ||
234 	    nwamd_lookup_string_property(OUR_FMRI, OUR_PG,
235 	    OUR_ACTIVE_NCP_PROP_NAME, active_ncp_tmp, NWAM_MAX_NAME_LEN) != 0) {
236 		(void) strlcpy(active_ncp, NWAM_NCP_NAME_AUTOMATIC,
237 		    NWAM_MAX_NAME_LEN);
238 	} else {
239 		(void) strlcpy(active_ncp, active_ncp_tmp, NWAM_MAX_NAME_LEN);
240 	}
241 	(void) pthread_mutex_unlock(&active_ncp_mutex);
242 	free(active_ncp_tmp);
243 
244 	if (nwamd_lookup_count_property(OUR_FMRI, OUR_PG,
245 	    OUR_CONDITION_CHECK_INTERVAL_PROP_NAME,
246 	    &condition_check_interval) != 0)
247 		condition_check_interval = CONDITION_CHECK_INTERVAL_DEFAULT;
248 
249 	if ((scan_level_tmp = malloc(NWAM_MAX_NAME_LEN)) == NULL ||
250 	    nwamd_lookup_string_property(OUR_FMRI, OUR_PG,
251 	    OUR_WIRELESS_SCAN_LEVEL_PROP_NAME, scan_level_tmp,
252 	    NWAM_MAX_NAME_LEN) != 0) {
253 		wireless_scan_level = WIRELESS_SCAN_LEVEL_DEFAULT;
254 	} else {
255 		if (dladm_wlan_str2strength(scan_level_tmp,
256 		    &wireless_scan_level) != DLADM_STATUS_OK)
257 			wireless_scan_level = DLADM_WLAN_STRENGTH_VERY_WEAK;
258 	}
259 	free(scan_level_tmp);
260 
261 	if (nwamd_lookup_count_property(OUR_FMRI, OUR_PG,
262 	    OUR_WIRELESS_SCAN_INTERVAL_PROP_NAME, &wireless_scan_interval) != 0)
263 		wireless_scan_interval = WIRELESS_SCAN_INTERVAL_DEFAULT;
264 
265 	if (nwamd_lookup_count_property(OUR_FMRI, OUR_PG,
266 	    OUR_NCU_WAIT_TIME_PROP_NAME, &ncu_wait_time) != 0)
267 		ncu_wait_time = NCU_WAIT_TIME_DEFAULT;
268 
269 	nlog(LOG_DEBUG, "Read daemon configuration properties.");
270 }
271 
272 /*
273  * Re-read the SMF properties.
274  * Reset ncu priority group (since the NCUs will have to walk
275  *   through their state machines again) and schedule a check
276  * Re-read objects from libnwam.
277  * Also, run condition checking for locations and ENMs.
278  */
279 static void
280 nwamd_refresh(void)
281 {
282 	lookup_daemon_properties();
283 
284 	(void) pthread_mutex_lock(&active_ncp_mutex);
285 	current_ncu_priority_group = INVALID_PRIORITY_GROUP;
286 	(void) pthread_mutex_unlock(&active_ncp_mutex);
287 
288 	nwamd_init_ncus();
289 	nwamd_init_enms();
290 	nwamd_init_locs();
291 
292 	nwamd_create_ncu_check_event(0);
293 	nwamd_create_triggered_condition_check_event(0);
294 }
295 
296 static void
297 graceful_shutdown(void)
298 {
299 	nwamd_event_t event;
300 
301 	shutting_down = B_TRUE;
302 	nwamd_event_sources_fini();
303 	nwamd_door_fini();
304 	nwamd_fini_enms();
305 	nwamd_fini_ncus();
306 	nwamd_fini_locs();
307 
308 	event = nwamd_event_init_shutdown();
309 	if (event == NULL)
310 		pfail("nwamd could not create shutdown event, exiting");
311 	nwamd_event_enqueue(event);
312 }
313 
314 int
315 main(int argc, char *argv[])
316 {
317 	int c;
318 	uint64_t version;
319 	nwamd_event_t event;
320 	dladm_status_t rc;
321 	uid_t uid = getuid();
322 
323 	/*
324 	 * Block the signals we care about (and which might cause us to
325 	 * exit based on default disposition) until we're ready to start
326 	 * handling them properly...see init_signalhandling() below.
327 	 */
328 	block_signals();
329 
330 	if (uid != UID_NETADM && uid != 0) {
331 		/*
332 		 * This shouldn't happen normally.  On upgrade the service might
333 		 * need reloading.
334 		 */
335 		pfail("nwamd should run as uid %d, not uid %d\n", UID_NETADM,
336 		    uid);
337 	}
338 
339 	(void) setlocale(LC_ALL, "");
340 	(void) textdomain(TEXT_DOMAIN);
341 
342 	start_logging();
343 	nlog(LOG_INFO, "nwamd pid %d started", getpid());
344 
345 	while ((c = getopt(argc, argv, "fs:")) != -1) {
346 		switch (c) {
347 			case 'f':
348 				fg = B_TRUE;
349 				break;
350 			default:
351 				nlog(LOG_ERR, "unrecognized option %c",
352 				    optopt);
353 				break;
354 		}
355 	}
356 
357 	lookup_daemon_properties();
358 
359 	if (!fg)
360 		daemonize();
361 
362 	/*
363 	 * The dladm handle *must* be opened before privileges are dropped.
364 	 * The device privilege requirements, which are stored in
365 	 * /etc/security/device_policy, may not be loaded yet, as that's
366 	 * done by svc:/system/filesystem/root.  If they are not loaded,
367 	 * then one must have *all* privs in order to open /dev/dld, which
368 	 * is one of the steps performed in dladm_open().
369 	 */
370 	rc = dladm_open(&dld_handle);
371 	if (rc != DLADM_STATUS_OK) {
372 		char status_str[DLADM_STRSIZE];
373 		(void) dladm_status2str(rc, status_str);
374 		pfail("failed to open dladm handle: %s", status_str);
375 	}
376 
377 	/*
378 	 * Create the event queue before starting event sources, including
379 	 * signal handling, so we are ready to handle incoming events.  Also
380 	 * start before attempting to upgrade, in case there's a problem
381 	 * upgrading and we need to retry (in which case we schedule an event
382 	 * to do so).
383 	 */
384 	nwamd_event_queue_init();
385 
386 	/*
387 	 * Handle upgrade of legacy config.  Absence of version property
388 	 * (which did not exist in phase 0 or 0.5) is the indication that
389 	 * we need to upgrade to phase 1 (version 1).
390 	 */
391 	if (nwamd_lookup_count_property(OUR_FMRI, OUR_PG, OUR_VERSION_PROP_NAME,
392 	    &version) != 0)
393 		nwamd_handle_upgrade(NULL);
394 
395 	/*
396 	 * Initialize lists handling internal representations of objects.
397 	 */
398 	nwamd_object_lists_init();
399 
400 	init_signalhandling();
401 
402 	/* Enqueue init event */
403 	event = nwamd_event_init_init();
404 	if (event == NULL)
405 		pfail("nwamd could not create init event, exiting");
406 	nwamd_event_enqueue(event);
407 
408 	/*
409 	 * Collect initial user configuration.
410 	 */
411 
412 	/*
413 	 * Walk the physical interfaces and update the Automatic NCP to
414 	 * contain the IP and link NCUs for the interfaces that exist in
415 	 * the system.
416 	 */
417 	nwamd_walk_physical_configuration();
418 
419 	/*
420 	 * We should initialize the door at the point that we can respond to
421 	 * user requests about the system but before we start actually process
422 	 * state changes or effecting the system.
423 	 */
424 	nwamd_door_init();
425 
426 	/*
427 	 * Initialize data objects.
428 	 *
429 	 * Enabling an NCP involves refreshing nwam, which initializes the
430 	 * objects (ncu, enm, loc, known wlan).  Thus, no need to
431 	 * explicitly initialize these objects here.  The refresh also
432 	 * enqueues and NCU activation checking event.  Location and ENM
433 	 * condition checking are triggered by changes in NCU states.
434 	 */
435 	(void) pthread_mutex_lock(&active_ncp_mutex);
436 	if (nwamd_ncp_action(active_ncp, NWAM_ACTION_ENABLE) != 0)
437 		pfail("Initial enable failed for active NCP %s", active_ncp);
438 	(void) pthread_mutex_unlock(&active_ncp_mutex);
439 
440 	/*
441 	 * Enqueue an event to start periodic checking of activation conditions.
442 	 */
443 	nwamd_create_timed_condition_check_event();
444 
445 	/*
446 	 * These two routines safely minimize our privilege set.  They
447 	 * use reference counting to be safe in a threaded program.  It is
448 	 * gross that we escalate/deescalate to initialize this functionality
449 	 * but a real fix is to add functionality to do fine grained privs
450 	 * (and if necessary set uid to 0) in this threaded daemon.
451 	 */
452 	nwamd_escalate();
453 	nwamd_deescalate();
454 
455 	/*
456 	 * Start the various agents (hooks on fds, threads) which collect events
457 	 */
458 	nwamd_event_sources_init();
459 
460 	/*
461 	 * nwamd_event_handler() only returns on shutdown.
462 	 */
463 	nwamd_event_handler();
464 
465 	dladm_close(dld_handle);
466 
467 	return (EXIT_SUCCESS);
468 }
469