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