#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdarg.h>
#include <strings.h>
#include <syslog.h>
#include <priv.h>
#include <wait.h>
#include <getopt.h>
#include <synch.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <libhotplug.h>
#include <libhotplug_impl.h>
#include "hotplugd_impl.h"
static const struct option lopts[] = {
{ "help", no_argument, 0, '?' },
{ "version", no_argument, 0, 'V' },
{ "debug", no_argument, 0, 'd' },
{ 0, 0, 0, 0 }
};
static void usage(void);
static boolean_t check_privileges(void);
static int daemonize(void);
static void init_signals(void);
static void signal_handler(int signum);
static void shutdown_daemon(void);
static char *prog;
static char version[] = "1.0";
static boolean_t log_flag = B_FALSE;
static boolean_t debug_flag = B_FALSE;
static boolean_t exit_flag = B_FALSE;
static sema_t signal_sem;
int
main(int argc, char *argv[])
{
int opt;
int pfd;
int status;
if ((prog = strrchr(argv[0], '/')) == NULL)
prog = argv[0];
else
prog++;
if (!check_privileges()) {
(void) fprintf(stderr, "Insufficient privileges. "
"(All privileges are required.)\n");
return (-1);
}
while ((opt = getopt_clip(argc, argv, "dV?", lopts, NULL)) != -1) {
switch (opt) {
case 'd':
debug_flag = B_TRUE;
break;
case 'V':
(void) printf("%s: Version %s\n", prog, version);
return (0);
default:
if (optopt == '?') {
usage();
return (0);
}
(void) fprintf(stderr, "Unrecognized option '%c'.\n",
optopt);
usage();
return (-1);
}
}
if (sema_init(&signal_sem, 1, USYNC_THREAD, NULL) != 0)
exit(EXIT_FAILURE);
init_signals();
if (!debug_flag)
pfd = daemonize();
if (!door_server_init()) {
if (!debug_flag) {
status = EXIT_FAILURE;
(void) write(pfd, &status, sizeof (status));
(void) close(pfd);
}
exit(EXIT_FAILURE);
}
if (!debug_flag) {
status = 0;
(void) write(pfd, &status, sizeof (status));
(void) close(pfd);
}
log_info("hotplug daemon started.\n");
while (!exit_flag)
(void) sema_wait(&signal_sem);
shutdown_daemon();
return (0);
}
static void
usage(void)
{
(void) printf("Usage: %s [-d]\n", prog);
}
static boolean_t
check_privileges(void)
{
priv_set_t *privset;
boolean_t rv = B_FALSE;
if ((privset = priv_allocset()) != NULL) {
if (getppriv(PRIV_EFFECTIVE, privset) == 0) {
rv = priv_isfullset(privset);
}
priv_freeset(privset);
}
return (rv);
}
static int
daemonize(void)
{
int status;
int pfds[2];
pid_t pid;
sigset_t set;
sigset_t oset;
(void) sigfillset(&set);
(void) sigdelset(&set, SIGABRT);
(void) sigprocmask(SIG_BLOCK, &set, &oset);
if (pipe(pfds) == -1) {
log_err("Cannot create pipe (%s)\n", strerror(errno));
exit(EXIT_FAILURE);
}
if ((pid = fork()) == -1) {
log_err("Cannot fork daemon process (%s)\n", strerror(errno));
exit(EXIT_FAILURE);
}
if (pid > 0) {
(void) close(pfds[1]);
if (read(pfds[0], &status, sizeof (status)) == sizeof (status))
_exit(status);
if ((waitpid(pid, &status, 0) == pid) && WIFEXITED(status))
_exit(WEXITSTATUS(status));
log_err("Failed to spawn daemon process.\n");
_exit(EXIT_FAILURE);
}
(void) setsid();
(void) chdir("/");
(void) umask(CMASK);
(void) sigprocmask(SIG_SETMASK, &oset, NULL);
(void) close(pfds[0]);
(void) close(0);
(void) close(1);
(void) close(2);
(void) open("/dev/null", O_RDONLY);
(void) open("/dev/null", O_WRONLY);
(void) open("/dev/null", O_WRONLY);
log_flag = B_TRUE;
openlog(prog, LOG_PID, LOG_DAEMON);
return (pfds[1]);
}
static void
init_signals(void)
{
struct sigaction act;
sigset_t set;
(void) sigfillset(&set);
(void) sigdelset(&set, SIGABRT);
(void) sigfillset(&act.sa_mask);
act.sa_handler = signal_handler;
act.sa_flags = 0;
(void) sigaction(SIGTERM, &act, NULL);
(void) sigaction(SIGHUP, &act, NULL);
(void) sigaction(SIGINT, &act, NULL);
(void) sigaction(SIGPIPE, &act, NULL);
(void) sigdelset(&set, SIGTERM);
(void) sigdelset(&set, SIGHUP);
(void) sigdelset(&set, SIGINT);
(void) sigdelset(&set, SIGPIPE);
}
static void
signal_handler(int signum)
{
log_info("Received signal %d.\n", signum);
switch (signum) {
case 0:
case SIGPIPE:
break;
default:
exit_flag = B_TRUE;
(void) sema_post(&signal_sem);
break;
}
}
static void
shutdown_daemon(void)
{
log_info("Hotplug daemon shutting down.\n");
door_server_fini();
if (log_flag)
closelog();
(void) sema_destroy(&signal_sem);
}
void
log_err(char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
if (debug_flag || !log_flag)
(void) vfprintf(stderr, fmt, ap);
else
vsyslog(LOG_ERR, fmt, ap);
va_end(ap);
}
void
log_info(char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
if (debug_flag || !log_flag)
(void) vfprintf(stdout, fmt, ap);
else
vsyslog(LOG_INFO, fmt, ap);
va_end(ap);
}
void
hp_dprintf(char *fmt, ...)
{
va_list ap;
if (debug_flag) {
va_start(ap, fmt);
(void) vprintf(fmt, ap);
va_end(ap);
}
}