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 int sig; 138 139 while (!shutting_down) { 140 sig = sigwait(&sigwaitset); 141 nlog(LOG_DEBUG, "signal %s caught", strsignal(sig)); 142 143 switch (sig) { 144 case SIGTHAW: 145 case SIGHUP: 146 /* 147 * Resumed from suspend or refresh. Clear up all 148 * objects so their states start from scratch; 149 * then refresh(). 150 */ 151 nwamd_fini_enms(); 152 nwamd_fini_ncus(); 153 nwamd_fini_locs(); 154 nwamd_refresh(); 155 break; 156 case SIGUSR1: 157 /* 158 * Undocumented "log ncu list" signal. 159 */ 160 nwamd_log_ncus(); 161 break; 162 case SIGTERM: 163 nlog(LOG_DEBUG, "%s received, shutting down", 164 strsignal(sig)); 165 graceful_shutdown(); 166 break; 167 default: 168 nlog(LOG_DEBUG, "unexpected signal %s received, " 169 "ignoring", strsignal(sig)); 170 break; 171 } 172 } 173 return (NULL); 174 } 175 176 static void 177 init_signalhandling(void) 178 { 179 pthread_attr_t attr; 180 pthread_t sighand; 181 int err; 182 183 (void) pthread_attr_init(&attr); 184 (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); 185 if (err = pthread_create(&sighand, &attr, sighandler, NULL)) { 186 nlog(LOG_ERR, "pthread_create system: %s", strerror(err)); 187 exit(EXIT_FAILURE); 188 } else { 189 nlog(LOG_DEBUG, "signal handler thread: %d", sighand); 190 } 191 (void) pthread_attr_destroy(&attr); 192 } 193 194 /* 195 * Construct the set of signals that we explicitly want to deal with. 196 * We block these while we're still single-threaded; this block will 197 * be inherited by all the threads we create. When we are ready to 198 * start handling signals, we will start the signal handling thread, 199 * which will sigwait() this same set of signals, and will thus receive 200 * and handle any that are sent to the process. 201 */ 202 static void 203 block_signals(void) 204 { 205 (void) sigemptyset(&sigwaitset); 206 (void) sigaddset(&sigwaitset, SIGHUP); 207 (void) sigaddset(&sigwaitset, SIGUSR1); 208 (void) sigaddset(&sigwaitset, SIGUSR2); 209 (void) sigaddset(&sigwaitset, SIGTERM); 210 (void) sigaddset(&sigwaitset, SIGTHAW); 211 (void) pthread_sigmask(SIG_BLOCK, &sigwaitset, &original_sigmask); 212 } 213 214 /* 215 * Look up nwamd property values and set daemon variables appropriately. 216 * This function will be called on startup and via the signal handling 217 * thread on receiving a HUP (which occurs when the nwam service is 218 * refreshed). 219 */ 220 static void 221 lookup_daemon_properties(void) 222 { 223 char *active_ncp_tmp; 224 char *scan_level_tmp; 225 226 (void) nwamd_lookup_boolean_property(OUR_FMRI, OUR_PG, 227 OUR_DEBUG_PROP_NAME, &debug); 228 (void) nwamd_lookup_boolean_property(OUR_FMRI, OUR_PG, 229 OUR_AUTOCONF_PROP_NAME, &wireless_autoconf); 230 (void) nwamd_lookup_boolean_property(OUR_FMRI, OUR_PG, 231 OUR_STRICT_BSSID_PROP_NAME, &wireless_strict_bssid); 232 233 (void) pthread_mutex_lock(&active_ncp_mutex); 234 if ((active_ncp_tmp = malloc(NWAM_MAX_NAME_LEN)) == NULL || 235 nwamd_lookup_string_property(OUR_FMRI, OUR_PG, 236 OUR_ACTIVE_NCP_PROP_NAME, active_ncp_tmp, NWAM_MAX_NAME_LEN) != 0) { 237 (void) strlcpy(active_ncp, NWAM_NCP_NAME_AUTOMATIC, 238 NWAM_MAX_NAME_LEN); 239 } else { 240 (void) strlcpy(active_ncp, active_ncp_tmp, NWAM_MAX_NAME_LEN); 241 } 242 (void) pthread_mutex_unlock(&active_ncp_mutex); 243 free(active_ncp_tmp); 244 245 if (nwamd_lookup_count_property(OUR_FMRI, OUR_PG, 246 OUR_CONDITION_CHECK_INTERVAL_PROP_NAME, 247 &condition_check_interval) != 0) 248 condition_check_interval = CONDITION_CHECK_INTERVAL_DEFAULT; 249 250 if ((scan_level_tmp = malloc(NWAM_MAX_NAME_LEN)) == NULL || 251 nwamd_lookup_string_property(OUR_FMRI, OUR_PG, 252 OUR_WIRELESS_SCAN_LEVEL_PROP_NAME, scan_level_tmp, 253 NWAM_MAX_NAME_LEN) != 0) { 254 wireless_scan_level = WIRELESS_SCAN_LEVEL_DEFAULT; 255 } else { 256 if (dladm_wlan_str2strength(scan_level_tmp, 257 &wireless_scan_level) != DLADM_STATUS_OK) 258 wireless_scan_level = DLADM_WLAN_STRENGTH_VERY_WEAK; 259 } 260 free(scan_level_tmp); 261 262 if (nwamd_lookup_count_property(OUR_FMRI, OUR_PG, 263 OUR_WIRELESS_SCAN_INTERVAL_PROP_NAME, &wireless_scan_interval) != 0) 264 wireless_scan_interval = WIRELESS_SCAN_INTERVAL_DEFAULT; 265 266 if (nwamd_lookup_count_property(OUR_FMRI, OUR_PG, 267 OUR_NCU_WAIT_TIME_PROP_NAME, &ncu_wait_time) != 0) 268 ncu_wait_time = NCU_WAIT_TIME_DEFAULT; 269 270 nlog(LOG_DEBUG, "Read daemon configuration properties."); 271 } 272 273 /* 274 * Re-read the SMF properties. 275 * Reset ncu priority group (since the NCUs will have to walk 276 * through their state machines again) and schedule a check 277 * Re-read objects from libnwam. 278 * Also, run condition checking for locations and ENMs. 279 */ 280 static void 281 nwamd_refresh(void) 282 { 283 lookup_daemon_properties(); 284 285 (void) pthread_mutex_lock(&active_ncp_mutex); 286 current_ncu_priority_group = INVALID_PRIORITY_GROUP; 287 (void) pthread_mutex_unlock(&active_ncp_mutex); 288 289 nwamd_init_ncus(); 290 nwamd_init_enms(); 291 nwamd_init_locs(); 292 293 nwamd_create_ncu_check_event(0); 294 nwamd_create_triggered_condition_check_event(0); 295 } 296 297 static void 298 graceful_shutdown(void) 299 { 300 nwamd_event_t event; 301 302 shutting_down = B_TRUE; 303 nwamd_event_sources_fini(); 304 nwamd_door_fini(); 305 nwamd_fini_enms(); 306 nwamd_fini_ncus(); 307 nwamd_fini_locs(); 308 309 event = nwamd_event_init_shutdown(); 310 if (event == NULL) 311 pfail("nwamd could not create shutdown event, exiting"); 312 nwamd_event_enqueue(event); 313 } 314 315 int 316 main(int argc, char *argv[]) 317 { 318 int c; 319 uint64_t version; 320 nwamd_event_t event; 321 dladm_status_t rc; 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 rc = dladm_open(&dld_handle); 372 if (rc != DLADM_STATUS_OK) { 373 char status_str[DLADM_STRSIZE]; 374 (void) dladm_status2str(rc, status_str); 375 pfail("failed to open dladm handle: %s", status_str); 376 } 377 378 /* 379 * Handle upgrade of legacy config. Absence of version property 380 * (which did not exist in phase 0 or 0.5) is the indication that 381 * we need to upgrade to phase 1 (version 1). 382 */ 383 if (nwamd_lookup_count_property(OUR_FMRI, OUR_PG, OUR_VERSION_PROP_NAME, 384 &version) != 0) 385 nwamd_handle_upgrade(NULL); 386 387 /* 388 * Initialize lists handling internal representations of objects. 389 */ 390 nwamd_object_lists_init(); 391 392 /* 393 * Start the event handling thread before starting event sources, 394 * including signal handling, so we are ready to handle incoming 395 * events. 396 */ 397 nwamd_event_queue_init(); 398 399 init_signalhandling(); 400 401 /* Enqueue init event */ 402 event = nwamd_event_init_init(); 403 if (event == NULL) 404 pfail("nwamd could not create init event, exiting"); 405 nwamd_event_enqueue(event); 406 /* 407 * Collect initial user configuration. 408 */ 409 410 /* 411 * Walk the physical interfaces and update the Automatic NCP to 412 * contain the IP and link NCUs for the interfaces that exist in 413 * the system. 414 */ 415 nwamd_walk_physical_configuration(); 416 417 /* 418 * We should initialize the door at the point that we can respond to 419 * user requests about the system but before we start actually process 420 * state changes or effecting the system. 421 */ 422 nwamd_door_init(); 423 424 /* 425 * Initialize data objects. 426 * 427 * Enabling an NCP involves refreshing nwam, which initializes the 428 * objects (ncu, enm, loc, known wlan). Thus, no need to 429 * explicitly initialize these objects here. The refresh also 430 * enqueues and NCU activation checking event. Location and ENM 431 * condition checking are triggered by changes in NCU states. 432 */ 433 (void) pthread_mutex_lock(&active_ncp_mutex); 434 if (nwamd_ncp_action(active_ncp, NWAM_ACTION_ENABLE) != 0) 435 pfail("Initial enable failed for active NCP %s", active_ncp); 436 (void) pthread_mutex_unlock(&active_ncp_mutex); 437 438 /* 439 * Enqueue an event to start periodic checking of activation conditions. 440 */ 441 nwamd_create_timed_condition_check_event(); 442 443 /* 444 * These two routines safely minimize our privilege set. They 445 * use reference counting to be safe in a threaded program. It is 446 * gross that we escalate/deescalate to initialize this functionality 447 * but a real fix is to add functionality to do fine grained privs 448 * (and if necessary set uid to 0) in this threaded daemon. 449 */ 450 nwamd_escalate(); 451 nwamd_deescalate(); 452 453 /* 454 * Start the various agents (hooks on fds, threads) which collect events 455 */ 456 nwamd_event_sources_init(); 457 458 /* 459 * nwamd_event_handler() only returns on shutdown. 460 */ 461 nwamd_event_handler(); 462 463 dladm_close(dld_handle); 464 465 return (EXIT_SUCCESS); 466 } 467