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