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.
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.
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]
23 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
28 #include <libdllink.h>
39 #include <sys/types.h>
44 #include "conditions.h"
52 * nwamd - NetWork Auto-Magic Daemon
55 boolean_t fg
= B_FALSE
;
56 dladm_handle_t dld_handle
= NULL
;
57 ipadm_handle_t ipadm_handle
= NULL
;
58 boolean_t shutting_down
= B_FALSE
;
60 sigset_t original_sigmask
;
61 static sigset_t sigwaitset
;
63 static void nwamd_refresh(void);
64 static void graceful_shutdown(void);
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, nwamd(1M), and the README in the source directory.
73 * The general structure of the code is as a set of event source threads
74 * which feed events into the event handling thread. Some of these events
75 * are internal-only (e.g UPGRADE), but some also involve propogation
76 * to external listeners (who register via a door call into the daemon).
79 * Due to being threaded, a simple set of signal handlers would not work
80 * very well for nwamd. Instead nwamd blocks signals in all but the
81 * signal handling thread at startup.
86 * In this file there are several utility functions which might otherwise
87 * belong in util.c, but since they are only called from main(), they can
88 * live here as static functions:
91 * - looking up SMF(5) properties
93 * - managing privileges(5)
99 openlog("nwamd", LOG_PID
| LOG_NDELAY
, LOG_DAEMON
);
108 * A little bit of magic here. By the first fork+setsid, we
109 * disconnect from our current controlling terminal and become
110 * a session group leader. By forking again without calling
111 * setsid again, we make certain that we are not the session
112 * group leader and can never reacquire a controlling terminal.
114 if ((pid
= fork()) == (pid_t
)-1)
115 pfail("fork 1 failed");
118 nlog(LOG_DEBUG
, "child %ld exited, daemonizing", pid
);
121 if (setsid() == (pid_t
)-1)
123 if ((pid
= fork()) == (pid_t
)-1)
124 pfail("fork 2 failed");
134 sighandler(void *arg
)
138 while (!shutting_down
) {
139 sig
= sigwait(&sigwaitset
);
140 nlog(LOG_DEBUG
, "signal %s caught", strsignal(sig
));
146 * Resumed from suspend or refresh. Clear up all
147 * objects so their states start from scratch;
157 * Undocumented "log ncu list" signal.
162 nlog(LOG_DEBUG
, "%s received, shutting down",
167 nlog(LOG_DEBUG
, "unexpected signal %s received, "
168 "ignoring", strsignal(sig
));
176 init_signalhandling(void)
182 (void) pthread_attr_init(&attr
);
183 (void) pthread_attr_setdetachstate(&attr
, PTHREAD_CREATE_DETACHED
);
184 if (err
= pthread_create(&sighand
, &attr
, sighandler
, NULL
)) {
185 nlog(LOG_ERR
, "pthread_create system: %s", strerror(err
));
188 nlog(LOG_DEBUG
, "signal handler thread: %d", sighand
);
190 (void) pthread_attr_destroy(&attr
);
194 * Construct the set of signals that we explicitly want to deal with.
195 * We block these while we're still single-threaded; this block will
196 * be inherited by all the threads we create. When we are ready to
197 * start handling signals, we will start the signal handling thread,
198 * which will sigwait() this same set of signals, and will thus receive
199 * and handle any that are sent to the process.
204 (void) sigemptyset(&sigwaitset
);
205 (void) sigaddset(&sigwaitset
, SIGHUP
);
206 (void) sigaddset(&sigwaitset
, SIGUSR1
);
207 (void) sigaddset(&sigwaitset
, SIGUSR2
);
208 (void) sigaddset(&sigwaitset
, SIGTERM
);
209 (void) sigaddset(&sigwaitset
, SIGTHAW
);
210 (void) pthread_sigmask(SIG_BLOCK
, &sigwaitset
, &original_sigmask
);
214 * Look up nwamd property values and set daemon variables appropriately.
215 * This function will be called on startup and via the signal handling
216 * thread on receiving a HUP (which occurs when the nwam service is
220 lookup_daemon_properties(void)
222 char *active_ncp_tmp
;
223 char *scan_level_tmp
;
225 (void) nwamd_lookup_boolean_property(OUR_FMRI
, OUR_PG
,
226 OUR_DEBUG_PROP_NAME
, &debug
);
227 (void) nwamd_lookup_boolean_property(OUR_FMRI
, OUR_PG
,
228 OUR_AUTOCONF_PROP_NAME
, &wireless_autoconf
);
229 (void) nwamd_lookup_boolean_property(OUR_FMRI
, OUR_PG
,
230 OUR_STRICT_BSSID_PROP_NAME
, &wireless_strict_bssid
);
232 (void) pthread_mutex_lock(&active_ncp_mutex
);
233 if ((active_ncp_tmp
= malloc(NWAM_MAX_NAME_LEN
)) == NULL
||
234 nwamd_lookup_string_property(OUR_FMRI
, OUR_PG
,
235 OUR_ACTIVE_NCP_PROP_NAME
, active_ncp_tmp
, NWAM_MAX_NAME_LEN
) != 0) {
236 (void) strlcpy(active_ncp
, NWAM_NCP_NAME_AUTOMATIC
,
239 (void) strlcpy(active_ncp
, active_ncp_tmp
, NWAM_MAX_NAME_LEN
);
241 (void) pthread_mutex_unlock(&active_ncp_mutex
);
242 free(active_ncp_tmp
);
244 if (nwamd_lookup_count_property(OUR_FMRI
, OUR_PG
,
245 OUR_CONDITION_CHECK_INTERVAL_PROP_NAME
,
246 &condition_check_interval
) != 0)
247 condition_check_interval
= CONDITION_CHECK_INTERVAL_DEFAULT
;
249 if ((scan_level_tmp
= malloc(NWAM_MAX_NAME_LEN
)) == NULL
||
250 nwamd_lookup_string_property(OUR_FMRI
, OUR_PG
,
251 OUR_WIRELESS_SCAN_LEVEL_PROP_NAME
, scan_level_tmp
,
252 NWAM_MAX_NAME_LEN
) != 0) {
253 wireless_scan_level
= WIRELESS_SCAN_LEVEL_DEFAULT
;
255 if (dladm_wlan_str2strength(scan_level_tmp
,
256 &wireless_scan_level
) != DLADM_STATUS_OK
)
257 wireless_scan_level
= DLADM_WLAN_STRENGTH_VERY_WEAK
;
259 free(scan_level_tmp
);
261 if (nwamd_lookup_count_property(OUR_FMRI
, OUR_PG
,
262 OUR_WIRELESS_SCAN_INTERVAL_PROP_NAME
, &wireless_scan_interval
) != 0)
263 wireless_scan_interval
= WIRELESS_SCAN_INTERVAL_DEFAULT
;
265 if (nwamd_lookup_count_property(OUR_FMRI
, OUR_PG
,
266 OUR_NCU_WAIT_TIME_PROP_NAME
, &ncu_wait_time
) != 0)
267 ncu_wait_time
= NCU_WAIT_TIME_DEFAULT
;
269 nlog(LOG_DEBUG
, "Read daemon configuration properties.");
273 * Re-read the SMF properties.
274 * Reset ncu priority group (since the NCUs will have to walk
275 * through their state machines again) and schedule a check
276 * Re-read objects from libnwam.
277 * Also, run condition checking for locations and ENMs.
282 lookup_daemon_properties();
284 (void) pthread_mutex_lock(&active_ncp_mutex
);
285 current_ncu_priority_group
= INVALID_PRIORITY_GROUP
;
286 (void) pthread_mutex_unlock(&active_ncp_mutex
);
292 nwamd_create_ncu_check_event(0);
293 nwamd_create_triggered_condition_check_event(0);
297 graceful_shutdown(void)
301 shutting_down
= B_TRUE
;
302 nwamd_event_sources_fini();
308 event
= nwamd_event_init_shutdown();
310 pfail("nwamd could not create shutdown event, exiting");
311 nwamd_event_enqueue(event
);
315 main(int argc
, char *argv
[])
322 uid_t uid
= getuid();
325 * Block the signals we care about (and which might cause us to
326 * exit based on default disposition) until we're ready to start
327 * handling them properly...see init_signalhandling() below.
331 if (uid
!= UID_NETADM
&& uid
!= 0) {
333 * This shouldn't happen normally. On upgrade the service might
336 pfail("nwamd should run as uid %d, not uid %d\n", UID_NETADM
,
340 (void) setlocale(LC_ALL
, "");
341 (void) textdomain(TEXT_DOMAIN
);
344 nlog(LOG_INFO
, "nwamd pid %d started", getpid());
346 while ((c
= getopt(argc
, argv
, "fs:")) != -1) {
352 nlog(LOG_ERR
, "unrecognized option %c",
358 lookup_daemon_properties();
364 * The dladm handle *must* be opened before privileges are dropped.
365 * The device privilege requirements, which are stored in
366 * /etc/security/device_policy, may not be loaded yet, as that's
367 * done by svc:/system/filesystem/root. If they are not loaded,
368 * then one must have *all* privs in order to open /dev/dld, which
369 * is one of the steps performed in dladm_open().
371 drc
= dladm_open(&dld_handle
);
372 if (drc
!= DLADM_STATUS_OK
) {
373 char status_str
[DLADM_STRSIZE
];
374 pfail("failed to open dladm handle: %s",
375 dladm_status2str(drc
, status_str
));
378 irc
= ipadm_open(&ipadm_handle
, 0);
379 if (irc
!= IPADM_SUCCESS
)
380 pfail("failed to open ipadm handle: %s", ipadm_status2str(irc
));
383 * Create the event queue before starting event sources, including
384 * signal handling, so we are ready to handle incoming events. Also
385 * start before attempting to upgrade, in case there's a problem
386 * upgrading and we need to retry (in which case we schedule an event
389 nwamd_event_queue_init();
392 * Handle upgrade of legacy config. Absence of version property
393 * (which did not exist in phase 0 or 0.5) is the indication that
394 * we need to upgrade to phase 1 (version 1).
396 if (nwamd_lookup_count_property(OUR_FMRI
, OUR_PG
, OUR_VERSION_PROP_NAME
,
398 nwamd_handle_upgrade(NULL
);
401 * Initialize lists handling internal representations of objects.
403 nwamd_object_lists_init();
405 init_signalhandling();
407 /* Enqueue init event */
408 event
= nwamd_event_init_init();
410 pfail("nwamd could not create init event, exiting");
411 nwamd_event_enqueue(event
);
414 * Collect initial user configuration.
418 * Walk the physical interfaces and update the Automatic NCP to
419 * contain the IP and link NCUs for the interfaces that exist in
422 nwamd_walk_physical_configuration();
425 * We should initialize the door at the point that we can respond to
426 * user requests about the system but before we start actually process
427 * state changes or effecting the system.
432 * Initialize data objects.
434 * Enabling an NCP involves refreshing nwam, which initializes the
435 * objects (ncu, enm, loc, known wlan). Thus, no need to
436 * explicitly initialize these objects here. The refresh also
437 * enqueues and NCU activation checking event. Location and ENM
438 * condition checking are triggered by changes in NCU states.
440 (void) pthread_mutex_lock(&active_ncp_mutex
);
441 if (nwamd_ncp_action(active_ncp
, NWAM_ACTION_ENABLE
) != 0)
442 pfail("Initial enable failed for active NCP %s", active_ncp
);
443 (void) pthread_mutex_unlock(&active_ncp_mutex
);
446 * Enqueue an event to start periodic checking of activation conditions.
448 nwamd_create_timed_condition_check_event();
451 * These two routines safely minimize our privilege set. They
452 * use reference counting to be safe in a threaded program. It is
453 * gross that we escalate/deescalate to initialize this functionality
454 * but a real fix is to add functionality to do fine grained privs
455 * (and if necessary set uid to 0) in this threaded daemon.
461 * Start the various agents (hooks on fds, threads) which collect events
463 nwamd_event_sources_init();
466 * nwamd_event_handler() only returns on shutdown.
468 nwamd_event_handler();
470 ipadm_close(ipadm_handle
);
471 dladm_close(dld_handle
);
473 return (EXIT_SUCCESS
);