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 /* 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 320 /* always start with the basic set */ 321 priv_basicset(priv_set); 322 (void) priv_addset(priv_set, PRIV_FILE_CHOWN_SELF); 323 (void) priv_addset(priv_set, PRIV_FILE_DAC_READ); 324 (void) priv_addset(priv_set, PRIV_FILE_DAC_WRITE); 325 (void) priv_addset(priv_set, PRIV_NET_PRIVADDR); 326 (void) priv_addset(priv_set, PRIV_NET_RAWACCESS); 327 (void) priv_addset(priv_set, PRIV_PROC_AUDIT); 328 (void) priv_addset(priv_set, PRIV_PROC_OWNER); 329 (void) priv_addset(priv_set, PRIV_PROC_SETID); 330 (void) priv_addset(priv_set, PRIV_SYS_CONFIG); 331 (void) priv_addset(priv_set, PRIV_SYS_IP_CONFIG); 332 (void) priv_addset(priv_set, PRIV_SYS_IPC_CONFIG); 333 (void) priv_addset(priv_set, PRIV_SYS_NET_CONFIG); 334 (void) priv_addset(priv_set, PRIV_SYS_RES_CONFIG); 335 (void) priv_addset(priv_set, PRIV_SYS_RESOURCE); 336 337 if (setppriv(PRIV_SET, PRIV_INHERITABLE, priv_set) == -1) { 338 syslog(LOG_ERR, "setppriv inheritable: %m"); 339 priv_freeset(priv_set); 340 exit(EXIT_FAILURE); 341 } 342 343 if (setppriv(PRIV_SET, PRIV_PERMITTED, priv_set) == -1) { 344 syslog(LOG_ERR, "setppriv permitted: %m"); 345 priv_freeset(priv_set); 346 exit(EXIT_FAILURE); 347 } 348 349 if (setppriv(PRIV_SET, PRIV_EFFECTIVE, priv_set) == -1) { 350 syslog(LOG_ERR, "setppriv effective: %m"); 351 priv_freeset(priv_set); 352 exit(EXIT_FAILURE); 353 } 354 355 priv_freeset(priv_set); 356 } 357 358 static void 359 init_machine_mutex(void) 360 { 361 pthread_mutexattr_t attrs; 362 363 (void) pthread_mutexattr_init(&attrs); 364 (void) pthread_mutexattr_settype(&attrs, PTHREAD_MUTEX_ERRORCHECK); 365 if (pthread_mutex_init(&machine_lock, &attrs) != 0) { 366 syslog(LOG_ERR, "unable to set up machine lock"); 367 exit(EXIT_FAILURE); 368 } 369 (void) pthread_mutexattr_destroy(&attrs); 370 } 371 372 int 373 main(int argc, char *argv[]) 374 { 375 int c; 376 int scan_lev; 377 struct np_event *e; 378 enum np_event_type etype; 379 380 (void) setlocale(LC_ALL, ""); 381 (void) textdomain(TEXT_DOMAIN); 382 383 shutting_down = B_FALSE; 384 start_logging(); 385 syslog(LOG_INFO, "nwamd pid %d started", getpid()); 386 387 while ((c = getopt(argc, argv, "fs:")) != -1) { 388 switch (c) { 389 case 'f': 390 fg = B_TRUE; 391 break; 392 case 's': 393 scan_lev = atoi(optarg); 394 if (scan_lev >= DLADM_WLAN_STRENGTH_VERY_WEAK && 395 scan_lev <= DLADM_WLAN_STRENGTH_EXCELLENT) { 396 wireless_scan_level = scan_lev; 397 } else { 398 syslog(LOG_ERR, "invalid signal " 399 "strength: %s", optarg); 400 } 401 break; 402 default: 403 syslog(LOG_ERR, "unrecognized option %c", 404 optopt); 405 break; 406 } 407 } 408 409 lookup_daemon_properties(); 410 411 /* 412 * The dladm handle *must* be opened before privileges are dropped 413 * by nwamd. The device privilege requirements from 414 * /etc/security/device_policy may not be loaded yet. These are 415 * loaded by svc:/system/filesystem/root, which comes online after 416 * svc:/network/physical. 417 */ 418 if (dladm_open(&dld_handle) != DLADM_STATUS_OK) { 419 syslog(LOG_ERR, "failed to open dladm handle"); 420 exit(EXIT_FAILURE); 421 } 422 423 change_user_set_privs(); 424 425 if (!fg) 426 daemonize(); 427 428 initialize_llp(); 429 430 init_signalhandling(); 431 432 initialize_wireless(); 433 434 lookup_zonename(zonename, sizeof (zonename)); 435 436 init_machine_mutex(); 437 438 initialize_interfaces(); 439 440 llp_parse_config(); 441 442 initialize_door(); 443 444 (void) start_event_collection(); 445 446 while ((e = np_queue_get_event()) != NULL) { 447 448 etype = e->npe_type; 449 syslog(LOG_INFO, "got event type %s", npe_type_str(etype)); 450 if (etype == EV_SHUTDOWN) 451 terminate_door(); 452 if (pthread_mutex_lock(&machine_lock) != 0) { 453 syslog(LOG_ERR, "mutex lock"); 454 exit(EXIT_FAILURE); 455 } 456 state_machine(e); 457 (void) pthread_mutex_unlock(&machine_lock); 458 free_event(e); 459 if (etype == EV_SHUTDOWN) 460 break; 461 } 462 syslog(LOG_DEBUG, "terminating routing and scanning threads"); 463 (void) pthread_cancel(routing); 464 (void) pthread_join(routing, NULL); 465 if (scan != 0) { 466 (void) pthread_cancel(scan); 467 (void) pthread_join(scan, NULL); 468 } 469 dladm_close(dld_handle); 470 syslog(LOG_INFO, "nwamd shutting down"); 471 return (EXIT_SUCCESS); 472 } 473