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 %d 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 closelog(); 152 (void) closefrom(STDIN_FILENO); 153 (void) open("/dev/null", O_RDONLY); 154 (void) open("/dev/null", O_WRONLY); 155 (void) dup2(STDOUT_FILENO, STDERR_FILENO); 156 start_logging(); 157 } 158 159 /* 160 * Look up nwamd property values and set daemon variables appropriately. 161 * This function will be called on startup and via the signal handling 162 * thread on receiving a HUP (which occurs when the nwam service is 163 * refreshed). 164 */ 165 static void 166 lookup_daemon_properties(void) 167 { 168 boolean_t debug_set; 169 uint64_t scan_interval; 170 171 if (lookup_boolean_property(OUR_PG, "debug", &debug_set) == 0) 172 debug = debug_set; 173 if (lookup_count_property(OUR_PG, "scan_interval", &scan_interval) == 0) 174 wlan_scan_interval = scan_interval; 175 dprintf("Read daemon configuration properties."); 176 } 177 178 /* ARGSUSED */ 179 static void * 180 sighandler(void *arg) 181 { 182 struct np_event *ev; 183 sigset_t sigset; 184 int sig; 185 uint32_t now; 186 187 (void) sigfillset(&sigset); 188 189 for (;;) { 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 walk_interface(check_interface_timer, &now); 202 break; 203 case SIGHUP: 204 /* 205 * Refresh action - reread configuration properties. 206 */ 207 lookup_daemon_properties(); 208 break; 209 default: 210 syslog(LOG_NOTICE, "%s received, shutting down", 211 strsignal(sig)); 212 shutting_down = B_TRUE; 213 if ((ev = malloc(sizeof (*ev))) == NULL) { 214 dprintf("could not allocate shutdown event"); 215 cleanup(); 216 exit(EXIT_FAILURE); 217 } 218 ev->npe_type = EV_SHUTDOWN; 219 ev->npe_name = NULL; 220 np_queue_add_event(ev); 221 break; 222 } 223 224 /* if we're shutting down, exit this thread */ 225 if (shutting_down) 226 return (NULL); 227 } 228 } 229 230 static void 231 init_signalhandling(void) 232 { 233 pthread_attr_t attr; 234 pthread_t sighand; 235 int err; 236 sigset_t new; 237 238 (void) sigfillset(&new); 239 (void) pthread_sigmask(SIG_BLOCK, &new, &original_sigmask); 240 (void) pthread_attr_init(&attr); 241 (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); 242 if (err = pthread_create(&sighand, &attr, sighandler, NULL)) { 243 syslog(LOG_ERR, "pthread_create system: %s", strerror(err)); 244 exit(EXIT_FAILURE); 245 } else { 246 dprintf("signal handler thread: %d", sighand); 247 } 248 (void) pthread_attr_destroy(&attr); 249 } 250 251 static void 252 change_user_set_privs(void) 253 { 254 priv_set_t *priv_set; 255 256 priv_set = priv_allocset(); 257 if (getppriv(PRIV_PERMITTED, priv_set) == -1) { 258 dprintf("getppriv %s", strerror(errno)); 259 } else { 260 char *p; 261 262 p = priv_set_to_str(priv_set, ',', 0); 263 dprintf("started with privs %s", p != NULL ? p : "Unknown"); 264 free(p); 265 } 266 267 priv_emptyset(priv_set); 268 (void) priv_addset(priv_set, "basic"); 269 (void) priv_addset(priv_set, "file_chown_self"); 270 (void) priv_addset(priv_set, "file_dac_read"); 271 (void) priv_addset(priv_set, "file_dac_write"); 272 (void) priv_addset(priv_set, "net_privaddr"); 273 (void) priv_addset(priv_set, "net_rawaccess"); 274 (void) priv_addset(priv_set, "proc_exec"); 275 (void) priv_addset(priv_set, "proc_fork"); 276 (void) priv_addset(priv_set, "proc_info"); 277 (void) priv_addset(priv_set, "proc_owner"); 278 (void) priv_addset(priv_set, "proc_session"); 279 (void) priv_addset(priv_set, "proc_setid"); 280 (void) priv_addset(priv_set, "sys_ip_config"); 281 (void) priv_addset(priv_set, "sys_ipc_config"); 282 (void) priv_addset(priv_set, "sys_net_config"); 283 (void) priv_addset(priv_set, "sys_res_config"); 284 (void) priv_addset(priv_set, "sys_resource"); 285 286 if (setppriv(PRIV_SET, PRIV_INHERITABLE, priv_set) == -1) { 287 syslog(LOG_ERR, "setppriv inheritable: %m"); 288 priv_freeset(priv_set); 289 exit(EXIT_FAILURE); 290 } 291 292 if (setppriv(PRIV_SET, PRIV_PERMITTED, priv_set) == -1) { 293 syslog(LOG_ERR, "setppriv permitted: %m"); 294 priv_freeset(priv_set); 295 exit(EXIT_FAILURE); 296 } 297 298 if (setppriv(PRIV_SET, PRIV_EFFECTIVE, priv_set) == -1) { 299 syslog(LOG_ERR, "setppriv effective: %m"); 300 priv_freeset(priv_set); 301 exit(EXIT_FAILURE); 302 } 303 304 priv_freeset(priv_set); 305 } 306 307 int 308 main(int argc, char *argv[]) 309 { 310 int c; 311 int scan_lev; 312 struct np_event *e; 313 314 (void) setlocale(LC_ALL, ""); 315 (void) textdomain(TEXT_DOMAIN); 316 317 shutting_down = B_FALSE; 318 start_logging(); 319 syslog(LOG_INFO, "nwamd pid %d started", getpid()); 320 321 while ((c = getopt(argc, argv, "fs:")) != -1) { 322 switch (c) { 323 case 'f': 324 fg = B_TRUE; 325 break; 326 case 's': 327 scan_lev = atoi(optarg); 328 if (scan_lev >= DLADM_WLAN_STRENGTH_VERY_WEAK && 329 scan_lev <= DLADM_WLAN_STRENGTH_EXCELLENT) { 330 wireless_scan_level = scan_lev; 331 } else { 332 syslog(LOG_ERR, "invalid signal " 333 "strength: %s", optarg); 334 } 335 break; 336 default: 337 syslog(LOG_ERR, "unrecognized option %c", 338 optopt); 339 break; 340 } 341 } 342 343 lookup_daemon_properties(); 344 345 change_user_set_privs(); 346 347 if (!fg) 348 daemonize(); 349 350 init_signalhandling(); 351 352 init_mutexes(); 353 354 lookup_zonename(zonename, sizeof (zonename)); 355 356 initialize_interfaces(); 357 358 llp_parse_config(); 359 360 (void) start_event_collection(); 361 362 while ((e = np_queue_get_event()) != NULL) { /* forever */ 363 364 syslog(LOG_INFO, "got event type %s", 365 npe_type_str(e->npe_type)); 366 switch (e->npe_type) { 367 case EV_ROUTING: 368 case EV_NEWADDR: 369 case EV_TIMER: 370 state_machine(e); 371 free_event(e); 372 break; 373 case EV_SYS: 374 free_event(e); 375 break; 376 case EV_SHUTDOWN: 377 state_machine(e); 378 (void) pthread_cancel(routing); 379 (void) pthread_cancel(scan); 380 (void) pthread_join(routing, NULL); 381 (void) pthread_join(scan, NULL); 382 syslog(LOG_INFO, "nwamd shutting down"); 383 exit(EXIT_SUCCESS); 384 /* NOTREACHED */ 385 default: 386 free_event(e); 387 syslog(LOG_NOTICE, "unknown event"); 388 break; 389 } 390 } 391 return (0); 392 } 393