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 <libdllink.h> 29 #include <libintl.h> 30 #include <libnwam.h> 31 #include <locale.h> 32 #include <priv.h> 33 #include <pthread.h> 34 #include <signal.h> 35 #include <stdio.h> 36 #include <stdlib.h> 37 #include <string.h> 38 #include <sys/stat.h> 39 #include <sys/types.h> 40 #include <sys/wait.h> 41 #include <unistd.h> 42 43 #include <libnwam.h> 44 #include "conditions.h" 45 #include "events.h" 46 #include "llp.h" 47 #include "ncp.h" 48 #include "objects.h" 49 #include "util.h" 50 51 /* 52 * nwamd - NetWork Auto-Magic Daemon 53 */ 54 55 boolean_t fg = B_FALSE; 56 dladm_handle_t dld_handle = NULL; 57 ipadm_handle_t ipadm_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(8), 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(7) properties 92 * - signal handling 93 * - managing privileges(7) 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 drc; 321 ipadm_status_t irc; 322 uid_t uid = getuid(); 323 324 /* 325 * Block the signals we care about (and which might cause us to 326 * exit based on default disposition) until we're ready to start 327 * handling them properly...see init_signalhandling() below. 328 */ 329 block_signals(); 330 331 if (uid != UID_NETADM && uid != 0) { 332 /* 333 * This shouldn't happen normally. On upgrade the service might 334 * need reloading. 335 */ 336 pfail("nwamd should run as uid %d, not uid %d\n", UID_NETADM, 337 uid); 338 } 339 340 (void) setlocale(LC_ALL, ""); 341 (void) textdomain(TEXT_DOMAIN); 342 343 start_logging(); 344 nlog(LOG_INFO, "nwamd pid %d started", getpid()); 345 346 while ((c = getopt(argc, argv, "fs:")) != -1) { 347 switch (c) { 348 case 'f': 349 fg = B_TRUE; 350 break; 351 default: 352 nlog(LOG_ERR, "unrecognized option %c", 353 optopt); 354 break; 355 } 356 } 357 358 lookup_daemon_properties(); 359 360 if (!fg) 361 daemonize(); 362 363 /* 364 * The dladm handle *must* be opened before privileges are dropped. 365 * The device privilege requirements, which are stored in 366 * /etc/security/device_policy, may not be loaded yet, as that's 367 * done by svc:/system/filesystem/root. If they are not loaded, 368 * then one must have *all* privs in order to open /dev/dld, which 369 * is one of the steps performed in dladm_open(). 370 */ 371 drc = dladm_open(&dld_handle); 372 if (drc != DLADM_STATUS_OK) { 373 char status_str[DLADM_STRSIZE]; 374 pfail("failed to open dladm handle: %s", 375 dladm_status2str(drc, status_str)); 376 } 377 378 irc = ipadm_open(&ipadm_handle, 0); 379 if (irc != IPADM_SUCCESS) 380 pfail("failed to open ipadm handle: %s", ipadm_status2str(irc)); 381 382 /* 383 * Create the event queue before starting event sources, including 384 * signal handling, so we are ready to handle incoming events. Also 385 * start before attempting to upgrade, in case there's a problem 386 * upgrading and we need to retry (in which case we schedule an event 387 * to do so). 388 */ 389 nwamd_event_queue_init(); 390 391 /* 392 * Handle upgrade of legacy config. Absence of version property 393 * (which did not exist in phase 0 or 0.5) is the indication that 394 * we need to upgrade to phase 1 (version 1). 395 */ 396 if (nwamd_lookup_count_property(OUR_FMRI, OUR_PG, OUR_VERSION_PROP_NAME, 397 &version) != 0) 398 nwamd_handle_upgrade(NULL); 399 400 /* 401 * Initialize lists handling internal representations of objects. 402 */ 403 nwamd_object_lists_init(); 404 405 init_signalhandling(); 406 407 /* Enqueue init event */ 408 event = nwamd_event_init_init(); 409 if (event == NULL) 410 pfail("nwamd could not create init event, exiting"); 411 nwamd_event_enqueue(event); 412 413 /* 414 * Collect initial user configuration. 415 */ 416 417 /* 418 * Walk the physical interfaces and update the Automatic NCP to 419 * contain the IP and link NCUs for the interfaces that exist in 420 * the system. 421 */ 422 nwamd_walk_physical_configuration(); 423 424 /* 425 * We should initialize the door at the point that we can respond to 426 * user requests about the system but before we start actually process 427 * state changes or effecting the system. 428 */ 429 nwamd_door_init(); 430 431 /* 432 * Initialize data objects. 433 * 434 * Enabling an NCP involves refreshing nwam, which initializes the 435 * objects (ncu, enm, loc, known wlan). Thus, no need to 436 * explicitly initialize these objects here. The refresh also 437 * enqueues and NCU activation checking event. Location and ENM 438 * condition checking are triggered by changes in NCU states. 439 */ 440 (void) pthread_mutex_lock(&active_ncp_mutex); 441 if (nwamd_ncp_action(active_ncp, NWAM_ACTION_ENABLE) != 0) 442 pfail("Initial enable failed for active NCP %s", active_ncp); 443 (void) pthread_mutex_unlock(&active_ncp_mutex); 444 445 /* 446 * Enqueue an event to start periodic checking of activation conditions. 447 */ 448 nwamd_create_timed_condition_check_event(); 449 450 /* 451 * These two routines safely minimize our privilege set. They 452 * use reference counting to be safe in a threaded program. It is 453 * gross that we escalate/deescalate to initialize this functionality 454 * but a real fix is to add functionality to do fine grained privs 455 * (and if necessary set uid to 0) in this threaded daemon. 456 */ 457 nwamd_escalate(); 458 nwamd_deescalate(); 459 460 /* 461 * Start the various agents (hooks on fds, threads) which collect events 462 */ 463 nwamd_event_sources_init(); 464 465 /* 466 * nwamd_event_handler() only returns on shutdown. 467 */ 468 nwamd_event_handler(); 469 470 ipadm_close(ipadm_handle); 471 dladm_close(dld_handle); 472 473 return (EXIT_SUCCESS); 474 } 475