1d71dbb73Sjbeck /* 2d71dbb73Sjbeck * CDDL HEADER START 3d71dbb73Sjbeck * 4d71dbb73Sjbeck * The contents of this file are subject to the terms of the 5d71dbb73Sjbeck * Common Development and Distribution License (the "License"). 6d71dbb73Sjbeck * You may not use this file except in compliance with the License. 7d71dbb73Sjbeck * 8d71dbb73Sjbeck * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9d71dbb73Sjbeck * or http://www.opensolaris.org/os/licensing. 10d71dbb73Sjbeck * See the License for the specific language governing permissions 11d71dbb73Sjbeck * and limitations under the License. 12d71dbb73Sjbeck * 13d71dbb73Sjbeck * When distributing Covered Code, include this CDDL HEADER in each 14d71dbb73Sjbeck * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15d71dbb73Sjbeck * If applicable, add the following below this CDDL HEADER, with the 16d71dbb73Sjbeck * fields enclosed by brackets "[]" replaced with your own identifying 17d71dbb73Sjbeck * information: Portions Copyright [yyyy] [name of copyright owner] 18d71dbb73Sjbeck * 19d71dbb73Sjbeck * CDDL HEADER END 20d71dbb73Sjbeck */ 21d71dbb73Sjbeck 22d71dbb73Sjbeck /* 23d71dbb73Sjbeck * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 24d71dbb73Sjbeck * Use is subject to license terms. 25d71dbb73Sjbeck */ 26d71dbb73Sjbeck 27d71dbb73Sjbeck #pragma ident "%Z%%M% %I% %E% SMI" 28d71dbb73Sjbeck 29d71dbb73Sjbeck /* 30d71dbb73Sjbeck * nwamd - NetWork Auto-Magic Daemon 31d71dbb73Sjbeck */ 32d71dbb73Sjbeck 33d71dbb73Sjbeck #include <fcntl.h> 34d71dbb73Sjbeck #include <priv.h> 35d71dbb73Sjbeck #include <pthread.h> 36d71dbb73Sjbeck #include <pwd.h> 37d71dbb73Sjbeck #include <stdio.h> 38d71dbb73Sjbeck #include <stdlib.h> 39d71dbb73Sjbeck #include <string.h> 40d71dbb73Sjbeck #include <signal.h> 41d71dbb73Sjbeck #include <sys/stat.h> 42d71dbb73Sjbeck #include <sys/types.h> 43d71dbb73Sjbeck #include <sys/wait.h> 44d71dbb73Sjbeck #include <syslog.h> 45d71dbb73Sjbeck #include <unistd.h> 46d71dbb73Sjbeck #include <locale.h> 47d71dbb73Sjbeck #include <libintl.h> 48d71dbb73Sjbeck #include <errno.h> 49d71dbb73Sjbeck 50d71dbb73Sjbeck #include "defines.h" 51d71dbb73Sjbeck #include "structures.h" 52d71dbb73Sjbeck #include "functions.h" 53d71dbb73Sjbeck #include "variables.h" 54d71dbb73Sjbeck 55d71dbb73Sjbeck #define TIMESPECGT(x, y) ((x.tv_sec > y.tv_sec) || \ 56d71dbb73Sjbeck ((x.tv_sec == y.tv_sec) && (x.tv_nsec > y.tv_nsec))) 57d71dbb73Sjbeck 58d71dbb73Sjbeck const char *OUR_FMRI = "svc:/network/physical:nwam"; 59d71dbb73Sjbeck const char *OUR_PG = "nwamd"; 60d71dbb73Sjbeck 61d71dbb73Sjbeck boolean_t fg = B_FALSE; 62d71dbb73Sjbeck boolean_t shutting_down; 63d71dbb73Sjbeck sigset_t original_sigmask; 64d71dbb73Sjbeck char zonename[ZONENAME_MAX]; 65d71dbb73Sjbeck 66d71dbb73Sjbeck /* 67d71dbb73Sjbeck * nwamd 68d71dbb73Sjbeck * 69d71dbb73Sjbeck * This is the Network Auto-Magic daemon. For further high level information 70d71dbb73Sjbeck * see the Network Auto-Magic project and the Approachability communities 71d71dbb73Sjbeck * on opensolaris.org, and nwamd(1M). 72d71dbb73Sjbeck * 73d71dbb73Sjbeck * The general structure of the code is as a set of threads collecting 74d71dbb73Sjbeck * system events which are fed into a state machine which alters system 75d71dbb73Sjbeck * state based on configuration. 76d71dbb73Sjbeck * 77d71dbb73Sjbeck * signal management 78d71dbb73Sjbeck * Due to being threaded, a simple set of signal handlers would not work 79d71dbb73Sjbeck * very well for nwamd. Instead nwamd blocks signals at startup and 80d71dbb73Sjbeck * then starts a thread which sits in sigwait(2) waiting for signals. 81d71dbb73Sjbeck * When a signal is received the signal handling thread dispatches it. 82d71dbb73Sjbeck * It handles: 83d71dbb73Sjbeck * - shutting down, done by creating an event which is passed through the 84d71dbb73Sjbeck * system allowing the various subsystems to do any necessary cleanup. 85d71dbb73Sjbeck * - SIGALRM for timers. 86d71dbb73Sjbeck * - SIGHUP for instance refresh, which tells us to look up various 87d71dbb73Sjbeck * properties from SMF(5). 88d71dbb73Sjbeck * 89d71dbb73Sjbeck * subprocess management 90d71dbb73Sjbeck * nwamd starts several different subprocesses to manage the system. Some 91d71dbb73Sjbeck * of those start other processes (e.g. `ifconfig <if> dhcp` ends up starting 92d71dbb73Sjbeck * dhcpagent if necessary). Due to the way we manage signals if we started 93d71dbb73Sjbeck * those up without doing anything special their signal mask would mostly 94d71dbb73Sjbeck * block signals. So we restore the signal mask when we start subprocesses. 95d71dbb73Sjbeck * This is especially important with respect to DHCP as later when we exit 96d71dbb73Sjbeck * we need to kill the dhcpagent process which we started; for details, see 97d71dbb73Sjbeck * the block comment in state_machine.c in its cleanup() function. 98d71dbb73Sjbeck */ 99d71dbb73Sjbeck 100d71dbb73Sjbeck /* 101d71dbb73Sjbeck * In this file there are several utility functions which might otherwise 102d71dbb73Sjbeck * belong in util.c, but since they are only called from main(), they can 103d71dbb73Sjbeck * live here as static functions: 104d71dbb73Sjbeck * - syslog set-up 105d71dbb73Sjbeck * - daemonizing 106d71dbb73Sjbeck * - looking up SMF(5) properties 107d71dbb73Sjbeck * - signal handling 108d71dbb73Sjbeck * - managing privileges(5) 109d71dbb73Sjbeck */ 110d71dbb73Sjbeck 111d71dbb73Sjbeck static void 112d71dbb73Sjbeck start_logging(void) 113d71dbb73Sjbeck { 114d71dbb73Sjbeck openlog("nwamd", LOG_PID | LOG_NDELAY, LOG_DAEMON); 115d71dbb73Sjbeck } 116d71dbb73Sjbeck 117d71dbb73Sjbeck static void 118d71dbb73Sjbeck daemonize(void) 119d71dbb73Sjbeck { 120d71dbb73Sjbeck pid_t pid; 121d71dbb73Sjbeck 122d71dbb73Sjbeck /* 123d71dbb73Sjbeck * A little bit of magic here. By the first fork+setsid, we 124d71dbb73Sjbeck * disconnect from our current controlling terminal and become 125d71dbb73Sjbeck * a session group leader. By forking again without calling 126d71dbb73Sjbeck * setsid again, we make certain that we are not the session 127d71dbb73Sjbeck * group leader and can never reacquire a controlling terminal. 128d71dbb73Sjbeck */ 129d71dbb73Sjbeck if ((pid = fork()) == (pid_t)-1) { 130d71dbb73Sjbeck syslog(LOG_ERR, "fork 1 failed"); 131d71dbb73Sjbeck exit(EXIT_FAILURE); 132d71dbb73Sjbeck } 133d71dbb73Sjbeck if (pid != 0) { 134d71dbb73Sjbeck (void) wait(NULL); 13510e672b7Smeem dprintf("child %ld exited, daemonizing", pid); 136d71dbb73Sjbeck _exit(0); 137d71dbb73Sjbeck } 138d71dbb73Sjbeck if (setsid() == (pid_t)-1) { 139d71dbb73Sjbeck syslog(LOG_ERR, "setsid"); 140d71dbb73Sjbeck exit(EXIT_FAILURE); 141d71dbb73Sjbeck } 142d71dbb73Sjbeck if ((pid = fork()) == (pid_t)-1) { 143d71dbb73Sjbeck syslog(LOG_ERR, "fork 2 failed"); 144d71dbb73Sjbeck exit(EXIT_FAILURE); 145d71dbb73Sjbeck } 146d71dbb73Sjbeck if (pid != 0) { 147d71dbb73Sjbeck _exit(0); 148d71dbb73Sjbeck } 149d71dbb73Sjbeck (void) chdir("/"); 150d71dbb73Sjbeck (void) umask(022); 151d71dbb73Sjbeck } 152d71dbb73Sjbeck 153d71dbb73Sjbeck /* 154d71dbb73Sjbeck * Look up nwamd property values and set daemon variables appropriately. 155d71dbb73Sjbeck * This function will be called on startup and via the signal handling 156d71dbb73Sjbeck * thread on receiving a HUP (which occurs when the nwam service is 157d71dbb73Sjbeck * refreshed). 158d71dbb73Sjbeck */ 159d71dbb73Sjbeck static void 160d71dbb73Sjbeck lookup_daemon_properties(void) 161d71dbb73Sjbeck { 162d71dbb73Sjbeck boolean_t debug_set; 163d71dbb73Sjbeck uint64_t scan_interval; 164d71dbb73Sjbeck 165d71dbb73Sjbeck if (lookup_boolean_property(OUR_PG, "debug", &debug_set) == 0) 166d71dbb73Sjbeck debug = debug_set; 167d71dbb73Sjbeck if (lookup_count_property(OUR_PG, "scan_interval", &scan_interval) == 0) 168d71dbb73Sjbeck wlan_scan_interval = scan_interval; 169d71dbb73Sjbeck dprintf("Read daemon configuration properties."); 170d71dbb73Sjbeck } 171d71dbb73Sjbeck 172d71dbb73Sjbeck /* ARGSUSED */ 173d71dbb73Sjbeck static void * 174d71dbb73Sjbeck sighandler(void *arg) 175d71dbb73Sjbeck { 176d71dbb73Sjbeck struct np_event *ev; 177d71dbb73Sjbeck sigset_t sigset; 178d71dbb73Sjbeck int sig; 179d71dbb73Sjbeck uint32_t now; 180d71dbb73Sjbeck 181d71dbb73Sjbeck (void) sigfillset(&sigset); 182d71dbb73Sjbeck 183d71dbb73Sjbeck for (;;) { 184d71dbb73Sjbeck sig = sigwait(&sigset); 185d71dbb73Sjbeck dprintf("signal %d caught", sig); 186d71dbb73Sjbeck switch (sig) { 187d71dbb73Sjbeck case SIGALRM: 188d71dbb73Sjbeck /* 189d71dbb73Sjbeck * We may have multiple interfaces with 190d71dbb73Sjbeck * scheduled timers; walk the list and 191d71dbb73Sjbeck * create a timer event for each one. 192d71dbb73Sjbeck */ 193d71dbb73Sjbeck timer_expire = TIMER_INFINITY; 194d71dbb73Sjbeck now = NSEC_TO_SEC(gethrtime()); 195d71dbb73Sjbeck walk_interface(check_interface_timer, &now); 196d71dbb73Sjbeck break; 197d71dbb73Sjbeck case SIGHUP: 198d71dbb73Sjbeck /* 199d71dbb73Sjbeck * Refresh action - reread configuration properties. 200d71dbb73Sjbeck */ 201d71dbb73Sjbeck lookup_daemon_properties(); 202d71dbb73Sjbeck break; 203d71dbb73Sjbeck default: 204d71dbb73Sjbeck syslog(LOG_NOTICE, "%s received, shutting down", 205d71dbb73Sjbeck strsignal(sig)); 206d71dbb73Sjbeck shutting_down = B_TRUE; 207d71dbb73Sjbeck if ((ev = malloc(sizeof (*ev))) == NULL) { 208d71dbb73Sjbeck dprintf("could not allocate shutdown event"); 209d71dbb73Sjbeck cleanup(); 210d71dbb73Sjbeck exit(EXIT_FAILURE); 211d71dbb73Sjbeck } 212d71dbb73Sjbeck ev->npe_type = EV_SHUTDOWN; 213d71dbb73Sjbeck ev->npe_name = NULL; 214d71dbb73Sjbeck np_queue_add_event(ev); 215d71dbb73Sjbeck break; 216d71dbb73Sjbeck } 217d71dbb73Sjbeck 218d71dbb73Sjbeck /* if we're shutting down, exit this thread */ 219d71dbb73Sjbeck if (shutting_down) 220d71dbb73Sjbeck return (NULL); 221d71dbb73Sjbeck } 222d71dbb73Sjbeck } 223d71dbb73Sjbeck 224d71dbb73Sjbeck static void 225d71dbb73Sjbeck init_signalhandling(void) 226d71dbb73Sjbeck { 227d71dbb73Sjbeck pthread_attr_t attr; 228d71dbb73Sjbeck pthread_t sighand; 229d71dbb73Sjbeck int err; 230d71dbb73Sjbeck sigset_t new; 231d71dbb73Sjbeck 232d71dbb73Sjbeck (void) sigfillset(&new); 233d71dbb73Sjbeck (void) pthread_sigmask(SIG_BLOCK, &new, &original_sigmask); 234d71dbb73Sjbeck (void) pthread_attr_init(&attr); 235d71dbb73Sjbeck (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); 236d71dbb73Sjbeck if (err = pthread_create(&sighand, &attr, sighandler, NULL)) { 237d71dbb73Sjbeck syslog(LOG_ERR, "pthread_create system: %s", strerror(err)); 238d71dbb73Sjbeck exit(EXIT_FAILURE); 239d71dbb73Sjbeck } else { 240d71dbb73Sjbeck dprintf("signal handler thread: %d", sighand); 241d71dbb73Sjbeck } 242d71dbb73Sjbeck (void) pthread_attr_destroy(&attr); 243d71dbb73Sjbeck } 244d71dbb73Sjbeck 245d71dbb73Sjbeck static void 246d71dbb73Sjbeck change_user_set_privs(void) 247d71dbb73Sjbeck { 248d71dbb73Sjbeck priv_set_t *priv_set; 249d71dbb73Sjbeck 250d71dbb73Sjbeck priv_set = priv_allocset(); 251d71dbb73Sjbeck if (getppriv(PRIV_PERMITTED, priv_set) == -1) { 252d71dbb73Sjbeck dprintf("getppriv %s", strerror(errno)); 253d71dbb73Sjbeck } else { 254d71dbb73Sjbeck char *p; 255d71dbb73Sjbeck 256d71dbb73Sjbeck p = priv_set_to_str(priv_set, ',', 0); 257d71dbb73Sjbeck dprintf("started with privs %s", p != NULL ? p : "Unknown"); 258d71dbb73Sjbeck free(p); 259d71dbb73Sjbeck } 260*9b38c944Sokie priv_freeset(priv_set); 261d71dbb73Sjbeck 262*9b38c944Sokie /* always start with the basic set */ 263*9b38c944Sokie priv_set = priv_str_to_set("basic", ",", NULL); 264*9b38c944Sokie if (priv_set == NULL) { 265*9b38c944Sokie syslog(LOG_ERR, "converting basic privilege set: %m"); 266*9b38c944Sokie exit(EXIT_FAILURE); 267*9b38c944Sokie } 268*9b38c944Sokie (void) priv_addset(priv_set, PRIV_FILE_CHOWN_SELF); 269*9b38c944Sokie (void) priv_addset(priv_set, PRIV_FILE_DAC_READ); 270*9b38c944Sokie (void) priv_addset(priv_set, PRIV_FILE_DAC_WRITE); 271*9b38c944Sokie (void) priv_addset(priv_set, PRIV_NET_PRIVADDR); 272*9b38c944Sokie (void) priv_addset(priv_set, PRIV_NET_RAWACCESS); 273*9b38c944Sokie (void) priv_addset(priv_set, PRIV_PROC_OWNER); 274*9b38c944Sokie (void) priv_addset(priv_set, PRIV_PROC_SETID); 275*9b38c944Sokie (void) priv_addset(priv_set, PRIV_SYS_IP_CONFIG); 276*9b38c944Sokie (void) priv_addset(priv_set, PRIV_SYS_IPC_CONFIG); 277*9b38c944Sokie (void) priv_addset(priv_set, PRIV_SYS_NET_CONFIG); 278*9b38c944Sokie (void) priv_addset(priv_set, PRIV_SYS_RES_CONFIG); 279*9b38c944Sokie (void) priv_addset(priv_set, PRIV_SYS_RESOURCE); 280d71dbb73Sjbeck 281d71dbb73Sjbeck if (setppriv(PRIV_SET, PRIV_INHERITABLE, priv_set) == -1) { 282d71dbb73Sjbeck syslog(LOG_ERR, "setppriv inheritable: %m"); 283d71dbb73Sjbeck priv_freeset(priv_set); 284d71dbb73Sjbeck exit(EXIT_FAILURE); 285d71dbb73Sjbeck } 286d71dbb73Sjbeck 287d71dbb73Sjbeck if (setppriv(PRIV_SET, PRIV_PERMITTED, priv_set) == -1) { 288d71dbb73Sjbeck syslog(LOG_ERR, "setppriv permitted: %m"); 289d71dbb73Sjbeck priv_freeset(priv_set); 290d71dbb73Sjbeck exit(EXIT_FAILURE); 291d71dbb73Sjbeck } 292d71dbb73Sjbeck 293d71dbb73Sjbeck if (setppriv(PRIV_SET, PRIV_EFFECTIVE, priv_set) == -1) { 294d71dbb73Sjbeck syslog(LOG_ERR, "setppriv effective: %m"); 295d71dbb73Sjbeck priv_freeset(priv_set); 296d71dbb73Sjbeck exit(EXIT_FAILURE); 297d71dbb73Sjbeck } 298d71dbb73Sjbeck 299d71dbb73Sjbeck priv_freeset(priv_set); 300d71dbb73Sjbeck } 301d71dbb73Sjbeck 302d71dbb73Sjbeck int 303d71dbb73Sjbeck main(int argc, char *argv[]) 304d71dbb73Sjbeck { 305d71dbb73Sjbeck int c; 306d71dbb73Sjbeck int scan_lev; 307d71dbb73Sjbeck struct np_event *e; 308d71dbb73Sjbeck 309d71dbb73Sjbeck (void) setlocale(LC_ALL, ""); 310d71dbb73Sjbeck (void) textdomain(TEXT_DOMAIN); 311d71dbb73Sjbeck 312d71dbb73Sjbeck shutting_down = B_FALSE; 313d71dbb73Sjbeck start_logging(); 314d71dbb73Sjbeck syslog(LOG_INFO, "nwamd pid %d started", getpid()); 315d71dbb73Sjbeck 316d71dbb73Sjbeck while ((c = getopt(argc, argv, "fs:")) != -1) { 317d71dbb73Sjbeck switch (c) { 318d71dbb73Sjbeck case 'f': 319d71dbb73Sjbeck fg = B_TRUE; 320d71dbb73Sjbeck break; 321d71dbb73Sjbeck case 's': 322d71dbb73Sjbeck scan_lev = atoi(optarg); 323d71dbb73Sjbeck if (scan_lev >= DLADM_WLAN_STRENGTH_VERY_WEAK && 324d71dbb73Sjbeck scan_lev <= DLADM_WLAN_STRENGTH_EXCELLENT) { 325d71dbb73Sjbeck wireless_scan_level = scan_lev; 326d71dbb73Sjbeck } else { 327d71dbb73Sjbeck syslog(LOG_ERR, "invalid signal " 328d71dbb73Sjbeck "strength: %s", optarg); 329d71dbb73Sjbeck } 330d71dbb73Sjbeck break; 331d71dbb73Sjbeck default: 332d71dbb73Sjbeck syslog(LOG_ERR, "unrecognized option %c", 333d71dbb73Sjbeck optopt); 334d71dbb73Sjbeck break; 335d71dbb73Sjbeck } 336d71dbb73Sjbeck } 337d71dbb73Sjbeck 338d71dbb73Sjbeck lookup_daemon_properties(); 339d71dbb73Sjbeck 340d71dbb73Sjbeck change_user_set_privs(); 341d71dbb73Sjbeck 342d71dbb73Sjbeck if (!fg) 343d71dbb73Sjbeck daemonize(); 344d71dbb73Sjbeck 345d71dbb73Sjbeck init_signalhandling(); 346d71dbb73Sjbeck 347d71dbb73Sjbeck init_mutexes(); 348d71dbb73Sjbeck 349d71dbb73Sjbeck lookup_zonename(zonename, sizeof (zonename)); 350d71dbb73Sjbeck 351d71dbb73Sjbeck initialize_interfaces(); 352d71dbb73Sjbeck 353d71dbb73Sjbeck llp_parse_config(); 354d71dbb73Sjbeck 355d71dbb73Sjbeck (void) start_event_collection(); 356d71dbb73Sjbeck 357d71dbb73Sjbeck while ((e = np_queue_get_event()) != NULL) { /* forever */ 358d71dbb73Sjbeck 359d71dbb73Sjbeck syslog(LOG_INFO, "got event type %s", 360d71dbb73Sjbeck npe_type_str(e->npe_type)); 361d71dbb73Sjbeck switch (e->npe_type) { 362d71dbb73Sjbeck case EV_ROUTING: 363d71dbb73Sjbeck case EV_NEWADDR: 364d71dbb73Sjbeck case EV_TIMER: 365d71dbb73Sjbeck state_machine(e); 366d71dbb73Sjbeck free_event(e); 367d71dbb73Sjbeck break; 368d71dbb73Sjbeck case EV_SYS: 369d71dbb73Sjbeck free_event(e); 370d71dbb73Sjbeck break; 371d71dbb73Sjbeck case EV_SHUTDOWN: 372d71dbb73Sjbeck state_machine(e); 373d71dbb73Sjbeck (void) pthread_cancel(routing); 374d71dbb73Sjbeck (void) pthread_cancel(scan); 375d71dbb73Sjbeck (void) pthread_join(routing, NULL); 376d71dbb73Sjbeck (void) pthread_join(scan, NULL); 377d71dbb73Sjbeck syslog(LOG_INFO, "nwamd shutting down"); 378d71dbb73Sjbeck exit(EXIT_SUCCESS); 379d71dbb73Sjbeck /* NOTREACHED */ 380d71dbb73Sjbeck default: 381d71dbb73Sjbeck free_event(e); 382d71dbb73Sjbeck syslog(LOG_NOTICE, "unknown event"); 383d71dbb73Sjbeck break; 384d71dbb73Sjbeck } 385d71dbb73Sjbeck } 386d71dbb73Sjbeck return (0); 387d71dbb73Sjbeck } 388