#include <libvarpd.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <libgen.h>
#include <stdarg.h>
#include <stdlib.h>
#include <paths.h>
#include <limits.h>
#include <sys/corectl.h>
#include <signal.h>
#include <strings.h>
#include <sys/wait.h>
#include <unistd.h>
#include <thread.h>
#include <priv.h>
#include <libscf.h>
#define VARPD_EXIT_REQUESTED SMF_EXIT_OK
#define VARPD_EXIT_FATAL SMF_EXIT_ERR_FATAL
#define VARPD_EXIT_USAGE SMF_EXIT_ERR_CONFIG
#define VARPD_RUNDIR "/var/run/varpd"
#define VARPD_DEFAULT_DOOR "/var/run/varpd/varpd.door"
#define VARPD_PG "varpd"
#define VARPD_PROP_INC "include_path"
static varpd_handle_t *varpd_handle;
static const char *varpd_pname;
static volatile boolean_t varpd_exit = B_FALSE;
#ifdef DEBUG
const char *
_umem_debug_init()
{
return ("default,verbose");
}
const char *
_umem_logging_init(void)
{
return ("fail,contents");
}
#endif
static void
varpd_vwarn(FILE *out, const char *fmt, va_list ap)
{
int error = errno;
(void) fprintf(out, "%s: ", varpd_pname);
(void) vfprintf(out, fmt, ap);
if (fmt[strlen(fmt) - 1] != '\n')
(void) fprintf(out, ": %s\n", strerror(error));
}
static void
varpd_fatal(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
varpd_vwarn(stderr, fmt, ap);
va_end(ap);
exit(VARPD_EXIT_FATAL);
}
static void
varpd_dfatal(int dfd, const char *fmt, ...)
{
int status = VARPD_EXIT_FATAL;
va_list ap;
va_start(ap, fmt);
varpd_vwarn(stdout, fmt, ap);
va_end(ap);
(void) write(dfd, &status, sizeof (status));
exit(status);
}
static int
varpd_plugin_walk_cb(varpd_handle_t *vph, const char *name, void *unused)
{
(void) printf("loaded %s!\n", name);
return (0);
}
static int
varpd_dir_setup(void)
{
int fd;
if (mkdir(VARPD_RUNDIR, 0700) != 0) {
if (errno != EEXIST)
varpd_fatal("failed to create %s: %s", VARPD_RUNDIR,
strerror(errno));
}
fd = open(VARPD_RUNDIR, O_RDONLY);
if (fd < 0)
varpd_fatal("failed to open %s: %s", VARPD_RUNDIR,
strerror(errno));
if (fchown(fd, UID_NETADM, GID_NETADM) != 0)
varpd_fatal("failed to chown %s: %s\n", VARPD_RUNDIR,
strerror(errno));
return (fd);
}
static void
varpd_fd_setup(void)
{
int dupfd;
closefrom(STDERR_FILENO + 1);
dupfd = open(_PATH_DEVNULL, O_RDONLY);
if (dupfd < 0)
varpd_fatal("failed to open %s: %s", _PATH_DEVNULL,
strerror(errno));
if (dup2(dupfd, STDIN_FILENO) == -1)
varpd_fatal("failed to dup out stdin: %s", strerror(errno));
}
static int
varpd_daemonize(int dirfd)
{
char path[PATH_MAX];
struct rlimit rlim;
sigset_t set, oset;
int estatus, pfds[2];
pid_t child;
priv_set_t *pset;
(void) snprintf(path, sizeof (path),
"/var/run/varpd/core.%s.%%p", varpd_pname);
(void) core_set_process_path(path, strlen(path) + 1, getpid());
rlim.rlim_cur = RLIM_INFINITY;
rlim.rlim_max = RLIM_INFINITY;
(void) setrlimit(RLIMIT_CORE, &rlim);
if (getrlimit(RLIMIT_NOFILE, &rlim) == 0) {
rlim.rlim_cur = rlim.rlim_max;
(void) setrlimit(RLIMIT_NOFILE, &rlim);
}
if (fchdir(dirfd) != 0)
varpd_fatal("failed to chdir to %s", VARPD_RUNDIR);
if (sigfillset(&set) != 0)
abort();
if (sigdelset(&set, SIGABRT) != 0)
abort();
if (sigprocmask(SIG_BLOCK, &set, &oset) != 0)
abort();
if (pipe(pfds) != 0)
varpd_fatal("failed to create pipe for daemonizing");
if ((child = fork()) == -1)
varpd_fatal("failed to fork for daemonizing");
if (child != 0) {
(void) close(pfds[1]);
if (read(pfds[0], &estatus, sizeof (estatus)) ==
sizeof (estatus))
_exit(estatus);
if (waitpid(child, &estatus, 0) == child && WIFEXITED(estatus))
_exit(WEXITSTATUS(estatus));
_exit(VARPD_EXIT_FATAL);
}
if (setgroups(0, NULL) != 0)
abort();
if (setgid(GID_NETADM) == -1 || seteuid(UID_NETADM) == -1)
abort();
if ((pset = priv_allocset()) == NULL)
abort();
priv_basicset(pset);
if (priv_delset(pset, PRIV_PROC_EXEC) == -1 ||
priv_delset(pset, PRIV_PROC_INFO) == -1 ||
priv_delset(pset, PRIV_PROC_FORK) == -1 ||
priv_delset(pset, PRIV_PROC_SESSION) == -1 ||
priv_delset(pset, PRIV_FILE_LINK_ANY) == -1 ||
priv_addset(pset, PRIV_SYS_DL_CONFIG) == -1 ||
priv_addset(pset, PRIV_NET_PRIVADDR) == -1) {
abort();
}
if (setppriv(PRIV_SET, PRIV_PERMITTED, pset) == -1)
abort();
if (setppriv(PRIV_SET, PRIV_EFFECTIVE, pset) == -1)
abort();
priv_freeset(pset);
if (close(pfds[0]) != 0)
abort();
if (setsid() == -1)
abort();
if (sigprocmask(SIG_SETMASK, &oset, NULL) != 0)
abort();
(void) umask(0022);
return (pfds[1]);
}
static int
varpd_setup_lookup_threads(void)
{
int ret;
long i, ncpus = sysconf(_SC_NPROCESSORS_ONLN) * 2 + 1;
if (ncpus <= 0)
abort();
for (i = 0; i < ncpus; i++) {
thread_t thr;
ret = thr_create(NULL, 0, libvarpd_overlay_lookup_run,
varpd_handle, THR_DETACHED | THR_DAEMON, &thr);
if (ret != 0)
return (ret);
}
return (0);
}
static void
varpd_cleanup(int signal __unused)
{
varpd_exit = B_TRUE;
}
static void
varpd_load_smf(int dfd)
{
char *fmri, *inc;
scf_simple_prop_t *prop;
if ((fmri = getenv("SMF_FMRI")) == NULL)
return;
if ((prop = scf_simple_prop_get(NULL, fmri, VARPD_PG,
VARPD_PROP_INC)) == NULL)
return;
while ((inc = scf_simple_prop_next_astring(prop)) != NULL) {
int err = libvarpd_plugin_load(varpd_handle, inc);
if (err != 0) {
varpd_dfatal(dfd, "failed to load from %s: %s\n",
inc, strerror(err));
}
}
scf_simple_prop_free(prop);
}
int
main(int argc, char *argv[])
{
int err, c, dirfd, dfd, i;
const char *doorpath = VARPD_DEFAULT_DOOR;
sigset_t set;
struct sigaction act;
int nincpath = 0, nextincpath = 0;
char **incpath = NULL;
varpd_pname = basename(argv[0]);
varpd_fd_setup();
if ((err = libvarpd_create(&varpd_handle)) != 0) {
varpd_fatal("failed to open a libvarpd handle");
return (1);
}
while ((c = getopt(argc, argv, ":i:d:")) != -1) {
switch (c) {
case 'i':
if (nextincpath == nincpath) {
if (nincpath == 0)
nincpath = 16;
else
nincpath *= 2;
incpath = realloc(incpath, sizeof (char *) *
nincpath);
if (incpath == NULL) {
(void) fprintf(stderr, "failed to "
"allocate memory for the %dth "
"-I option: %s\n", nextincpath + 1,
strerror(errno));
}
}
incpath[nextincpath] = optarg;
nextincpath++;
break;
case 'd':
doorpath = optarg;
break;
default:
(void) fprintf(stderr, "unknown option: %c\n", c);
return (1);
}
}
dirfd = varpd_dir_setup();
(void) libvarpd_plugin_walk(varpd_handle, varpd_plugin_walk_cb, NULL);
dfd = varpd_daemonize(dirfd);
for (i = 0; i < nextincpath; i++) {
err = libvarpd_plugin_load(varpd_handle, incpath[i]);
if (err != 0) {
varpd_dfatal(dfd, "failed to load from %s: %s\n",
incpath[i], strerror(err));
}
}
varpd_load_smf(dfd);
if ((err = libvarpd_persist_enable(varpd_handle, VARPD_RUNDIR)) != 0)
varpd_dfatal(dfd, "failed to enable varpd persistence: %s\n",
strerror(err));
if ((err = libvarpd_persist_restore(varpd_handle)) != 0)
varpd_dfatal(dfd, "failed to enable varpd persistence: %s\n",
strerror(err));
if (sigfillset(&set) != 0)
varpd_dfatal(dfd, "failed to fill a signal set...");
if (sigdelset(&set, SIGABRT) != 0)
varpd_dfatal(dfd, "failed to unmask SIGABRT");
if (sigprocmask(SIG_BLOCK, &set, NULL) != 0)
varpd_dfatal(dfd, "failed to set our door signal mask");
if ((err = varpd_setup_lookup_threads()) != 0)
varpd_dfatal(dfd, "failed to create lookup threads: %s\n",
strerror(err));
if ((err = libvarpd_door_server_create(varpd_handle, doorpath)) != 0)
varpd_dfatal(dfd, "failed to create door server at %s: %s\n",
doorpath, strerror(err));
bzero(&act, sizeof (struct sigaction));
act.sa_handler = varpd_cleanup;
if (sigfillset(&act.sa_mask) != 0)
varpd_dfatal(dfd, "failed to fill sigaction mask");
act.sa_flags = 0;
if (sigaction(SIGHUP, &act, NULL) != 0)
varpd_dfatal(dfd, "failed to register HUP handler");
if (sigdelset(&set, SIGHUP) != 0)
varpd_dfatal(dfd, "failed to remove HUP from mask");
if (sigaction(SIGQUIT, &act, NULL) != 0)
varpd_dfatal(dfd, "failed to register QUIT handler");
if (sigdelset(&set, SIGQUIT) != 0)
varpd_dfatal(dfd, "failed to remove QUIT from mask");
if (sigaction(SIGINT, &act, NULL) != 0)
varpd_dfatal(dfd, "failed to register INT handler");
if (sigdelset(&set, SIGINT) != 0)
varpd_dfatal(dfd, "failed to remove INT from mask");
if (sigaction(SIGTERM, &act, NULL) != 0)
varpd_dfatal(dfd, "failed to register TERM handler");
if (sigdelset(&set, SIGTERM) != 0)
varpd_dfatal(dfd, "failed to remove TERM from mask");
err = 0;
(void) write(dfd, &err, sizeof (err));
(void) close(dfd);
for (;;) {
if (sigsuspend(&set) == -1)
if (errno == EFAULT)
abort();
if (varpd_exit == B_TRUE)
break;
}
libvarpd_door_server_destroy(varpd_handle);
libvarpd_destroy(varpd_handle);
return (VARPD_EXIT_REQUESTED);
}