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 2008 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 char zonename[ZONENAME_MAX]; 63 pthread_mutex_t machine_lock = PTHREAD_MUTEX_INITIALIZER; 64 dladm_handle_t dld_handle = NULL; 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, and nwamd(1M). 72 * 73 * The general structure of the code is as a set of threads collecting 74 * system events which are fed into a state machine which alters system 75 * state based on configuration. 76 * 77 * signal management 78 * Due to being threaded, a simple set of signal handlers would not work 79 * very well for nwamd. Instead nwamd blocks signals at startup and 80 * then starts a thread which sits in sigwait(2) waiting for signals. 81 * When a signal is received the signal handling thread dispatches it. 82 * It handles: 83 * - shutting down, done by creating an event which is passed through the 84 * system allowing the various subsystems to do any necessary cleanup. 85 * - SIGALRM for timers. 86 * - SIGHUP for instance refresh, which tells us to look up various 87 * properties from SMF(5). 88 * 89 * subprocess management 90 * nwamd starts several different subprocesses to manage the system. Some 91 * of those start other processes (e.g. `ifconfig <if> dhcp` ends up starting 92 * dhcpagent if necessary). Due to the way we manage signals if we started 93 * those up without doing anything special their signal mask would mostly 94 * block signals. So we restore the signal mask when we start subprocesses. 95 * This is especially important with respect to DHCP as later when we exit 96 * we need to kill the dhcpagent process which we started; for details, see 97 * the block comment in state_machine.c in its cleanup() function. 98 */ 99 100 /* 101 * In this file there are several utility functions which might otherwise 102 * belong in util.c, but since they are only called from main(), they can 103 * live here as static functions: 104 * - syslog set-up 105 * - daemonizing 106 * - looking up SMF(5) properties 107 * - signal handling 108 * - managing privileges(5) 109 */ 110 111 static void 112 start_logging(void) 113 { 114 openlog("nwamd", LOG_PID | LOG_NDELAY, LOG_DAEMON); 115 } 116 117 static void 118 daemonize(void) 119 { 120 pid_t pid; 121 122 /* 123 * A little bit of magic here. By the first fork+setsid, we 124 * disconnect from our current controlling terminal and become 125 * a session group leader. By forking again without calling 126 * setsid again, we make certain that we are not the session 127 * group leader and can never reacquire a controlling terminal. 128 */ 129 if ((pid = fork()) == (pid_t)-1) { 130 syslog(LOG_ERR, "fork 1 failed"); 131 exit(EXIT_FAILURE); 132 } 133 if (pid != 0) { 134 (void) wait(NULL); 135 dprintf("child %ld exited, daemonizing", pid); 136 _exit(0); 137 } 138 if (setsid() == (pid_t)-1) { 139 syslog(LOG_ERR, "setsid"); 140 exit(EXIT_FAILURE); 141 } 142 if ((pid = fork()) == (pid_t)-1) { 143 syslog(LOG_ERR, "fork 2 failed"); 144 exit(EXIT_FAILURE); 145 } 146 if (pid != 0) { 147 _exit(0); 148 } 149 (void) chdir("/"); 150 (void) umask(022); 151 } 152 153 /* 154 * Look up nwamd property values and set daemon variables appropriately. 155 * This function will be called on startup and via the signal handling 156 * thread on receiving a HUP (which occurs when the nwam service is 157 * refreshed). 158 */ 159 static void 160 lookup_daemon_properties(void) 161 { 162 boolean_t debug_set; 163 uint64_t scan_interval; 164 uint64_t idle_time; 165 boolean_t strict_bssid_set; 166 167 if (lookup_boolean_property(OUR_PG, "debug", &debug_set) == 0) 168 debug = debug_set; 169 if (lookup_count_property(OUR_PG, "scan_interval", &scan_interval) == 0) 170 wlan_scan_interval = scan_interval; 171 if (lookup_count_property(OUR_PG, "idle_time", &idle_time) == 0) 172 door_idle_time = idle_time; 173 if (lookup_boolean_property(OUR_PG, "strict_bssid", 174 &strict_bssid_set) == 0) 175 strict_bssid = strict_bssid_set; 176 dprintf("Read daemon configuration properties."); 177 } 178 179 /* ARGSUSED */ 180 static void * 181 sighandler(void *arg) 182 { 183 sigset_t sigset; 184 int sig, err; 185 uint32_t now; 186 187 (void) sigfillset(&sigset); 188 189 while (!shutting_down) { 190 sig = sigwait(&sigset); 191 dprintf("signal %d caught", sig); 192 switch (sig) { 193 case SIGALRM: 194 /* 195 * We may have multiple interfaces with 196 * scheduled timers; walk the list and 197 * create a timer event for each one. 198 */ 199 timer_expire = TIMER_INFINITY; 200 now = NSEC_TO_SEC(gethrtime()); 201 check_interface_timers(now); 202 check_door_life(now); 203 break; 204 case SIGHUP: 205 /* 206 * Refresh action - reread configuration properties. 207 */ 208 lookup_daemon_properties(); 209 /* 210 * Check if user restarted scanning. 211 */ 212 if (scan == 0 && wlan_scan_interval != 0) { 213 err = pthread_create(&scan, NULL, 214 periodic_wireless_scan, NULL); 215 if (err != 0) { 216 syslog(LOG_NOTICE, 217 "pthread_create wireless scan: %s", 218 strerror(err)); 219 } else { 220 dprintf("wireless scan thread: %d", 221 scan); 222 } 223 } 224 break; 225 case SIGINT: 226 /* 227 * Undocumented "print debug status" signal. 228 */ 229 print_llp_status(); 230 print_interface_status(); 231 print_wireless_status(); 232 break; 233 case SIGTHAW: 234 /* 235 * It seems unlikely that this is helpful, but it can't 236 * hurt: when waking up from a sleep, check if the 237 * wireless interface is still viable. There've been 238 * bugs in this area. 239 */ 240 if (pthread_mutex_lock(&machine_lock) == 0) { 241 if (link_layer_profile != NULL && 242 link_layer_profile->llp_type == 243 IF_WIRELESS) { 244 wireless_verify( 245 link_layer_profile->llp_lname); 246 } 247 (void) pthread_mutex_unlock(&machine_lock); 248 } 249 break; 250 default: 251 syslog(LOG_NOTICE, "%s received, shutting down", 252 strsignal(sig)); 253 shutting_down = B_TRUE; 254 if (!np_queue_add_event(EV_SHUTDOWN, NULL)) { 255 dprintf("could not allocate shutdown event"); 256 cleanup(); 257 exit(EXIT_FAILURE); 258 } 259 break; 260 } 261 } 262 return (NULL); 263 } 264 265 /* ARGSUSED */ 266 static void 267 sigdummy(int sig) 268 { 269 } 270 271 static void 272 init_signalhandling(void) 273 { 274 struct sigaction act; 275 pthread_attr_t attr; 276 pthread_t sighand; 277 int err; 278 sigset_t new; 279 280 /* 281 * The default is to ignore, so we need a dummy handler. 282 */ 283 (void) memset(&act, 0, sizeof (act)); 284 act.sa_handler = sigdummy; 285 act.sa_flags = SA_RESTART; 286 (void) sigaction(SIGTHAW, &act, NULL); 287 288 (void) sigfillset(&new); 289 (void) pthread_sigmask(SIG_BLOCK, &new, &original_sigmask); 290 (void) pthread_attr_init(&attr); 291 (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); 292 if (err = pthread_create(&sighand, &attr, sighandler, NULL)) { 293 syslog(LOG_ERR, "pthread_create system: %s", strerror(err)); 294 exit(EXIT_FAILURE); 295 } else { 296 dprintf("signal handler thread: %d", sighand); 297 } 298 (void) pthread_attr_destroy(&attr); 299 } 300 301 static void 302 change_user_set_privs(void) 303 { 304 priv_set_t *priv_set; 305 306 priv_set = priv_allocset(); 307 if (getppriv(PRIV_PERMITTED, priv_set) == -1) { 308 dprintf("getppriv %s", strerror(errno)); 309 } else { 310 char *p; 311 312 p = priv_set_to_str(priv_set, ',', 0); 313 dprintf("started with privs %s", p != NULL ? p : "Unknown"); 314 free(p); 315 } 316 priv_freeset(priv_set); 317 318 /* always start with the basic set */ 319 priv_set = priv_str_to_set("basic", ",", NULL); 320 if (priv_set == NULL) { 321 syslog(LOG_ERR, "converting basic privilege set: %m"); 322 exit(EXIT_FAILURE); 323 } 324 (void) priv_addset(priv_set, PRIV_FILE_CHOWN_SELF); 325 (void) priv_addset(priv_set, PRIV_FILE_DAC_READ); 326 (void) priv_addset(priv_set, PRIV_FILE_DAC_WRITE); 327 (void) priv_addset(priv_set, PRIV_NET_PRIVADDR); 328 (void) priv_addset(priv_set, PRIV_NET_RAWACCESS); 329 (void) priv_addset(priv_set, PRIV_PROC_AUDIT); 330 (void) priv_addset(priv_set, PRIV_PROC_OWNER); 331 (void) priv_addset(priv_set, PRIV_PROC_SETID); 332 (void) priv_addset(priv_set, PRIV_SYS_CONFIG); 333 (void) priv_addset(priv_set, PRIV_SYS_IP_CONFIG); 334 (void) priv_addset(priv_set, PRIV_SYS_IPC_CONFIG); 335 (void) priv_addset(priv_set, PRIV_SYS_NET_CONFIG); 336 (void) priv_addset(priv_set, PRIV_SYS_RES_CONFIG); 337 (void) priv_addset(priv_set, PRIV_SYS_RESOURCE); 338 339 if (setppriv(PRIV_SET, PRIV_INHERITABLE, priv_set) == -1) { 340 syslog(LOG_ERR, "setppriv inheritable: %m"); 341 priv_freeset(priv_set); 342 exit(EXIT_FAILURE); 343 } 344 345 if (setppriv(PRIV_SET, PRIV_PERMITTED, priv_set) == -1) { 346 syslog(LOG_ERR, "setppriv permitted: %m"); 347 priv_freeset(priv_set); 348 exit(EXIT_FAILURE); 349 } 350 351 if (setppriv(PRIV_SET, PRIV_EFFECTIVE, priv_set) == -1) { 352 syslog(LOG_ERR, "setppriv effective: %m"); 353 priv_freeset(priv_set); 354 exit(EXIT_FAILURE); 355 } 356 357 priv_freeset(priv_set); 358 } 359 360 static void 361 init_machine_mutex(void) 362 { 363 pthread_mutexattr_t attrs; 364 365 (void) pthread_mutexattr_init(&attrs); 366 (void) pthread_mutexattr_settype(&attrs, PTHREAD_MUTEX_ERRORCHECK); 367 if (pthread_mutex_init(&machine_lock, &attrs) != 0) { 368 syslog(LOG_ERR, "unable to set up machine lock"); 369 exit(EXIT_FAILURE); 370 } 371 (void) pthread_mutexattr_destroy(&attrs); 372 } 373 374 int 375 main(int argc, char *argv[]) 376 { 377 int c; 378 int scan_lev; 379 struct np_event *e; 380 enum np_event_type etype; 381 382 (void) setlocale(LC_ALL, ""); 383 (void) textdomain(TEXT_DOMAIN); 384 385 shutting_down = B_FALSE; 386 start_logging(); 387 syslog(LOG_INFO, "nwamd pid %d started", getpid()); 388 389 while ((c = getopt(argc, argv, "fs:")) != -1) { 390 switch (c) { 391 case 'f': 392 fg = B_TRUE; 393 break; 394 case 's': 395 scan_lev = atoi(optarg); 396 if (scan_lev >= DLADM_WLAN_STRENGTH_VERY_WEAK && 397 scan_lev <= DLADM_WLAN_STRENGTH_EXCELLENT) { 398 wireless_scan_level = scan_lev; 399 } else { 400 syslog(LOG_ERR, "invalid signal " 401 "strength: %s", optarg); 402 } 403 break; 404 default: 405 syslog(LOG_ERR, "unrecognized option %c", 406 optopt); 407 break; 408 } 409 } 410 411 lookup_daemon_properties(); 412 413 change_user_set_privs(); 414 415 if (!fg) 416 daemonize(); 417 418 initialize_llp(); 419 420 init_signalhandling(); 421 422 initialize_wireless(); 423 424 lookup_zonename(zonename, sizeof (zonename)); 425 426 init_machine_mutex(); 427 428 /* open the dladm handle */ 429 if (dladm_open(&dld_handle) != DLADM_STATUS_OK) { 430 syslog(LOG_ERR, "failed to open dladm handle"); 431 exit(EXIT_FAILURE); 432 } 433 434 initialize_interfaces(); 435 436 llp_parse_config(); 437 438 initialize_door(); 439 440 (void) start_event_collection(); 441 442 while ((e = np_queue_get_event()) != NULL) { 443 444 etype = e->npe_type; 445 syslog(LOG_INFO, "got event type %s", npe_type_str(etype)); 446 if (etype == EV_SHUTDOWN) 447 terminate_door(); 448 if (pthread_mutex_lock(&machine_lock) != 0) { 449 syslog(LOG_ERR, "mutex lock"); 450 exit(EXIT_FAILURE); 451 } 452 state_machine(e); 453 (void) pthread_mutex_unlock(&machine_lock); 454 free_event(e); 455 if (etype == EV_SHUTDOWN) 456 break; 457 } 458 syslog(LOG_DEBUG, "terminating routing and scanning threads"); 459 (void) pthread_cancel(routing); 460 (void) pthread_join(routing, NULL); 461 if (scan != 0) { 462 (void) pthread_cancel(scan); 463 (void) pthread_join(scan, NULL); 464 } 465 dladm_close(dld_handle); 466 syslog(LOG_INFO, "nwamd shutting down"); 467 return (EXIT_SUCCESS); 468 } 469