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 2007 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * nwamd - NetWork Auto-Magic Daemon 31 */ 32 33 #include <fcntl.h> 34 #include <priv.h> 35 #include <pthread.h> 36 #include <pwd.h> 37 #include <stdio.h> 38 #include <stdlib.h> 39 #include <string.h> 40 #include <signal.h> 41 #include <sys/stat.h> 42 #include <sys/types.h> 43 #include <sys/wait.h> 44 #include <syslog.h> 45 #include <unistd.h> 46 #include <locale.h> 47 #include <libintl.h> 48 #include <errno.h> 49 50 #include "defines.h" 51 #include "structures.h" 52 #include "functions.h" 53 #include "variables.h" 54 55 #define TIMESPECGT(x, y) ((x.tv_sec > y.tv_sec) || \ 56 ((x.tv_sec == y.tv_sec) && (x.tv_nsec > y.tv_nsec))) 57 58 const char *OUR_FMRI = "svc:/network/physical:nwam"; 59 const char *OUR_PG = "nwamd"; 60 61 boolean_t fg = B_FALSE; 62 boolean_t shutting_down; 63 sigset_t original_sigmask; 64 char zonename[ZONENAME_MAX]; 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 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 dprintf("Read daemon configuration properties."); 170 } 171 172 /* ARGSUSED */ 173 static void * 174 sighandler(void *arg) 175 { 176 struct np_event *ev; 177 sigset_t sigset; 178 int sig; 179 uint32_t now; 180 181 (void) sigfillset(&sigset); 182 183 for (;;) { 184 sig = sigwait(&sigset); 185 dprintf("signal %d caught", sig); 186 switch (sig) { 187 case SIGALRM: 188 /* 189 * We may have multiple interfaces with 190 * scheduled timers; walk the list and 191 * create a timer event for each one. 192 */ 193 timer_expire = TIMER_INFINITY; 194 now = NSEC_TO_SEC(gethrtime()); 195 walk_interface(check_interface_timer, &now); 196 break; 197 case SIGHUP: 198 /* 199 * Refresh action - reread configuration properties. 200 */ 201 lookup_daemon_properties(); 202 break; 203 default: 204 syslog(LOG_NOTICE, "%s received, shutting down", 205 strsignal(sig)); 206 shutting_down = B_TRUE; 207 if ((ev = malloc(sizeof (*ev))) == NULL) { 208 dprintf("could not allocate shutdown event"); 209 cleanup(); 210 exit(EXIT_FAILURE); 211 } 212 ev->npe_type = EV_SHUTDOWN; 213 ev->npe_name = NULL; 214 np_queue_add_event(ev); 215 break; 216 } 217 218 /* if we're shutting down, exit this thread */ 219 if (shutting_down) 220 return (NULL); 221 } 222 } 223 224 static void 225 init_signalhandling(void) 226 { 227 pthread_attr_t attr; 228 pthread_t sighand; 229 int err; 230 sigset_t new; 231 232 (void) sigfillset(&new); 233 (void) pthread_sigmask(SIG_BLOCK, &new, &original_sigmask); 234 (void) pthread_attr_init(&attr); 235 (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); 236 if (err = pthread_create(&sighand, &attr, sighandler, NULL)) { 237 syslog(LOG_ERR, "pthread_create system: %s", strerror(err)); 238 exit(EXIT_FAILURE); 239 } else { 240 dprintf("signal handler thread: %d", sighand); 241 } 242 (void) pthread_attr_destroy(&attr); 243 } 244 245 static void 246 change_user_set_privs(void) 247 { 248 priv_set_t *priv_set; 249 250 priv_set = priv_allocset(); 251 if (getppriv(PRIV_PERMITTED, priv_set) == -1) { 252 dprintf("getppriv %s", strerror(errno)); 253 } else { 254 char *p; 255 256 p = priv_set_to_str(priv_set, ',', 0); 257 dprintf("started with privs %s", p != NULL ? p : "Unknown"); 258 free(p); 259 } 260 priv_freeset(priv_set); 261 262 /* always start with the basic set */ 263 priv_set = priv_str_to_set("basic", ",", NULL); 264 if (priv_set == NULL) { 265 syslog(LOG_ERR, "converting basic privilege set: %m"); 266 exit(EXIT_FAILURE); 267 } 268 (void) priv_addset(priv_set, PRIV_FILE_CHOWN_SELF); 269 (void) priv_addset(priv_set, PRIV_FILE_DAC_READ); 270 (void) priv_addset(priv_set, PRIV_FILE_DAC_WRITE); 271 (void) priv_addset(priv_set, PRIV_NET_PRIVADDR); 272 (void) priv_addset(priv_set, PRIV_NET_RAWACCESS); 273 (void) priv_addset(priv_set, PRIV_PROC_OWNER); 274 (void) priv_addset(priv_set, PRIV_PROC_SETID); 275 (void) priv_addset(priv_set, PRIV_SYS_IP_CONFIG); 276 (void) priv_addset(priv_set, PRIV_SYS_IPC_CONFIG); 277 (void) priv_addset(priv_set, PRIV_SYS_NET_CONFIG); 278 (void) priv_addset(priv_set, PRIV_SYS_RES_CONFIG); 279 (void) priv_addset(priv_set, PRIV_SYS_RESOURCE); 280 281 if (setppriv(PRIV_SET, PRIV_INHERITABLE, priv_set) == -1) { 282 syslog(LOG_ERR, "setppriv inheritable: %m"); 283 priv_freeset(priv_set); 284 exit(EXIT_FAILURE); 285 } 286 287 if (setppriv(PRIV_SET, PRIV_PERMITTED, priv_set) == -1) { 288 syslog(LOG_ERR, "setppriv permitted: %m"); 289 priv_freeset(priv_set); 290 exit(EXIT_FAILURE); 291 } 292 293 if (setppriv(PRIV_SET, PRIV_EFFECTIVE, priv_set) == -1) { 294 syslog(LOG_ERR, "setppriv effective: %m"); 295 priv_freeset(priv_set); 296 exit(EXIT_FAILURE); 297 } 298 299 priv_freeset(priv_set); 300 } 301 302 int 303 main(int argc, char *argv[]) 304 { 305 int c; 306 int scan_lev; 307 struct np_event *e; 308 309 (void) setlocale(LC_ALL, ""); 310 (void) textdomain(TEXT_DOMAIN); 311 312 shutting_down = B_FALSE; 313 start_logging(); 314 syslog(LOG_INFO, "nwamd pid %d started", getpid()); 315 316 while ((c = getopt(argc, argv, "fs:")) != -1) { 317 switch (c) { 318 case 'f': 319 fg = B_TRUE; 320 break; 321 case 's': 322 scan_lev = atoi(optarg); 323 if (scan_lev >= DLADM_WLAN_STRENGTH_VERY_WEAK && 324 scan_lev <= DLADM_WLAN_STRENGTH_EXCELLENT) { 325 wireless_scan_level = scan_lev; 326 } else { 327 syslog(LOG_ERR, "invalid signal " 328 "strength: %s", optarg); 329 } 330 break; 331 default: 332 syslog(LOG_ERR, "unrecognized option %c", 333 optopt); 334 break; 335 } 336 } 337 338 lookup_daemon_properties(); 339 340 change_user_set_privs(); 341 342 if (!fg) 343 daemonize(); 344 345 init_signalhandling(); 346 347 init_mutexes(); 348 349 lookup_zonename(zonename, sizeof (zonename)); 350 351 initialize_interfaces(); 352 353 llp_parse_config(); 354 355 (void) start_event_collection(); 356 357 while ((e = np_queue_get_event()) != NULL) { /* forever */ 358 359 syslog(LOG_INFO, "got event type %s", 360 npe_type_str(e->npe_type)); 361 switch (e->npe_type) { 362 case EV_ROUTING: 363 case EV_NEWADDR: 364 case EV_TIMER: 365 state_machine(e); 366 free_event(e); 367 break; 368 case EV_SYS: 369 free_event(e); 370 break; 371 case EV_SHUTDOWN: 372 state_machine(e); 373 (void) pthread_cancel(routing); 374 (void) pthread_cancel(scan); 375 (void) pthread_join(routing, NULL); 376 (void) pthread_join(scan, NULL); 377 syslog(LOG_INFO, "nwamd shutting down"); 378 exit(EXIT_SUCCESS); 379 /* NOTREACHED */ 380 default: 381 free_event(e); 382 syslog(LOG_NOTICE, "unknown event"); 383 break; 384 } 385 } 386 return (0); 387 } 388