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 261 priv_emptyset(priv_set); 262 (void) priv_addset(priv_set, "basic"); 263 (void) priv_addset(priv_set, "file_chown_self"); 264 (void) priv_addset(priv_set, "file_dac_read"); 265 (void) priv_addset(priv_set, "file_dac_write"); 266 (void) priv_addset(priv_set, "net_privaddr"); 267 (void) priv_addset(priv_set, "net_rawaccess"); 268 (void) priv_addset(priv_set, "proc_exec"); 269 (void) priv_addset(priv_set, "proc_fork"); 270 (void) priv_addset(priv_set, "proc_info"); 271 (void) priv_addset(priv_set, "proc_owner"); 272 (void) priv_addset(priv_set, "proc_session"); 273 (void) priv_addset(priv_set, "proc_setid"); 274 (void) priv_addset(priv_set, "sys_ip_config"); 275 (void) priv_addset(priv_set, "sys_ipc_config"); 276 (void) priv_addset(priv_set, "sys_net_config"); 277 (void) priv_addset(priv_set, "sys_res_config"); 278 (void) priv_addset(priv_set, "sys_resource"); 279 280 if (setppriv(PRIV_SET, PRIV_INHERITABLE, priv_set) == -1) { 281 syslog(LOG_ERR, "setppriv inheritable: %m"); 282 priv_freeset(priv_set); 283 exit(EXIT_FAILURE); 284 } 285 286 if (setppriv(PRIV_SET, PRIV_PERMITTED, priv_set) == -1) { 287 syslog(LOG_ERR, "setppriv permitted: %m"); 288 priv_freeset(priv_set); 289 exit(EXIT_FAILURE); 290 } 291 292 if (setppriv(PRIV_SET, PRIV_EFFECTIVE, priv_set) == -1) { 293 syslog(LOG_ERR, "setppriv effective: %m"); 294 priv_freeset(priv_set); 295 exit(EXIT_FAILURE); 296 } 297 298 priv_freeset(priv_set); 299 } 300 301 int 302 main(int argc, char *argv[]) 303 { 304 int c; 305 int scan_lev; 306 struct np_event *e; 307 308 (void) setlocale(LC_ALL, ""); 309 (void) textdomain(TEXT_DOMAIN); 310 311 shutting_down = B_FALSE; 312 start_logging(); 313 syslog(LOG_INFO, "nwamd pid %d started", getpid()); 314 315 while ((c = getopt(argc, argv, "fs:")) != -1) { 316 switch (c) { 317 case 'f': 318 fg = B_TRUE; 319 break; 320 case 's': 321 scan_lev = atoi(optarg); 322 if (scan_lev >= DLADM_WLAN_STRENGTH_VERY_WEAK && 323 scan_lev <= DLADM_WLAN_STRENGTH_EXCELLENT) { 324 wireless_scan_level = scan_lev; 325 } else { 326 syslog(LOG_ERR, "invalid signal " 327 "strength: %s", optarg); 328 } 329 break; 330 default: 331 syslog(LOG_ERR, "unrecognized option %c", 332 optopt); 333 break; 334 } 335 } 336 337 lookup_daemon_properties(); 338 339 change_user_set_privs(); 340 341 if (!fg) 342 daemonize(); 343 344 init_signalhandling(); 345 346 init_mutexes(); 347 348 lookup_zonename(zonename, sizeof (zonename)); 349 350 initialize_interfaces(); 351 352 llp_parse_config(); 353 354 (void) start_event_collection(); 355 356 while ((e = np_queue_get_event()) != NULL) { /* forever */ 357 358 syslog(LOG_INFO, "got event type %s", 359 npe_type_str(e->npe_type)); 360 switch (e->npe_type) { 361 case EV_ROUTING: 362 case EV_NEWADDR: 363 case EV_TIMER: 364 state_machine(e); 365 free_event(e); 366 break; 367 case EV_SYS: 368 free_event(e); 369 break; 370 case EV_SHUTDOWN: 371 state_machine(e); 372 (void) pthread_cancel(routing); 373 (void) pthread_cancel(scan); 374 (void) pthread_join(routing, NULL); 375 (void) pthread_join(scan, NULL); 376 syslog(LOG_INFO, "nwamd shutting down"); 377 exit(EXIT_SUCCESS); 378 /* NOTREACHED */ 379 default: 380 free_event(e); 381 syslog(LOG_NOTICE, "unknown event"); 382 break; 383 } 384 } 385 return (0); 386 } 387