Home | History | Annotate | Download | only in nwamd
      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