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 165 if (lookup_boolean_property(OUR_PG, "debug", &debug_set) == 0) 166 debug = debug_set; 167 if (lookup_count_property(OUR_PG, "scan_interval", &scan_interval) == 0) 168 wlan_scan_interval = scan_interval; 169 if (lookup_count_property(OUR_PG, "idle_time", &idle_time) == 0) 170 door_idle_time = idle_time; 171 dprintf("Read daemon configuration properties."); 172 } 173 174 /* ARGSUSED */ 175 static void * 176 sighandler(void *arg) 177 { 178 sigset_t sigset; 179 int sig; 180 uint32_t now; 181 182 (void) sigfillset(&sigset); 183 184 while (!shutting_down) { 185 sig = sigwait(&sigset); 186 dprintf("signal %d caught", sig); 187 switch (sig) { 188 case SIGALRM: 189 /* 190 * We may have multiple interfaces with 191 * scheduled timers; walk the list and 192 * create a timer event for each one. 193 */ 194 timer_expire = TIMER_INFINITY; 195 now = NSEC_TO_SEC(gethrtime()); 196 check_interface_timers(now); 197 check_door_life(now); 198 break; 199 case SIGHUP: 200 /* 201 * Refresh action - reread configuration properties. 202 */ 203 lookup_daemon_properties(); 204 break; 205 case SIGINT: 206 /* 207 * Undocumented "print debug status" signal. 208 */ 209 print_llp_status(); 210 print_interface_status(); 211 print_wireless_status(); 212 break; 213 default: 214 syslog(LOG_NOTICE, "%s received, shutting down", 215 strsignal(sig)); 216 shutting_down = B_TRUE; 217 if (!np_queue_add_event(EV_SHUTDOWN, NULL)) { 218 dprintf("could not allocate shutdown event"); 219 cleanup(); 220 exit(EXIT_FAILURE); 221 } 222 break; 223 } 224 } 225 return (NULL); 226 } 227 228 static void 229 init_signalhandling(void) 230 { 231 pthread_attr_t attr; 232 pthread_t sighand; 233 int err; 234 sigset_t new; 235 236 (void) sigfillset(&new); 237 (void) pthread_sigmask(SIG_BLOCK, &new, &original_sigmask); 238 (void) pthread_attr_init(&attr); 239 (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); 240 if (err = pthread_create(&sighand, &attr, sighandler, NULL)) { 241 syslog(LOG_ERR, "pthread_create system: %s", strerror(err)); 242 exit(EXIT_FAILURE); 243 } else { 244 dprintf("signal handler thread: %d", sighand); 245 } 246 (void) pthread_attr_destroy(&attr); 247 } 248 249 static void 250 change_user_set_privs(void) 251 { 252 priv_set_t *priv_set; 253 254 priv_set = priv_allocset(); 255 if (getppriv(PRIV_PERMITTED, priv_set) == -1) { 256 dprintf("getppriv %s", strerror(errno)); 257 } else { 258 char *p; 259 260 p = priv_set_to_str(priv_set, ',', 0); 261 dprintf("started with privs %s", p != NULL ? p : "Unknown"); 262 free(p); 263 } 264 priv_freeset(priv_set); 265 266 /* always start with the basic set */ 267 priv_set = priv_str_to_set("basic", ",", NULL); 268 if (priv_set == NULL) { 269 syslog(LOG_ERR, "converting basic privilege set: %m"); 270 exit(EXIT_FAILURE); 271 } 272 (void) priv_addset(priv_set, PRIV_FILE_CHOWN_SELF); 273 (void) priv_addset(priv_set, PRIV_FILE_DAC_READ); 274 (void) priv_addset(priv_set, PRIV_FILE_DAC_WRITE); 275 (void) priv_addset(priv_set, PRIV_NET_PRIVADDR); 276 (void) priv_addset(priv_set, PRIV_NET_RAWACCESS); 277 (void) priv_addset(priv_set, PRIV_PROC_AUDIT); 278 (void) priv_addset(priv_set, PRIV_PROC_OWNER); 279 (void) priv_addset(priv_set, PRIV_PROC_SETID); 280 (void) priv_addset(priv_set, PRIV_SYS_CONFIG); 281 (void) priv_addset(priv_set, PRIV_SYS_IP_CONFIG); 282 (void) priv_addset(priv_set, PRIV_SYS_IPC_CONFIG); 283 (void) priv_addset(priv_set, PRIV_SYS_NET_CONFIG); 284 (void) priv_addset(priv_set, PRIV_SYS_RES_CONFIG); 285 (void) priv_addset(priv_set, PRIV_SYS_RESOURCE); 286 287 if (setppriv(PRIV_SET, PRIV_INHERITABLE, priv_set) == -1) { 288 syslog(LOG_ERR, "setppriv inheritable: %m"); 289 priv_freeset(priv_set); 290 exit(EXIT_FAILURE); 291 } 292 293 if (setppriv(PRIV_SET, PRIV_PERMITTED, priv_set) == -1) { 294 syslog(LOG_ERR, "setppriv permitted: %m"); 295 priv_freeset(priv_set); 296 exit(EXIT_FAILURE); 297 } 298 299 if (setppriv(PRIV_SET, PRIV_EFFECTIVE, priv_set) == -1) { 300 syslog(LOG_ERR, "setppriv effective: %m"); 301 priv_freeset(priv_set); 302 exit(EXIT_FAILURE); 303 } 304 305 priv_freeset(priv_set); 306 } 307 308 static void 309 init_machine_mutex(void) 310 { 311 pthread_mutexattr_t attrs; 312 313 (void) pthread_mutexattr_init(&attrs); 314 (void) pthread_mutexattr_settype(&attrs, PTHREAD_MUTEX_ERRORCHECK); 315 if (pthread_mutex_init(&machine_lock, &attrs) != 0) { 316 syslog(LOG_ERR, "unable to set up machine lock"); 317 exit(EXIT_FAILURE); 318 } 319 (void) pthread_mutexattr_destroy(&attrs); 320 } 321 322 int 323 main(int argc, char *argv[]) 324 { 325 int c; 326 int scan_lev; 327 struct np_event *e; 328 enum np_event_type etype; 329 330 (void) setlocale(LC_ALL, ""); 331 (void) textdomain(TEXT_DOMAIN); 332 333 shutting_down = B_FALSE; 334 start_logging(); 335 syslog(LOG_INFO, "nwamd pid %d started", getpid()); 336 337 while ((c = getopt(argc, argv, "fs:")) != -1) { 338 switch (c) { 339 case 'f': 340 fg = B_TRUE; 341 break; 342 case 's': 343 scan_lev = atoi(optarg); 344 if (scan_lev >= DLADM_WLAN_STRENGTH_VERY_WEAK && 345 scan_lev <= DLADM_WLAN_STRENGTH_EXCELLENT) { 346 wireless_scan_level = scan_lev; 347 } else { 348 syslog(LOG_ERR, "invalid signal " 349 "strength: %s", optarg); 350 } 351 break; 352 default: 353 syslog(LOG_ERR, "unrecognized option %c", 354 optopt); 355 break; 356 } 357 } 358 359 lookup_daemon_properties(); 360 361 change_user_set_privs(); 362 363 if (!fg) 364 daemonize(); 365 366 initialize_llp(); 367 368 init_signalhandling(); 369 370 initialize_wireless(); 371 372 lookup_zonename(zonename, sizeof (zonename)); 373 374 init_machine_mutex(); 375 376 initialize_interfaces(); 377 378 llp_parse_config(); 379 380 initialize_door(); 381 382 (void) start_event_collection(); 383 384 while ((e = np_queue_get_event()) != NULL) { 385 386 etype = e->npe_type; 387 syslog(LOG_INFO, "got event type %s", npe_type_str(etype)); 388 if (etype == EV_SHUTDOWN) 389 terminate_door(); 390 if (pthread_mutex_lock(&machine_lock) != 0) { 391 syslog(LOG_ERR, "mutex lock"); 392 exit(EXIT_FAILURE); 393 } 394 state_machine(e); 395 (void) pthread_mutex_unlock(&machine_lock); 396 free_event(e); 397 if (etype == EV_SHUTDOWN) 398 break; 399 } 400 syslog(LOG_DEBUG, "terminating routing and scanning threads"); 401 (void) pthread_cancel(routing); 402 (void) pthread_cancel(scan); 403 (void) pthread_join(routing, NULL); 404 (void) pthread_join(scan, NULL); 405 syslog(LOG_INFO, "nwamd shutting down"); 406 return (EXIT_SUCCESS); 407 } 408