#include <errno.h>
#include <fcntl.h>
#include <libdllink.h>
#include <libintl.h>
#include <libnwam.h>
#include <locale.h>
#include <priv.h>
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <libnwam.h>
#include "conditions.h"
#include "events.h"
#include "llp.h"
#include "ncp.h"
#include "objects.h"
#include "util.h"
boolean_t fg = B_FALSE;
dladm_handle_t dld_handle = NULL;
ipadm_handle_t ipadm_handle = NULL;
boolean_t shutting_down = B_FALSE;
sigset_t original_sigmask;
static sigset_t sigwaitset;
static void nwamd_refresh(void);
static void graceful_shutdown(void);
static void
start_logging(void)
{
openlog("nwamd", LOG_PID | LOG_NDELAY, LOG_DAEMON);
}
static void
daemonize(void)
{
pid_t pid;
if ((pid = fork()) == (pid_t)-1)
pfail("fork 1 failed");
if (pid != 0) {
(void) wait(NULL);
nlog(LOG_DEBUG, "child %ld exited, daemonizing", pid);
_exit(0);
}
if (setsid() == (pid_t)-1)
pfail("setsid");
if ((pid = fork()) == (pid_t)-1)
pfail("fork 2 failed");
if (pid != 0) {
_exit(0);
}
(void) chdir("/");
(void) umask(022);
}
static void *
sighandler(void *arg)
{
int sig;
while (!shutting_down) {
sig = sigwait(&sigwaitset);
nlog(LOG_DEBUG, "signal %s caught", strsignal(sig));
switch (sig) {
case SIGTHAW:
case SIGHUP:
nwamd_fini_enms();
nwamd_fini_ncus();
nwamd_fini_locs();
nwamd_refresh();
break;
case SIGUSR1:
nwamd_log_ncus();
break;
case SIGTERM:
nlog(LOG_DEBUG, "%s received, shutting down",
strsignal(sig));
graceful_shutdown();
break;
default:
nlog(LOG_DEBUG, "unexpected signal %s received, "
"ignoring", strsignal(sig));
break;
}
}
return (NULL);
}
static void
init_signalhandling(void)
{
pthread_attr_t attr;
pthread_t sighand;
int err;
(void) pthread_attr_init(&attr);
(void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
if (err = pthread_create(&sighand, &attr, sighandler, NULL)) {
nlog(LOG_ERR, "pthread_create system: %s", strerror(err));
exit(EXIT_FAILURE);
} else {
nlog(LOG_DEBUG, "signal handler thread: %d", sighand);
}
(void) pthread_attr_destroy(&attr);
}
static void
block_signals(void)
{
(void) sigemptyset(&sigwaitset);
(void) sigaddset(&sigwaitset, SIGHUP);
(void) sigaddset(&sigwaitset, SIGUSR1);
(void) sigaddset(&sigwaitset, SIGUSR2);
(void) sigaddset(&sigwaitset, SIGTERM);
(void) sigaddset(&sigwaitset, SIGTHAW);
(void) pthread_sigmask(SIG_BLOCK, &sigwaitset, &original_sigmask);
}
static void
lookup_daemon_properties(void)
{
char *active_ncp_tmp;
char *scan_level_tmp;
(void) nwamd_lookup_boolean_property(OUR_FMRI, OUR_PG,
OUR_DEBUG_PROP_NAME, &debug);
(void) nwamd_lookup_boolean_property(OUR_FMRI, OUR_PG,
OUR_AUTOCONF_PROP_NAME, &wireless_autoconf);
(void) nwamd_lookup_boolean_property(OUR_FMRI, OUR_PG,
OUR_STRICT_BSSID_PROP_NAME, &wireless_strict_bssid);
(void) pthread_mutex_lock(&active_ncp_mutex);
if ((active_ncp_tmp = malloc(NWAM_MAX_NAME_LEN)) == NULL ||
nwamd_lookup_string_property(OUR_FMRI, OUR_PG,
OUR_ACTIVE_NCP_PROP_NAME, active_ncp_tmp, NWAM_MAX_NAME_LEN) != 0) {
(void) strlcpy(active_ncp, NWAM_NCP_NAME_AUTOMATIC,
NWAM_MAX_NAME_LEN);
} else {
(void) strlcpy(active_ncp, active_ncp_tmp, NWAM_MAX_NAME_LEN);
}
(void) pthread_mutex_unlock(&active_ncp_mutex);
free(active_ncp_tmp);
if (nwamd_lookup_count_property(OUR_FMRI, OUR_PG,
OUR_CONDITION_CHECK_INTERVAL_PROP_NAME,
&condition_check_interval) != 0)
condition_check_interval = CONDITION_CHECK_INTERVAL_DEFAULT;
if ((scan_level_tmp = malloc(NWAM_MAX_NAME_LEN)) == NULL ||
nwamd_lookup_string_property(OUR_FMRI, OUR_PG,
OUR_WIRELESS_SCAN_LEVEL_PROP_NAME, scan_level_tmp,
NWAM_MAX_NAME_LEN) != 0) {
wireless_scan_level = WIRELESS_SCAN_LEVEL_DEFAULT;
} else {
if (dladm_wlan_str2strength(scan_level_tmp,
&wireless_scan_level) != DLADM_STATUS_OK)
wireless_scan_level = DLADM_WLAN_STRENGTH_VERY_WEAK;
}
free(scan_level_tmp);
if (nwamd_lookup_count_property(OUR_FMRI, OUR_PG,
OUR_WIRELESS_SCAN_INTERVAL_PROP_NAME, &wireless_scan_interval) != 0)
wireless_scan_interval = WIRELESS_SCAN_INTERVAL_DEFAULT;
if (nwamd_lookup_count_property(OUR_FMRI, OUR_PG,
OUR_NCU_WAIT_TIME_PROP_NAME, &ncu_wait_time) != 0)
ncu_wait_time = NCU_WAIT_TIME_DEFAULT;
nlog(LOG_DEBUG, "Read daemon configuration properties.");
}
static void
nwamd_refresh(void)
{
lookup_daemon_properties();
(void) pthread_mutex_lock(&active_ncp_mutex);
current_ncu_priority_group = INVALID_PRIORITY_GROUP;
(void) pthread_mutex_unlock(&active_ncp_mutex);
nwamd_init_ncus();
nwamd_init_enms();
nwamd_init_locs();
nwamd_create_ncu_check_event(0);
nwamd_create_triggered_condition_check_event(0);
}
static void
graceful_shutdown(void)
{
nwamd_event_t event;
shutting_down = B_TRUE;
nwamd_event_sources_fini();
nwamd_door_fini();
nwamd_fini_enms();
nwamd_fini_ncus();
nwamd_fini_locs();
event = nwamd_event_init_shutdown();
if (event == NULL)
pfail("nwamd could not create shutdown event, exiting");
nwamd_event_enqueue(event);
}
int
main(int argc, char *argv[])
{
int c;
uint64_t version;
nwamd_event_t event;
dladm_status_t drc;
ipadm_status_t irc;
uid_t uid = getuid();
block_signals();
if (uid != UID_NETADM && uid != 0) {
pfail("nwamd should run as uid %d, not uid %d\n", UID_NETADM,
uid);
}
(void) setlocale(LC_ALL, "");
(void) textdomain(TEXT_DOMAIN);
start_logging();
nlog(LOG_INFO, "nwamd pid %d started", getpid());
while ((c = getopt(argc, argv, "fs:")) != -1) {
switch (c) {
case 'f':
fg = B_TRUE;
break;
default:
nlog(LOG_ERR, "unrecognized option %c",
optopt);
break;
}
}
lookup_daemon_properties();
if (!fg)
daemonize();
drc = dladm_open(&dld_handle);
if (drc != DLADM_STATUS_OK) {
char status_str[DLADM_STRSIZE];
pfail("failed to open dladm handle: %s",
dladm_status2str(drc, status_str));
}
irc = ipadm_open(&ipadm_handle, 0);
if (irc != IPADM_SUCCESS)
pfail("failed to open ipadm handle: %s", ipadm_status2str(irc));
nwamd_event_queue_init();
if (nwamd_lookup_count_property(OUR_FMRI, OUR_PG, OUR_VERSION_PROP_NAME,
&version) != 0)
nwamd_handle_upgrade(NULL);
nwamd_object_lists_init();
init_signalhandling();
event = nwamd_event_init_init();
if (event == NULL)
pfail("nwamd could not create init event, exiting");
nwamd_event_enqueue(event);
nwamd_walk_physical_configuration();
nwamd_door_init();
(void) pthread_mutex_lock(&active_ncp_mutex);
if (nwamd_ncp_action(active_ncp, NWAM_ACTION_ENABLE) != 0)
pfail("Initial enable failed for active NCP %s", active_ncp);
(void) pthread_mutex_unlock(&active_ncp_mutex);
nwamd_create_timed_condition_check_event();
nwamd_escalate();
nwamd_deescalate();
nwamd_event_sources_init();
nwamd_event_handler();
ipadm_close(ipadm_handle);
dladm_close(dld_handle);
return (EXIT_SUCCESS);
}