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