summaryrefslogtreecommitdiff
path: root/usr/src/cmd/cmd-inet/lib/nwamd/main.c
diff options
context:
space:
mode:
authorjbeck <none@none>2007-03-30 17:01:13 -0700
committerjbeck <none@none>2007-03-30 17:01:13 -0700
commitd71dbb732372504daff1f1783bc0d8864ce9bd50 (patch)
tree250aa1eba146725a05b071c536722a5e879d43f0 /usr/src/cmd/cmd-inet/lib/nwamd/main.c
parent623cc4421f2b351089438cb912f6d8b9f2fd3e6c (diff)
downloadillumos-gate-d71dbb732372504daff1f1783bc0d8864ce9bd50.tar.gz
PSARC 2007/136 Network Auto-Magic (NWAM) Phase 0
6355747 /lib/svc/method/net-svc makes a mess of hosts and ipnodes 6366093 "ifconfig <wireless-lan-device> dhcp" not enough to surf with browser 6539574 _link_aton() underallocates a buffer
Diffstat (limited to 'usr/src/cmd/cmd-inet/lib/nwamd/main.c')
-rw-r--r--usr/src/cmd/cmd-inet/lib/nwamd/main.c392
1 files changed, 392 insertions, 0 deletions
diff --git a/usr/src/cmd/cmd-inet/lib/nwamd/main.c b/usr/src/cmd/cmd-inet/lib/nwamd/main.c
new file mode 100644
index 0000000000..1072a8f11c
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/lib/nwamd/main.c
@@ -0,0 +1,392 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * nwamd - NetWork Auto-Magic Daemon
+ */
+
+#include <fcntl.h>
+#include <priv.h>
+#include <pthread.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <locale.h>
+#include <libintl.h>
+#include <errno.h>
+
+#include "defines.h"
+#include "structures.h"
+#include "functions.h"
+#include "variables.h"
+
+#define TIMESPECGT(x, y) ((x.tv_sec > y.tv_sec) || \
+ ((x.tv_sec == y.tv_sec) && (x.tv_nsec > y.tv_nsec)))
+
+const char *OUR_FMRI = "svc:/network/physical:nwam";
+const char *OUR_PG = "nwamd";
+
+boolean_t fg = B_FALSE;
+boolean_t shutting_down;
+sigset_t original_sigmask;
+char zonename[ZONENAME_MAX];
+
+/*
+ * nwamd
+ *
+ * This is the Network Auto-Magic daemon. For further high level information
+ * see the Network Auto-Magic project and the Approachability communities
+ * on opensolaris.org, and nwamd(1M).
+ *
+ * The general structure of the code is as a set of threads collecting
+ * system events which are fed into a state machine which alters system
+ * state based on configuration.
+ *
+ * signal management
+ * Due to being threaded, a simple set of signal handlers would not work
+ * very well for nwamd. Instead nwamd blocks signals at startup and
+ * then starts a thread which sits in sigwait(2) waiting for signals.
+ * When a signal is received the signal handling thread dispatches it.
+ * It handles:
+ * - shutting down, done by creating an event which is passed through the
+ * system allowing the various subsystems to do any necessary cleanup.
+ * - SIGALRM for timers.
+ * - SIGHUP for instance refresh, which tells us to look up various
+ * properties from SMF(5).
+ *
+ * subprocess management
+ * nwamd starts several different subprocesses to manage the system. Some
+ * of those start other processes (e.g. `ifconfig <if> dhcp` ends up starting
+ * dhcpagent if necessary). Due to the way we manage signals if we started
+ * those up without doing anything special their signal mask would mostly
+ * block signals. So we restore the signal mask when we start subprocesses.
+ * This is especially important with respect to DHCP as later when we exit
+ * we need to kill the dhcpagent process which we started; for details, see
+ * the block comment in state_machine.c in its cleanup() function.
+ */
+
+/*
+ * In this file there are several utility functions which might otherwise
+ * belong in util.c, but since they are only called from main(), they can
+ * live here as static functions:
+ * - syslog set-up
+ * - daemonizing
+ * - looking up SMF(5) properties
+ * - signal handling
+ * - managing privileges(5)
+ */
+
+static void
+start_logging(void)
+{
+ openlog("nwamd", LOG_PID | LOG_NDELAY, LOG_DAEMON);
+}
+
+static void
+daemonize(void)
+{
+ pid_t pid;
+
+ /*
+ * A little bit of magic here. By the first fork+setsid, we
+ * disconnect from our current controlling terminal and become
+ * a session group leader. By forking again without calling
+ * setsid again, we make certain that we are not the session
+ * group leader and can never reacquire a controlling terminal.
+ */
+ if ((pid = fork()) == (pid_t)-1) {
+ syslog(LOG_ERR, "fork 1 failed");
+ exit(EXIT_FAILURE);
+ }
+ if (pid != 0) {
+ (void) wait(NULL);
+ dprintf("child %d exited, daemonizing", pid);
+ _exit(0);
+ }
+ if (setsid() == (pid_t)-1) {
+ syslog(LOG_ERR, "setsid");
+ exit(EXIT_FAILURE);
+ }
+ if ((pid = fork()) == (pid_t)-1) {
+ syslog(LOG_ERR, "fork 2 failed");
+ exit(EXIT_FAILURE);
+ }
+ if (pid != 0) {
+ _exit(0);
+ }
+ (void) chdir("/");
+ (void) umask(022);
+ closelog();
+ (void) closefrom(STDIN_FILENO);
+ (void) open("/dev/null", O_RDONLY);
+ (void) open("/dev/null", O_WRONLY);
+ (void) dup2(STDOUT_FILENO, STDERR_FILENO);
+ start_logging();
+}
+
+/*
+ * Look up nwamd property values and set daemon variables appropriately.
+ * This function will be called on startup and via the signal handling
+ * thread on receiving a HUP (which occurs when the nwam service is
+ * refreshed).
+ */
+static void
+lookup_daemon_properties(void)
+{
+ boolean_t debug_set;
+ uint64_t scan_interval;
+
+ if (lookup_boolean_property(OUR_PG, "debug", &debug_set) == 0)
+ debug = debug_set;
+ if (lookup_count_property(OUR_PG, "scan_interval", &scan_interval) == 0)
+ wlan_scan_interval = scan_interval;
+ dprintf("Read daemon configuration properties.");
+}
+
+/* ARGSUSED */
+static void *
+sighandler(void *arg)
+{
+ struct np_event *ev;
+ sigset_t sigset;
+ int sig;
+ uint32_t now;
+
+ (void) sigfillset(&sigset);
+
+ for (;;) {
+ sig = sigwait(&sigset);
+ dprintf("signal %d caught", sig);
+ switch (sig) {
+ case SIGALRM:
+ /*
+ * We may have multiple interfaces with
+ * scheduled timers; walk the list and
+ * create a timer event for each one.
+ */
+ timer_expire = TIMER_INFINITY;
+ now = NSEC_TO_SEC(gethrtime());
+ walk_interface(check_interface_timer, &now);
+ break;
+ case SIGHUP:
+ /*
+ * Refresh action - reread configuration properties.
+ */
+ lookup_daemon_properties();
+ break;
+ default:
+ syslog(LOG_NOTICE, "%s received, shutting down",
+ strsignal(sig));
+ shutting_down = B_TRUE;
+ if ((ev = malloc(sizeof (*ev))) == NULL) {
+ dprintf("could not allocate shutdown event");
+ cleanup();
+ exit(EXIT_FAILURE);
+ }
+ ev->npe_type = EV_SHUTDOWN;
+ ev->npe_name = NULL;
+ np_queue_add_event(ev);
+ break;
+ }
+
+ /* if we're shutting down, exit this thread */
+ if (shutting_down)
+ return (NULL);
+ }
+}
+
+static void
+init_signalhandling(void)
+{
+ pthread_attr_t attr;
+ pthread_t sighand;
+ int err;
+ sigset_t new;
+
+ (void) sigfillset(&new);
+ (void) pthread_sigmask(SIG_BLOCK, &new, &original_sigmask);
+ (void) pthread_attr_init(&attr);
+ (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ if (err = pthread_create(&sighand, &attr, sighandler, NULL)) {
+ syslog(LOG_ERR, "pthread_create system: %s", strerror(err));
+ exit(EXIT_FAILURE);
+ } else {
+ dprintf("signal handler thread: %d", sighand);
+ }
+ (void) pthread_attr_destroy(&attr);
+}
+
+static void
+change_user_set_privs(void)
+{
+ priv_set_t *priv_set;
+
+ priv_set = priv_allocset();
+ if (getppriv(PRIV_PERMITTED, priv_set) == -1) {
+ dprintf("getppriv %s", strerror(errno));
+ } else {
+ char *p;
+
+ p = priv_set_to_str(priv_set, ',', 0);
+ dprintf("started with privs %s", p != NULL ? p : "Unknown");
+ free(p);
+ }
+
+ priv_emptyset(priv_set);
+ (void) priv_addset(priv_set, "basic");
+ (void) priv_addset(priv_set, "file_chown_self");
+ (void) priv_addset(priv_set, "file_dac_read");
+ (void) priv_addset(priv_set, "file_dac_write");
+ (void) priv_addset(priv_set, "net_privaddr");
+ (void) priv_addset(priv_set, "net_rawaccess");
+ (void) priv_addset(priv_set, "proc_exec");
+ (void) priv_addset(priv_set, "proc_fork");
+ (void) priv_addset(priv_set, "proc_info");
+ (void) priv_addset(priv_set, "proc_owner");
+ (void) priv_addset(priv_set, "proc_session");
+ (void) priv_addset(priv_set, "proc_setid");
+ (void) priv_addset(priv_set, "sys_ip_config");
+ (void) priv_addset(priv_set, "sys_ipc_config");
+ (void) priv_addset(priv_set, "sys_net_config");
+ (void) priv_addset(priv_set, "sys_res_config");
+ (void) priv_addset(priv_set, "sys_resource");
+
+ if (setppriv(PRIV_SET, PRIV_INHERITABLE, priv_set) == -1) {
+ syslog(LOG_ERR, "setppriv inheritable: %m");
+ priv_freeset(priv_set);
+ exit(EXIT_FAILURE);
+ }
+
+ if (setppriv(PRIV_SET, PRIV_PERMITTED, priv_set) == -1) {
+ syslog(LOG_ERR, "setppriv permitted: %m");
+ priv_freeset(priv_set);
+ exit(EXIT_FAILURE);
+ }
+
+ if (setppriv(PRIV_SET, PRIV_EFFECTIVE, priv_set) == -1) {
+ syslog(LOG_ERR, "setppriv effective: %m");
+ priv_freeset(priv_set);
+ exit(EXIT_FAILURE);
+ }
+
+ priv_freeset(priv_set);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int c;
+ int scan_lev;
+ struct np_event *e;
+
+ (void) setlocale(LC_ALL, "");
+ (void) textdomain(TEXT_DOMAIN);
+
+ shutting_down = B_FALSE;
+ start_logging();
+ syslog(LOG_INFO, "nwamd pid %d started", getpid());
+
+ while ((c = getopt(argc, argv, "fs:")) != -1) {
+ switch (c) {
+ case 'f':
+ fg = B_TRUE;
+ break;
+ case 's':
+ scan_lev = atoi(optarg);
+ if (scan_lev >= DLADM_WLAN_STRENGTH_VERY_WEAK &&
+ scan_lev <= DLADM_WLAN_STRENGTH_EXCELLENT) {
+ wireless_scan_level = scan_lev;
+ } else {
+ syslog(LOG_ERR, "invalid signal "
+ "strength: %s", optarg);
+ }
+ break;
+ default:
+ syslog(LOG_ERR, "unrecognized option %c",
+ optopt);
+ break;
+ }
+ }
+
+ lookup_daemon_properties();
+
+ change_user_set_privs();
+
+ if (!fg)
+ daemonize();
+
+ init_signalhandling();
+
+ init_mutexes();
+
+ lookup_zonename(zonename, sizeof (zonename));
+
+ initialize_interfaces();
+
+ llp_parse_config();
+
+ (void) start_event_collection();
+
+ while ((e = np_queue_get_event()) != NULL) { /* forever */
+
+ syslog(LOG_INFO, "got event type %s",
+ npe_type_str(e->npe_type));
+ switch (e->npe_type) {
+ case EV_ROUTING:
+ case EV_NEWADDR:
+ case EV_TIMER:
+ state_machine(e);
+ free_event(e);
+ break;
+ case EV_SYS:
+ free_event(e);
+ break;
+ case EV_SHUTDOWN:
+ state_machine(e);
+ (void) pthread_cancel(routing);
+ (void) pthread_cancel(scan);
+ (void) pthread_join(routing, NULL);
+ (void) pthread_join(scan, NULL);
+ syslog(LOG_INFO, "nwamd shutting down");
+ exit(EXIT_SUCCESS);
+ /* NOTREACHED */
+ default:
+ free_event(e);
+ syslog(LOG_NOTICE, "unknown event");
+ break;
+ }
+ }
+ return (0);
+}