aboutsummaryrefslogtreecommitdiff
path: root/src/shared
diff options
context:
space:
mode:
authorWilliam Hubbs <w.d.hubbs@gmail.com>2022-04-06 10:51:55 -0500
committerWilliam Hubbs <w.d.hubbs@gmail.com>2022-04-06 10:51:55 -0500
commit391d12db48754861b5cecac92ee3321597ee02c1 (patch)
treeb42fad5a31ca342de7b7ecf1fb78784194c1400c /src/shared
parent0efc1b133e4182bd53cde78153bd8b5cc2e99448 (diff)
migrate fully to meson build system
- drop old build system - move shared include and source files to common directory - drop "rc-" prefix from shared include and source files - move executable-specific code to individual directories under src - adjust top-level .gitignore file for new build system This closes #489.
Diffstat (limited to 'src/shared')
-rw-r--r--src/shared/_usage.c95
-rw-r--r--src/shared/_usage.h56
-rw-r--r--src/shared/helpers.h174
-rw-r--r--src/shared/meson.build35
-rw-r--r--src/shared/misc.c492
-rw-r--r--src/shared/misc.h74
-rw-r--r--src/shared/plugin.c245
-rw-r--r--src/shared/plugin.h41
-rw-r--r--src/shared/queue.h846
-rw-r--r--src/shared/schedules.c433
-rw-r--r--src/shared/schedules.h26
-rw-r--r--src/shared/selinux.c417
-rw-r--r--src/shared/selinux.h36
-rw-r--r--src/shared/version.h.in18
-rw-r--r--src/shared/version.in1
-rw-r--r--src/shared/wtmp.c51
-rw-r--r--src/shared/wtmp.h26
17 files changed, 3066 insertions, 0 deletions
diff --git a/src/shared/_usage.c b/src/shared/_usage.c
new file mode 100644
index 00000000..bba03b92
--- /dev/null
+++ b/src/shared/_usage.c
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2007-2015 The OpenRC Authors.
+ * See the Authors file at the top-level directory of this distribution and
+ * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS
+ *
+ * This file is part of OpenRC. It is subject to the license terms in
+ * the LICENSE file found in the top-level directory of this
+ * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE
+ * This file may not be copied, modified, propagated, or distributed
+ * except according to the terms contained in the LICENSE file.
+ */
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "rc.h"
+#include "misc.h"
+#include "_usage.h"
+#include "version.h"
+
+#if lint
+# define _noreturn
+#endif
+#if __GNUC__ > 2 || defined(__INTEL_COMPILER)
+# define _noreturn __attribute__ ((__noreturn__))
+#else
+# define _noreturn
+#endif
+
+void set_quiet_options(void)
+{
+ static int qcount = 0;
+
+ qcount ++;
+ switch (qcount) {
+ case 1:
+ setenv ("EINFO_QUIET", "YES", 1);
+ break;
+ case 2:
+ setenv ("EERROR_QUIET", "YES", 1);
+ break;
+ }
+}
+
+_noreturn void show_version(void)
+{
+ const char *systype = NULL;
+
+ printf("%s (OpenRC", applet);
+ if ((systype = rc_sys()))
+ printf(" [%s]", systype);
+ printf(") %s", VERSION);
+#ifdef BRANDING
+ printf(" (%s)", BRANDING);
+#endif
+ printf("\n");
+ exit(EXIT_SUCCESS);
+}
+
+_noreturn void usage(int exit_status)
+{
+ const char * const has_arg[] = { "", "<arg>", "[arg]" };
+ int i;
+ int len;
+ char *lo;
+ char *p;
+ char *token;
+ char val[4] = "-?,";
+
+ if (usagestring)
+ printf("%s", usagestring);
+ else
+ printf("Usage: %s [options] ", applet);
+
+ if (extraopts)
+ printf("%s", extraopts);
+
+ printf("\n\nOptions: [ %s ]\n", getoptstring);
+ for (i = 0; longopts[i].name; ++i) {
+ val[1] = longopts[i].val;
+ len = printf(" %3s --%s %s", isprint(longopts[i].val) ? val : "",
+ longopts[i].name, has_arg[longopts[i].has_arg]);
+
+ lo = p = xstrdup(longopts_help[i]);
+ while ((token = strsep(&p, "\n"))) {
+ len = 36 - len;
+ if (len > 0)
+ printf("%*s", len, "");
+ puts(token);
+ len = 0;
+ }
+ free(lo);
+ }
+ exit(exit_status);
+}
diff --git a/src/shared/_usage.h b/src/shared/_usage.h
new file mode 100644
index 00000000..62c131d4
--- /dev/null
+++ b/src/shared/_usage.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2007-2015 The OpenRC Authors.
+ * See the Authors file at the top-level directory of this distribution and
+ * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS
+ *
+ * This file is part of OpenRC. It is subject to the license terms in
+ * the LICENSE file found in the top-level directory of this
+ * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE
+ * This file may not be copied, modified, propagated, or distributed
+ * except according to the terms contained in the LICENSE file.
+ */
+
+#include <getopt.h>
+
+#define getoptstring_COMMON "ChqVv"
+
+#define longopts_COMMON \
+ { "help", 0, NULL, 'h'}, \
+ { "nocolor", 0, NULL, 'C'}, \
+ { "version", 0, NULL, 'V'}, \
+ { "verbose", 0, NULL, 'v'}, \
+ { "quiet", 0, NULL, 'q'}, \
+ { NULL, 0, NULL, 0 }
+
+#define longopts_help_COMMON \
+ "Display this help output", \
+ "Disable color output", \
+ "Display software version", \
+ "Run verbosely", \
+ "Run quietly (repeat to suppress errors)"
+
+#define case_RC_COMMON_getopt_case_C setenv ("EINFO_COLOR", "NO", 1);
+#define case_RC_COMMON_getopt_case_h usage (EXIT_SUCCESS);
+#define case_RC_COMMON_getopt_case_V if (argc == 2) show_version();
+#define case_RC_COMMON_getopt_case_v setenv ("EINFO_VERBOSE", "YES", 1);
+#define case_RC_COMMON_getopt_case_q set_quiet_options();
+#define case_RC_COMMON_getopt_default usage (EXIT_FAILURE);
+
+#define case_RC_COMMON_GETOPT \
+ case 'C': case_RC_COMMON_getopt_case_C; break; \
+ case 'h': case_RC_COMMON_getopt_case_h; break; \
+ case 'V': case_RC_COMMON_getopt_case_V; break; \
+ case 'v': case_RC_COMMON_getopt_case_v; break; \
+ case 'q': case_RC_COMMON_getopt_case_q; break; \
+ default: case_RC_COMMON_getopt_default; break;
+
+extern const char *applet;
+extern const char *extraopts;
+extern const char getoptstring[];
+extern const struct option longopts[];
+extern const char * const longopts_help[];
+extern const char *usagestring;
+
+void set_quiet_options(void);
+void show_version(void);
+void usage(int exit_status);
diff --git a/src/shared/helpers.h b/src/shared/helpers.h
new file mode 100644
index 00000000..44d76552
--- /dev/null
+++ b/src/shared/helpers.h
@@ -0,0 +1,174 @@
+/*
+ * helpers.h
+ * This is private to us and not for user consumption
+ */
+
+/*
+ * Copyright (c) 2007-2015 The OpenRC Authors.
+ * See the Authors file at the top-level directory of this distribution and
+ * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS
+ *
+ * This file is part of OpenRC. It is subject to the license terms in
+ * the LICENSE file found in the top-level directory of this
+ * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE
+ * This file may not be copied, modified, propagated, or distributed
+ * except according to the terms contained in the LICENSE file.
+ */
+
+#ifndef __HELPERS_H__
+#define __HELPERS_H__
+
+#define ERRX fprintf (stderr, "out of memory\n"); exit (1)
+
+#define UNCONST(a) ((void *)(unsigned long)(const void *)(a))
+
+#ifdef lint
+# define _unused
+#endif
+#if __GNUC__ > 2 || defined(__INTEL_COMPILER)
+# define _dead __attribute__((__noreturn__))
+# define _unused __attribute__((__unused__))
+# define _xasprintf(a, b) __attribute__((__format__(__printf__, a, b)))
+#else
+# define _dead
+# define _unused
+# define _xasprintf(a, b)
+#endif
+
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+
+#ifdef __GLIBC__
+# if !defined (__UCLIBC__) && !defined (__dietlibc__)
+# define strlcpy(dst, src, size) snprintf(dst, size, "%s", src)
+# endif
+#endif
+
+#ifndef timespecsub
+#define timespecsub(tsp, usp, vsp) \
+ do { \
+ (vsp)->tv_sec = (tsp)->tv_sec - (usp)->tv_sec; \
+ (vsp)->tv_nsec = (tsp)->tv_nsec - (usp)->tv_nsec; \
+ if ((vsp)->tv_nsec < 0) { \
+ (vsp)->tv_sec--; \
+ (vsp)->tv_nsec += 1000000000L; \
+ } \
+ } while (/* CONSTCOND */ 0)
+#endif
+
+#include <stdarg.h>
+#include <stdbool.h>
+#include <sys/stat.h>
+
+_unused static void *xmalloc (size_t size)
+{
+ void *value = malloc(size);
+
+ if (value)
+ return (value);
+
+ ERRX;
+ /* NOTREACHED */
+}
+
+_unused static void *xrealloc(void *ptr, size_t size)
+{
+ void *value = realloc(ptr, size);
+
+ if (value)
+ return (value);
+
+ ERRX;
+ /* NOTREACHED */
+}
+
+_unused static char *xstrdup(const char *str)
+{
+ char *value;
+
+ if (!str)
+ return (NULL);
+
+ value = strdup(str);
+
+ if (value)
+ return (value);
+
+ ERRX;
+ /* NOTREACHED */
+}
+
+#undef ERRX
+
+/*
+ * basename_c never modifies the argument. As such, if there is a trailing
+ * slash then an empty string is returned.
+ */
+_unused static const char *basename_c(const char *path)
+{
+ const char *slash = strrchr(path, '/');
+
+ if (slash)
+ return (++slash);
+ return (path);
+}
+
+_unused static bool exists(const char *pathname)
+{
+ struct stat buf;
+
+ return (stat(pathname, &buf) == 0);
+}
+
+_unused static bool existss(const char *pathname)
+{
+ struct stat buf;
+
+ return (stat(pathname, &buf) == 0 && buf.st_size != 0);
+}
+
+/*
+ * This is an OpenRC specific version of the asprintf() function.
+ * We do this to avoid defining the _GNU_SOURCE feature test macro on
+ * glibc systems and to insure that we have a consistent function across
+ * platforms. This also allows us to call our xmalloc and xrealloc
+ * functions to handle memory allocation.
+ * this function was originally written by Mike Frysinger.
+ */
+_unused _xasprintf(2,3) static int xasprintf(char **strp, const char *fmt, ...)
+{
+ va_list ap;
+ int len;
+ int memlen;
+ char *ret;
+
+ /*
+ * Start with a buffer size that should cover the vast majority of uses
+ * (path construction).
+ */
+ memlen = 4096;
+ ret = xmalloc(memlen);
+
+ va_start(ap, fmt);
+ len = vsnprintf(ret, memlen, fmt, ap);
+ va_end(ap);
+ if (len >= memlen) {
+ /*
+ * Output was truncated, so increase buffer to exactly what we need.
+ */
+ memlen = len + 1;
+ ret = xrealloc(ret, memlen);
+ va_start(ap, fmt);
+ len = vsnprintf(ret, len + 1, fmt, ap);
+ va_end(ap);
+ }
+ if (len < 0 || len >= memlen) {
+ /* Give up! */
+ fprintf(stderr, "xasprintf: unable to format a buffer\n");
+ free(ret);
+ exit(1);
+ }
+ *strp = ret;
+ return len;
+}
+
+#endif
diff --git a/src/shared/meson.build b/src/shared/meson.build
new file mode 100644
index 00000000..b80b242e
--- /dev/null
+++ b/src/shared/meson.build
@@ -0,0 +1,35 @@
+misc_c = files([
+ 'misc.c',
+ ])
+
+plugin_c = files([
+ 'plugin.c',
+ ])
+
+schedules_c = files([
+ 'schedules.c',
+ ])
+
+if selinux_dep.found()
+ selinux_c = files([
+ 'selinux.c',
+ ])
+else
+ selinux_c = []
+endif
+
+wtmp_c = files([
+ 'wtmp.c',
+ ])
+
+usage_c = files([
+ '_usage.c',
+ ])
+
+version_h = vcs_tag(
+ input : 'version.h.in',
+ output : 'version.h')
+
+version_f = vcs_tag(
+ input : 'version.in',
+ output : 'version')
diff --git a/src/shared/misc.c b/src/shared/misc.c
new file mode 100644
index 00000000..d8e4d5e6
--- /dev/null
+++ b/src/shared/misc.c
@@ -0,0 +1,492 @@
+/*
+ * misc.c
+ * misc functions
+ */
+
+/*
+ * Copyright (c) 2007-2015 The OpenRC Authors.
+ * See the Authors file at the top-level directory of this distribution and
+ * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS
+ *
+ * This file is part of OpenRC. It is subject to the license terms in
+ * the LICENSE file found in the top-level directory of this
+ * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE
+ * This file may not be copied, modified, propagated, or distributed
+ * except according to the terms contained in the LICENSE file.
+ */
+
+#include <sys/file.h>
+#include <sys/types.h>
+#include <sys/utsname.h>
+
+#ifdef __linux__
+# include <sys/sysinfo.h>
+#endif
+
+#include <sys/time.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <utime.h>
+
+#include "einfo.h"
+#include "queue.h"
+#include "rc.h"
+#include "misc.h"
+#include "version.h"
+
+extern char **environ;
+
+bool
+rc_conf_yesno(const char *setting)
+{
+ return rc_yesno(rc_conf_value (setting));
+}
+
+static const char *const env_whitelist[] = {
+ "EERROR_QUIET", "EINFO_QUIET",
+ "IN_BACKGROUND", "IN_DRYRUN", "IN_HOTPLUG",
+ "RC_DEBUG", "RC_NODEPS",
+ "LANG", "LC_MESSAGES", "TERM",
+ "EINFO_COLOR", "EINFO_VERBOSE",
+ NULL
+};
+
+void
+env_filter(void)
+{
+ RC_STRINGLIST *env_allow;
+ RC_STRINGLIST *profile;
+ RC_STRINGLIST *env_list;
+ RC_STRING *env;
+ char *e;
+ size_t i = 0;
+
+ /* Add the user defined list of vars */
+ env_allow = rc_stringlist_split(rc_conf_value("rc_env_allow"), " ");
+ /*
+ * If '*' is an entry in rc_env_allow, do nothing as we are to pass
+ * through all environment variables.
+ */
+ if (rc_stringlist_find(env_allow, "*"))
+ return;
+ profile = rc_config_load(RC_PROFILE_ENV);
+
+ /* Copy the env and work from this so we can manipulate it safely */
+ env_list = rc_stringlist_new();
+ while (environ && environ[i]) {
+ env = rc_stringlist_add(env_list, environ[i++]);
+ e = strchr(env->value, '=');
+ if (e)
+ *e = '\0';
+ }
+
+ TAILQ_FOREACH(env, env_list, entries) {
+ /* Check the whitelist */
+ for (i = 0; env_whitelist[i]; i++) {
+ if (strcmp(env_whitelist[i], env->value) == 0)
+ break;
+ }
+ if (env_whitelist[i])
+ continue;
+
+ /* Check our user defined list */
+ if (rc_stringlist_find(env_allow, env->value))
+ continue;
+
+ /* OK, not allowed! */
+ unsetenv(env->value);
+ }
+
+ /* Now add anything missing from the profile */
+ TAILQ_FOREACH(env, profile, entries) {
+ e = strchr(env->value, '=');
+ *e = '\0';
+ if (!getenv(env->value))
+ setenv(env->value, e + 1, 1);
+ }
+
+ rc_stringlist_free(env_list);
+ rc_stringlist_free(env_allow);
+ rc_stringlist_free(profile);
+}
+
+void
+env_config(void)
+{
+ size_t pplen = strlen(RC_PATH_PREFIX);
+ char *path;
+ char *p;
+ char *e;
+ size_t l;
+ struct utsname uts;
+ FILE *fp;
+ char *token;
+ char *np;
+ char *npp;
+ char *tok;
+ const char *sys = rc_sys();
+ char *buffer = NULL;
+ size_t size = 0;
+
+ /* Ensure our PATH is prefixed with the system locations first
+ for a little extra security */
+ path = getenv("PATH");
+ if (!path)
+ setenv("PATH", RC_PATH_PREFIX, 1);
+ else if (strncmp (RC_PATH_PREFIX, path, pplen) != 0) {
+ l = strlen(path) + pplen + 3;
+ e = p = xmalloc(sizeof(char) * l);
+ p += snprintf(p, l, "%s", RC_PATH_PREFIX);
+
+ /* Now go through the env var and only add bits not in our
+ * PREFIX */
+ while ((token = strsep(&path, ":"))) {
+ np = npp = xstrdup(RC_PATH_PREFIX);
+ while ((tok = strsep(&npp, ":")))
+ if (strcmp(tok, token) == 0)
+ break;
+ if (!tok)
+ p += snprintf(p, l - (p - e), ":%s", token);
+ free (np);
+ }
+ *p++ = '\0';
+ unsetenv("PATH");
+ setenv("PATH", e, 1);
+ free(e);
+ }
+
+ setenv("RC_VERSION", VERSION, 1);
+ setenv("RC_LIBEXECDIR", RC_LIBEXECDIR, 1);
+ setenv("RC_SVCDIR", RC_SVCDIR, 1);
+ setenv("RC_TMPDIR", RC_SVCDIR "/tmp", 1);
+ setenv("RC_BOOTLEVEL", RC_LEVEL_BOOT, 1);
+ e = rc_runlevel_get();
+ setenv("RC_RUNLEVEL", e, 1);
+ free(e);
+
+ if ((fp = fopen(RC_KRUNLEVEL, "r"))) {
+ if (getline(&buffer, &size, fp) != -1) {
+ l = strlen (buffer) - 1;
+ if (buffer[l] == '\n')
+ buffer[l] = 0;
+ setenv("RC_DEFAULTLEVEL", buffer, 1);
+ }
+ fclose(fp);
+ } else
+ setenv("RC_DEFAULTLEVEL", RC_LEVEL_DEFAULT, 1);
+
+ free(buffer);
+ if (sys)
+ setenv("RC_SYS", sys, 1);
+
+#ifdef PREFIX
+ setenv("RC_PREFIX", RC_PREFIX, 1);
+#endif
+
+ /* Some scripts may need to take a different code path if
+ Linux/FreeBSD, etc
+ To save on calling uname, we store it in an environment variable */
+ if (uname(&uts) == 0)
+ setenv("RC_UNAME", uts.sysname, 1);
+
+ /* Be quiet or verbose as necessary */
+ if (rc_conf_yesno("rc_quiet"))
+ setenv("EINFO_QUIET", "YES", 1);
+ if (rc_conf_yesno("rc_verbose"))
+ setenv("EINFO_VERBOSE", "YES", 1);
+
+ errno = 0;
+ if ((!rc_conf_yesno("rc_color") && errno == 0) ||
+ rc_conf_yesno("rc_nocolor"))
+ setenv("EINFO_COLOR", "NO", 1);
+}
+
+int
+signal_setup(int sig, void (*handler)(int))
+{
+ struct sigaction sa;
+
+ memset(&sa, 0, sizeof (sa));
+ sigemptyset(&sa.sa_mask);
+ sa.sa_handler = handler;
+ return sigaction(sig, &sa, NULL);
+}
+
+int
+signal_setup_restart(int sig, void (*handler)(int))
+{
+ struct sigaction sa;
+
+ memset(&sa, 0, sizeof (sa));
+ sigemptyset(&sa.sa_mask);
+ sa.sa_handler = handler;
+ sa.sa_flags = SA_RESTART;
+ return sigaction(sig, &sa, NULL);
+}
+
+int
+svc_lock(const char *applet)
+{
+ char *file = NULL;
+ int fd;
+
+ xasprintf(&file, RC_SVCDIR "/exclusive/%s", applet);
+ fd = open(file, O_WRONLY | O_CREAT | O_NONBLOCK, 0664);
+ free(file);
+ if (fd == -1)
+ return -1;
+ if (flock(fd, LOCK_EX | LOCK_NB) == -1) {
+ eerror("Call to flock failed: %s", strerror(errno));
+ close(fd);
+ return -1;
+ }
+ return fd;
+}
+
+int
+svc_unlock(const char *applet, int fd)
+{
+ char *file = NULL;
+
+ xasprintf(&file, RC_SVCDIR "/exclusive/%s", applet);
+ close(fd);
+ unlink(file);
+ free(file);
+ return -1;
+}
+
+pid_t
+exec_service(const char *service, const char *arg)
+{
+ char *file, sfd[32];
+ int fd;
+ pid_t pid = -1;
+ sigset_t full;
+ sigset_t old;
+ struct sigaction sa;
+
+ fd = svc_lock(basename_c(service));
+ if (fd == -1)
+ return -1;
+
+ file = rc_service_resolve(service);
+ if (!exists(file)) {
+ rc_service_mark(service, RC_SERVICE_STOPPED);
+ svc_unlock(basename_c(service), fd);
+ free(file);
+ return 0;
+ }
+ snprintf(sfd, sizeof(sfd), "%d", fd);
+
+ /* We need to block signals until we have forked */
+ memset(&sa, 0, sizeof (sa));
+ sa.sa_handler = SIG_DFL;
+ sigemptyset(&sa.sa_mask);
+ sigfillset(&full);
+ sigprocmask(SIG_SETMASK, &full, &old);
+
+ if ((pid = fork()) == 0) {
+ /* Restore default handlers */
+ sigaction(SIGCHLD, &sa, NULL);
+ sigaction(SIGHUP, &sa, NULL);
+ sigaction(SIGINT, &sa, NULL);
+ sigaction(SIGQUIT, &sa, NULL);
+ sigaction(SIGTERM, &sa, NULL);
+ sigaction(SIGUSR1, &sa, NULL);
+ sigaction(SIGWINCH, &sa, NULL);
+
+ /* Unmask signals */
+ sigprocmask(SIG_SETMASK, &old, NULL);
+
+ /* Safe to run now */
+ execl(file, file, "--lockfd", sfd, arg, (char *) NULL);
+ fprintf(stderr, "unable to exec `%s': %s\n",
+ file, strerror(errno));
+ svc_unlock(basename_c(service), fd);
+ _exit(EXIT_FAILURE);
+ }
+
+ if (pid == -1) {
+ fprintf(stderr, "fork: %s\n",strerror (errno));
+ svc_unlock(basename_c(service), fd);
+ } else
+ fcntl(fd, F_SETFD, fcntl(fd, F_GETFD, 0) | FD_CLOEXEC);
+
+ sigprocmask(SIG_SETMASK, &old, NULL);
+ free(file);
+ return pid;
+}
+
+int
+parse_mode(mode_t *mode, char *text)
+{
+ char *p;
+ unsigned long l;
+
+ /* Check for a numeric mode */
+ if ((*text - '0') < 8) {
+ l = strtoul(text, &p, 8);
+ if (*p || l > 07777U) {
+ errno = EINVAL;
+ return -1;
+ }
+ *mode = (mode_t) l;
+ return 0;
+ }
+
+ /* We currently don't check g+w type stuff */
+ errno = EINVAL;
+ return -1;
+}
+
+int
+is_writable(const char *path)
+{
+ if (access(path, W_OK) == 0)
+ return 1;
+
+ return 0;
+}
+
+RC_DEPTREE * _rc_deptree_load(int force, int *regen)
+{
+ int fd;
+ int retval;
+ int serrno = errno;
+ int merrno;
+ time_t t;
+ char file[PATH_MAX];
+ struct stat st;
+ struct utimbuf ut;
+ FILE *fp;
+
+ t = 0;
+ if (rc_deptree_update_needed(&t, file) || force != 0) {
+ /* Test if we have permission to update the deptree */
+ fd = open(RC_DEPTREE_CACHE, O_WRONLY);
+ merrno = errno;
+ errno = serrno;
+ if (fd == -1 && merrno == EACCES)
+ return rc_deptree_load();
+ close(fd);
+
+ if (regen)
+ *regen = 1;
+ ebegin("Caching service dependencies");
+ retval = rc_deptree_update() ? 0 : -1;
+ eend (retval, "Failed to update the dependency tree");
+
+ if (retval == 0) {
+ if (stat(RC_DEPTREE_CACHE, &st) != 0) {
+ eerror("stat(%s): %s", RC_DEPTREE_CACHE, strerror(errno));
+ return NULL;
+ }
+ if (st.st_mtime < t) {
+ eerror("Clock skew detected with `%s'", file);
+ eerrorn("Adjusting mtime of `" RC_DEPTREE_CACHE
+ "' to %s", ctime(&t));
+ fp = fopen(RC_DEPTREE_SKEWED, "w");
+ if (fp != NULL) {
+ fprintf(fp, "%s\n", file);
+ fclose(fp);
+ }
+ ut.actime = t;
+ ut.modtime = t;
+ utime(RC_DEPTREE_CACHE, &ut);
+ } else {
+ if (exists(RC_DEPTREE_SKEWED))
+ unlink(RC_DEPTREE_SKEWED);
+ }
+ }
+ if (force == -1 && regen != NULL)
+ *regen = retval;
+ }
+ return rc_deptree_load();
+}
+
+static const struct {
+ const char * const name;
+ RC_SERVICE bit;
+} service_bits[] = {
+ { "service_started", RC_SERVICE_STARTED, },
+ { "service_stopped", RC_SERVICE_STOPPED, },
+ { "service_inactive", RC_SERVICE_INACTIVE, },
+ { "service_starting", RC_SERVICE_STARTING, },
+ { "service_stopping", RC_SERVICE_STOPPING, },
+ { "service_hotplugged", RC_SERVICE_HOTPLUGGED, },
+ { "service_wasinactive", RC_SERVICE_WASINACTIVE, },
+ { "service_failed", RC_SERVICE_FAILED, },
+ { "service_crashed", RC_SERVICE_CRASHED, },
+};
+
+RC_SERVICE lookup_service_state(const char *service)
+{
+ size_t i;
+ for (i = 0; i < ARRAY_SIZE(service_bits); ++i)
+ if (!strcmp(service, service_bits[i].name))
+ return service_bits[i].bit;
+ return 0;
+}
+
+void from_time_t(char *time_string, time_t tv)
+{
+ strftime(time_string, 20, "%Y-%m-%d %H:%M:%S", localtime(&tv));
+}
+
+time_t to_time_t(char *timestring)
+{
+ int check = 0;
+ int year = 0;
+ int month = 0;
+ int day = 0;
+ int hour = 0;
+ int min = 0;
+ int sec = 0;
+ struct tm breakdown = {0};
+ time_t result = -1;
+
+ check = sscanf(timestring, "%4d-%2d-%2d %2d:%2d:%2d",
+ &year, &month, &day, &hour, &min, &sec);
+ if (check == 6) {
+ breakdown.tm_year = year - 1900; /* years since 1900 */
+ breakdown.tm_mon = month - 1;
+ breakdown.tm_mday = day;
+ breakdown.tm_hour = hour;
+ breakdown.tm_min = min;
+ breakdown.tm_sec = sec;
+ breakdown.tm_isdst = -1;
+ result = mktime(&breakdown);
+ }
+ return result;
+}
+
+pid_t get_pid(const char *applet,const char *pidfile)
+{
+ FILE *fp;
+ pid_t pid;
+
+ if (!pidfile)
+ return -1;
+
+ if ((fp = fopen(pidfile, "r")) == NULL) {
+ ewarnv("%s: fopen `%s': %s", applet, pidfile, strerror(errno));
+ return -1;
+ }
+
+ if (fscanf(fp, "%d", &pid) != 1) {
+ ewarnv("%s: no pid found in `%s'", applet, pidfile);
+ fclose(fp);
+ return -1;
+ }
+
+ fclose(fp);
+
+ return pid;
+}
diff --git a/src/shared/misc.h b/src/shared/misc.h
new file mode 100644
index 00000000..6821038d
--- /dev/null
+++ b/src/shared/misc.h
@@ -0,0 +1,74 @@
+/*
+ * misc.h
+ * This is private to us and not for user consumption
+*/
+
+/*
+ * Copyright (c) 2007-2015 The OpenRC Authors.
+ * See the Authors file at the top-level directory of this distribution and
+ * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS
+ *
+ * This file is part of OpenRC. It is subject to the license terms in
+ * the LICENSE file found in the top-level directory of this
+ * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE
+ * This file may not be copied, modified, propagated, or distributed
+ * except according to the terms contained in the LICENSE file.
+ */
+
+#ifndef __RC_MISC_H__
+#define __RC_MISC_H__
+
+#include <sys/stat.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "helpers.h"
+
+#define RC_LEVEL_BOOT "boot"
+#define RC_LEVEL_DEFAULT "default"
+
+#define RC_DEPTREE_CACHE RC_SVCDIR "/deptree"
+#define RC_DEPTREE_SKEWED RC_SVCDIR "/clock-skewed"
+#define RC_KRUNLEVEL RC_SVCDIR "/krunlevel"
+#define RC_STARTING RC_SVCDIR "/rc.starting"
+#define RC_STOPPING RC_SVCDIR "/rc.stopping"
+
+#define RC_SVCDIR_STARTING RC_SVCDIR "/starting"
+#define RC_SVCDIR_INACTIVE RC_SVCDIR "/inactive"
+#define RC_SVCDIR_STARTED RC_SVCDIR "/started"
+#define RC_SVCDIR_COLDPLUGGED RC_SVCDIR "/coldplugged"
+
+char *rc_conf_value(const char *var);
+bool rc_conf_yesno(const char *var);
+void env_filter(void);
+void env_config(void);
+int signal_setup(int sig, void (*handler)(int));
+int signal_setup_restart(int sig, void (*handler)(int));
+int svc_lock(const char *);
+int svc_unlock(const char *, int);
+pid_t exec_service(const char *, const char *);
+
+/*
+ * Check whether path is writable or not,
+ * this also works properly with read-only filesystems
+ */
+int is_writable(const char *);
+
+#define service_start(service) exec_service(service, "start");
+#define service_stop(service) exec_service(service, "stop");
+
+int parse_mode(mode_t *, char *);
+
+/* Handy function so we can wrap einfo around our deptree */
+RC_DEPTREE *_rc_deptree_load (int, int *);
+
+RC_SERVICE lookup_service_state(const char *service);
+void from_time_t(char *time_string, time_t tv);
+time_t to_time_t(char *timestring);
+pid_t get_pid(const char *applet, const char *pidfile);
+
+#endif
diff --git a/src/shared/plugin.c b/src/shared/plugin.c
new file mode 100644
index 00000000..cce845fa
--- /dev/null
+++ b/src/shared/plugin.c
@@ -0,0 +1,245 @@
+/*
+ * plugin.c
+ * Simple plugin handler
+ */
+
+/*
+ * Copyright (c) 2007-2015 The OpenRC Authors.
+ * See the Authors file at the top-level directory of this distribution and
+ * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS
+ *
+ * This file is part of OpenRC. It is subject to the license terms in
+ * the LICENSE file found in the top-level directory of this
+ * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE
+ * This file may not be copied, modified, propagated, or distributed
+ * except according to the terms contained in the LICENSE file.
+ */
+
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <dirent.h>
+#include <dlfcn.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "einfo.h"
+#include "queue.h"
+#include "rc.h"
+#include "misc.h"
+#include "plugin.h"
+
+#define RC_PLUGIN_HOOK "rc_plugin_hook"
+
+bool rc_in_plugin = false;
+
+typedef struct plugin
+{
+ char *name;
+ void *handle;
+ int (*hook)(RC_HOOK, const char *);
+ TAILQ_ENTRY(plugin) entries;
+} PLUGIN;
+TAILQ_HEAD(, plugin) plugins;
+
+#ifndef __FreeBSD__
+dlfunc_t
+dlfunc(void * __restrict handle, const char * __restrict symbol)
+{
+ union {
+ void *d;
+ dlfunc_t f;
+ } rv;
+
+ rv.d = dlsym(handle, symbol);
+ return rv.f;
+}
+#endif
+
+void
+rc_plugin_load(void)
+{
+ DIR *dp;
+ struct dirent *d;
+ PLUGIN *plugin;
+ char *file = NULL;
+ void *h;
+ int (*fptr)(RC_HOOK, const char *);
+
+ /* Don't load plugins if we're in one */
+ if (rc_in_plugin)
+ return;
+
+ TAILQ_INIT(&plugins);
+
+ if (!(dp = opendir(RC_PLUGINDIR)))
+ return;
+
+ while ((d = readdir(dp))) {
+ if (d->d_name[0] == '.')
+ continue;
+
+ xasprintf(&file, RC_PLUGINDIR "/%s", d->d_name);
+ h = dlopen(file, RTLD_LAZY);
+ free(file);
+ if (h == NULL) {
+ eerror("dlopen: %s", dlerror());
+ continue;
+ }
+
+ fptr = (int (*)(RC_HOOK, const char *))
+ dlfunc(h, RC_PLUGIN_HOOK);
+ if (fptr == NULL) {
+ eerror("%s: cannot find symbol `%s'",
+ d->d_name, RC_PLUGIN_HOOK);
+ dlclose(h);
+ } else {
+ plugin = xmalloc(sizeof(*plugin));
+ plugin->name = xstrdup(d->d_name);
+ plugin->handle = h;
+ plugin->hook = fptr;
+ TAILQ_INSERT_TAIL(&plugins, plugin, entries);
+ }
+ }
+ closedir(dp);
+}
+
+int
+rc_waitpid(pid_t pid)
+{
+ int status;
+
+ while (waitpid(pid, &status, 0) == -1) {
+ if (errno != EINTR) {
+ status = -1;
+ break;
+ }
+ }
+ return status;
+}
+
+void
+rc_plugin_run(RC_HOOK hook, const char *value)
+{
+ PLUGIN *plugin;
+ struct sigaction sa;
+ sigset_t empty;
+ sigset_t full;
+ sigset_t old;
+ int i;
+ int flags;
+ int pfd[2];
+ pid_t pid;
+ char *buffer;
+ char *token;
+ char *p;
+ ssize_t nr;
+ int retval;
+
+ /* Don't run plugins if we're in one */
+ if (rc_in_plugin)
+ return;
+
+ /* We need to block signals until we have forked */
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = SIG_DFL;
+ sigemptyset(&sa.sa_mask);
+ sigemptyset(&empty);
+ sigfillset(&full);
+
+ TAILQ_FOREACH(plugin, &plugins, entries) {
+ /* We create a pipe so that plugins can affect our environment
+ * vars, which in turn influence our scripts. */
+ if (pipe(pfd) == -1) {
+ eerror("pipe: %s", strerror(errno));
+ return;
+ }
+
+ /* Stop any scripts from inheriting us.
+ * This is actually quite important as without this, the splash
+ * plugin will probably hang when running in silent mode. */
+ for (i = 0; i < 2; i++)
+ if ((flags = fcntl (pfd[i], F_GETFD, 0)) < 0 ||
+ fcntl (pfd[i], F_SETFD, flags | FD_CLOEXEC) < 0)
+ eerror("fcntl: %s", strerror(errno));
+
+ sigprocmask(SIG_SETMASK, &full, &old);
+
+ /* We run the plugin in a new process so we never crash
+ * or otherwise affected by it */
+ if ((pid = fork()) == -1) {
+ eerror("fork: %s", strerror(errno));
+ break;
+ }
+
+ if (pid == 0) {
+ /* Restore default handlers */
+ sigaction(SIGCHLD, &sa, NULL);
+ sigaction(SIGHUP, &sa, NULL);
+ sigaction(SIGINT, &sa, NULL);
+ sigaction(SIGQUIT, &sa, NULL);
+ sigaction(SIGTERM, &sa, NULL);
+ sigaction(SIGUSR1, &sa, NULL);
+ sigaction(SIGWINCH, &sa, NULL);
+ sigprocmask(SIG_SETMASK, &old, NULL);
+
+ rc_in_plugin = true;
+ close(pfd[0]);
+ rc_environ_fd = fdopen(pfd[1], "w");
+ retval = plugin->hook(hook, value);
+ fclose(rc_environ_fd);
+ rc_environ_fd = NULL;
+
+ /* Just in case the plugin sets this to false */
+ rc_in_plugin = true;
+ exit(retval);
+ }
+
+ sigprocmask(SIG_SETMASK, &old, NULL);
+ close(pfd[1]);
+ buffer = xmalloc(sizeof(char) * BUFSIZ);
+ memset(buffer, 0, BUFSIZ);
+
+ while ((nr = read(pfd[0], buffer, BUFSIZ)) > 0) {
+ p = buffer;
+ while (*p && p - buffer < nr) {
+ token = strsep(&p, "=");
+ if (token) {
+ unsetenv(token);
+ if (*p) {
+ setenv(token, p, 1);
+ p += strlen(p) + 1;
+ } else
+ p++;
+ }
+ }
+ }
+
+ free(buffer);
+ close(pfd[0]);
+
+ rc_waitpid(pid);
+ }
+}
+
+void
+rc_plugin_unload(void)
+{
+ PLUGIN *plugin = TAILQ_FIRST(&plugins);
+ PLUGIN *next;
+
+ while (plugin) {
+ next = TAILQ_NEXT(plugin, entries);
+ dlclose(plugin->handle);
+ free(plugin->name);
+ free(plugin);
+ plugin = next;
+ }
+ TAILQ_INIT(&plugins);
+}
diff --git a/src/shared/plugin.h b/src/shared/plugin.h
new file mode 100644
index 00000000..db4b6878
--- /dev/null
+++ b/src/shared/plugin.h
@@ -0,0 +1,41 @@
+/*
+ * plugin.h
+ * Private instructions to use plugins
+ */
+
+/*
+ * Copyright (c) 2007-2015 The OpenRC Authors.
+ * See the Authors file at the top-level directory of this distribution and
+ * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS
+ *
+ * This file is part of OpenRC. It is subject to the license terms in
+ * the LICENSE file found in the top-level directory of this
+ * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE
+ * This file may not be copied, modified, propagated, or distributed
+ * except according to the terms contained in the LICENSE file.
+ */
+
+#ifndef __LIBRC_PLUGIN_H__
+#define __LIBRC_PLUGIN_H__
+
+/* A simple flag to say if we're in a plugin proccess or not.
+ * Mainly used in atexit code. */
+extern bool rc_in_plugin;
+
+int rc_waitpid(pid_t pid);
+void rc_plugin_load(void);
+void rc_plugin_unload(void);
+void rc_plugin_run(RC_HOOK, const char *value);
+
+/* dlfunc defines needed to avoid ISO errors. FreeBSD has this right :) */
+#if !defined(__FreeBSD__) && !defined(__DragonFly__)
+struct __dlfunc_arg {
+ int __dlfunc_dummy;
+};
+
+typedef void (*dlfunc_t)(struct __dlfunc_arg);
+
+dlfunc_t dlfunc (void * __restrict handle, const char * __restrict symbol);
+#endif
+
+#endif
diff --git a/src/shared/queue.h b/src/shared/queue.h
new file mode 100644
index 00000000..67f801d7
--- /dev/null
+++ b/src/shared/queue.h
@@ -0,0 +1,846 @@
+/* $NetBSD: queue.h,v 1.67 2014/05/17 21:22:56 rmind Exp $ */
+
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)queue.h 8.5 (Berkeley) 8/20/94
+ */
+
+#ifndef _SYS_QUEUE_H_
+#define _SYS_QUEUE_H_
+
+/*
+ * This file defines five types of data structures: singly-linked lists,
+ * lists, simple queues, tail queues, and circular queues.
+ *
+ * A singly-linked list is headed by a single forward pointer. The
+ * elements are singly linked for minimum space and pointer manipulation
+ * overhead at the expense of O(n) removal for arbitrary elements. New
+ * elements can be added to the list after an existing element or at the
+ * head of the list. Elements being removed from the head of the list
+ * should use the explicit macro for this purpose for optimum
+ * efficiency. A singly-linked list may only be traversed in the forward
+ * direction. Singly-linked lists are ideal for applications with large
+ * datasets and few or no removals or for implementing a LIFO queue.
+ *
+ * A list is headed by a single forward pointer (or an array of forward
+ * pointers for a hash table header). The elements are doubly linked
+ * so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before
+ * or after an existing element or at the head of the list. A list
+ * may only be traversed in the forward direction.
+ *
+ * A simple queue is headed by a pair of pointers, one the head of the
+ * list and the other to the tail of the list. The elements are singly
+ * linked to save space, so elements can only be removed from the
+ * head of the list. New elements can be added to the list after
+ * an existing element, at the head of the list, or at the end of the
+ * list. A simple queue may only be traversed in the forward direction.
+ *
+ * A tail queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or
+ * after an existing element, at the head of the list, or at the end of
+ * the list. A tail queue may be traversed in either direction.
+ *
+ * A circle queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or after
+ * an existing element, at the head of the list, or at the end of the list.
+ * A circle queue may be traversed in either direction, but has a more
+ * complex end of list detection.
+ *
+ * For details on the use of these macros, see the queue(3) manual page.
+ */
+
+/*
+ * Include the definition of NULL only on NetBSD because sys/null.h
+ * is not available elsewhere. This conditional makes the header
+ * portable and it can simply be dropped verbatim into any system.
+ * The caveat is that on other systems some other header
+ * must provide NULL before the macros can be used.
+ */
+#ifdef __NetBSD__
+#include <sys/null.h>
+#endif
+
+#if defined(QUEUEDEBUG)
+# if defined(_KERNEL)
+# define QUEUEDEBUG_ABORT(...) panic(__VA_ARGS__)
+# else
+# include <err.h>
+# define QUEUEDEBUG_ABORT(...) err(1, __VA_ARGS__)
+# endif
+#endif
+
+/*
+ * Singly-linked List definitions.
+ */
+#define SLIST_HEAD(name, type) \
+struct name { \
+ struct type *slh_first; /* first element */ \
+}
+
+#define SLIST_HEAD_INITIALIZER(head) \
+ { NULL }
+
+#define SLIST_ENTRY(type) \
+struct { \
+ struct type *sle_next; /* next element */ \
+}
+
+/*
+ * Singly-linked List access methods.
+ */
+#define SLIST_FIRST(head) ((head)->slh_first)
+#define SLIST_END(head) NULL
+#define SLIST_EMPTY(head) ((head)->slh_first == NULL)
+#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
+
+#define SLIST_FOREACH(var, head, field) \
+ for((var) = (head)->slh_first; \
+ (var) != SLIST_END(head); \
+ (var) = (var)->field.sle_next)
+
+#define SLIST_FOREACH_SAFE(var, head, field, tvar) \
+ for ((var) = SLIST_FIRST((head)); \
+ (var) != SLIST_END(head) && \
+ ((tvar) = SLIST_NEXT((var), field), 1); \
+ (var) = (tvar))
+
+/*
+ * Singly-linked List functions.
+ */
+#define SLIST_INIT(head) do { \
+ (head)->slh_first = SLIST_END(head); \
+} while (/*CONSTCOND*/0)
+
+#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \
+ (elm)->field.sle_next = (slistelm)->field.sle_next; \
+ (slistelm)->field.sle_next = (elm); \
+} while (/*CONSTCOND*/0)
+
+#define SLIST_INSERT_HEAD(head, elm, field) do { \
+ (elm)->field.sle_next = (head)->slh_first; \
+ (head)->slh_first = (elm); \
+} while (/*CONSTCOND*/0)
+
+#define SLIST_REMOVE_AFTER(slistelm, field) do { \
+ (slistelm)->field.sle_next = \
+ SLIST_NEXT(SLIST_NEXT((slistelm), field), field); \
+} while (/*CONSTCOND*/0)
+
+#define SLIST_REMOVE_HEAD(head, field) do { \
+ (head)->slh_first = (head)->slh_first->field.sle_next; \
+} while (/*CONSTCOND*/0)
+
+#define SLIST_REMOVE(head, elm, type, field) do { \
+ if ((head)->slh_first == (elm)) { \
+ SLIST_REMOVE_HEAD((head), field); \
+ } \
+ else { \
+ struct type *curelm = (head)->slh_first; \
+ while(curelm->field.sle_next != (elm)) \
+ curelm = curelm->field.sle_next; \
+ curelm->field.sle_next = \
+ curelm->field.sle_next->field.sle_next; \
+ } \
+} while (/*CONSTCOND*/0)
+
+
+/*
+ * List definitions.
+ */
+#define LIST_HEAD(name, type) \
+struct name { \
+ struct type *lh_first; /* first element */ \
+}
+
+#define LIST_HEAD_INITIALIZER(head) \
+ { NULL }
+
+#define LIST_ENTRY(type) \
+struct { \
+ struct type *le_next; /* next element */ \
+ struct type **le_prev; /* address of previous next element */ \
+}
+
+/*
+ * List access methods.
+ */
+#define LIST_FIRST(head) ((head)->lh_first)
+#define LIST_END(head) NULL
+#define LIST_EMPTY(head) ((head)->lh_first == LIST_END(head))
+#define LIST_NEXT(elm, field) ((elm)->field.le_next)
+
+#define LIST_FOREACH(var, head, field) \
+ for ((var) = ((head)->lh_first); \
+ (var) != LIST_END(head); \
+ (var) = ((var)->field.le_next))
+
+#define LIST_FOREACH_SAFE(var, head, field, tvar) \
+ for ((var) = LIST_FIRST((head)); \
+ (var) != LIST_END(head) && \
+ ((tvar) = LIST_NEXT((var), field), 1); \
+ (var) = (tvar))
+
+#define LIST_MOVE(head1, head2) do { \
+ LIST_INIT((head2)); \
+ if (!LIST_EMPTY((head1))) { \
+ (head2)->lh_first = (head1)->lh_first; \
+ LIST_INIT((head1)); \
+ } \
+} while (/*CONSTCOND*/0)
+
+/*
+ * List functions.
+ */
+#if defined(QUEUEDEBUG)
+#define QUEUEDEBUG_LIST_INSERT_HEAD(head, elm, field) \
+ if ((head)->lh_first && \
+ (head)->lh_first->field.le_prev != &(head)->lh_first) \
+ QUEUEDEBUG_ABORT("LIST_INSERT_HEAD %p %s:%d", (head), \
+ __FILE__, __LINE__);
+#define QUEUEDEBUG_LIST_OP(elm, field) \
+ if ((elm)->field.le_next && \
+ (elm)->field.le_next->field.le_prev != \
+ &(elm)->field.le_next) \
+ QUEUEDEBUG_ABORT("LIST_* forw %p %s:%d", (elm), \
+ __FILE__, __LINE__); \
+ if (*(elm)->field.le_prev != (elm)) \
+ QUEUEDEBUG_ABORT("LIST_* back %p %s:%d", (elm), \
+ __FILE__, __LINE__);
+#define QUEUEDEBUG_LIST_POSTREMOVE(elm, field) \
+ (elm)->field.le_next = (void *)1L; \
+ (elm)->field.le_prev = (void *)1L;
+#else
+#define QUEUEDEBUG_LIST_INSERT_HEAD(head, elm, field)
+#define QUEUEDEBUG_LIST_OP(elm, field)
+#define QUEUEDEBUG_LIST_POSTREMOVE(elm, field)
+#endif
+
+#define LIST_INIT(head) do { \
+ (head)->lh_first = LIST_END(head); \
+} while (/*CONSTCOND*/0)
+
+#define LIST_INSERT_AFTER(listelm, elm, field) do { \
+ QUEUEDEBUG_LIST_OP((listelm), field) \
+ if (((elm)->field.le_next = (listelm)->field.le_next) != \
+ LIST_END(head)) \
+ (listelm)->field.le_next->field.le_prev = \
+ &(elm)->field.le_next; \
+ (listelm)->field.le_next = (elm); \
+ (elm)->field.le_prev = &(listelm)->field.le_next; \
+} while (/*CONSTCOND*/0)
+
+#define LIST_INSERT_BEFORE(listelm, elm, field) do { \
+ QUEUEDEBUG_LIST_OP((listelm), field) \
+ (elm)->field.le_prev = (listelm)->field.le_prev; \
+ (elm)->field.le_next = (listelm); \
+ *(listelm)->field.le_prev = (elm); \
+ (listelm)->field.le_prev = &(elm)->field.le_next; \
+} while (/*CONSTCOND*/0)
+
+#define LIST_INSERT_HEAD(head, elm, field) do { \
+ QUEUEDEBUG_LIST_INSERT_HEAD((head), (elm), field) \
+ if (((elm)->field.le_next = (head)->lh_first) != LIST_END(head))\
+ (head)->lh_first->field.le_prev = &(elm)->field.le_next;\
+ (head)->lh_first = (elm); \
+ (elm)->field.le_prev = &(head)->lh_first; \
+} while (/*CONSTCOND*/0)
+
+#define LIST_REMOVE(elm, field) do { \
+ QUEUEDEBUG_LIST_OP((elm), field) \
+ if ((elm)->field.le_next != NULL) \
+ (elm)->field.le_next->field.le_prev = \
+ (elm)->field.le_prev; \
+ *(elm)->field.le_prev = (elm)->field.le_next; \
+ QUEUEDEBUG_LIST_POSTREMOVE((elm), field) \
+} while (/*CONSTCOND*/0)
+
+#define LIST_REPLACE(elm, elm2, field) do { \
+ if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \
+ (elm2)->field.le_next->field.le_prev = \
+ &(elm2)->field.le_next; \
+ (elm2)->field.le_prev = (elm)->field.le_prev; \
+ *(elm2)->field.le_prev = (elm2); \
+ QUEUEDEBUG_LIST_POSTREMOVE((elm), field) \
+} while (/*CONSTCOND*/0)
+
+/*
+ * Simple queue definitions.
+ */
+#define SIMPLEQ_HEAD(name, type) \
+struct name { \
+ struct type *sqh_first; /* first element */ \
+ struct type **sqh_last; /* addr of last next element */ \
+}
+
+#define SIMPLEQ_HEAD_INITIALIZER(head) \
+ { NULL, &(head).sqh_first }
+
+#define SIMPLEQ_ENTRY(type) \
+struct { \
+ struct type *sqe_next; /* next element */ \
+}
+
+/*
+ * Simple queue access methods.
+ */
+#define SIMPLEQ_FIRST(head) ((head)->sqh_first)
+#define SIMPLEQ_END(head) NULL
+#define SIMPLEQ_EMPTY(head) ((head)->sqh_first == SIMPLEQ_END(head))
+#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next)
+
+#define SIMPLEQ_FOREACH(var, head, field) \
+ for ((var) = ((head)->sqh_first); \
+ (var) != SIMPLEQ_END(head); \
+ (var) = ((var)->field.sqe_next))
+
+#define SIMPLEQ_FOREACH_SAFE(var, head, field, next) \
+ for ((var) = ((head)->sqh_first); \
+ (var) != SIMPLEQ_END(head) && \
+ ((next = ((var)->field.sqe_next)), 1); \
+ (var) = (next))
+
+/*
+ * Simple queue functions.
+ */
+#define SIMPLEQ_INIT(head) do { \
+ (head)->sqh_first = NULL; \
+ (head)->sqh_last = &(head)->sqh_first; \
+} while (/*CONSTCOND*/0)
+
+#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \
+ if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \
+ (head)->sqh_last = &(elm)->field.sqe_next; \
+ (head)->sqh_first = (elm); \
+} while (/*CONSTCOND*/0)
+
+#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \
+ (elm)->field.sqe_next = NULL; \
+ *(head)->sqh_last = (elm); \
+ (head)->sqh_last = &(elm)->field.sqe_next; \
+} while (/*CONSTCOND*/0)
+
+#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\
+ (head)->sqh_last = &(elm)->field.sqe_next; \
+ (listelm)->field.sqe_next = (elm); \
+} while (/*CONSTCOND*/0)
+
+#define SIMPLEQ_REMOVE_HEAD(head, field) do { \
+ if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \
+ (head)->sqh_last = &(head)->sqh_first; \
+} while (/*CONSTCOND*/0)
+
+#define SIMPLEQ_REMOVE_AFTER(head, elm, field) do { \
+ if (((elm)->field.sqe_next = (elm)->field.sqe_next->field.sqe_next) \
+ == NULL) \
+ (head)->sqh_last = &(elm)->field.sqe_next; \
+} while (/*CONSTCOND*/0)
+
+#define SIMPLEQ_REMOVE(head, elm, type, field) do { \
+ if ((head)->sqh_first == (elm)) { \
+ SIMPLEQ_REMOVE_HEAD((head), field); \
+ } else { \
+ struct type *curelm = (head)->sqh_first; \
+ while (curelm->field.sqe_next != (elm)) \
+ curelm = curelm->field.sqe_next; \
+ if ((curelm->field.sqe_next = \
+ curelm->field.sqe_next->field.sqe_next) == NULL) \
+ (head)->sqh_last = &(curelm)->field.sqe_next; \
+ } \
+} while (/*CONSTCOND*/0)
+
+#define SIMPLEQ_CONCAT(head1, head2) do { \
+ if (!SIMPLEQ_EMPTY((head2))) { \
+ *(head1)->sqh_last = (head2)->sqh_first; \
+ (head1)->sqh_last = (head2)->sqh_last; \
+ SIMPLEQ_INIT((head2)); \
+ } \
+} while (/*CONSTCOND*/0)
+
+#define SIMPLEQ_LAST(head, type, field) \
+ (SIMPLEQ_EMPTY((head)) ? \
+ NULL : \
+ ((struct type *)(void *) \
+ ((char *)((head)->sqh_last) - offsetof(struct type, field))))
+
+/*
+ * Tail queue definitions.
+ */
+#define _TAILQ_HEAD(name, type, qual) \
+struct name { \
+ qual type *tqh_first; /* first element */ \
+ qual type *qual *tqh_last; /* addr of last next element */ \
+}
+#define TAILQ_HEAD(name, type) _TAILQ_HEAD(name, struct type,)
+
+#define TAILQ_HEAD_INITIALIZER(head) \
+ { TAILQ_END(head), &(head).tqh_first }
+
+#define _TAILQ_ENTRY(type, qual) \
+struct { \
+ qual type *tqe_next; /* next element */ \
+ qual type *qual *tqe_prev; /* address of previous next element */\
+}
+#define TAILQ_ENTRY(type) _TAILQ_ENTRY(struct type,)
+
+/*
+ * Tail queue access methods.
+ */
+#define TAILQ_FIRST(head) ((head)->tqh_first)
+#define TAILQ_END(head) (NULL)
+#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
+#define TAILQ_LAST(head, headname) \
+ (*(((struct headname *)((head)->tqh_last))->tqh_last))
+#define TAILQ_PREV(elm, headname, field) \
+ (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
+#define TAILQ_EMPTY(head) (TAILQ_FIRST(head) == TAILQ_END(head))
+
+
+#define TAILQ_FOREACH(var, head, field) \
+ for ((var) = ((head)->tqh_first); \
+ (var) != TAILQ_END(head); \
+ (var) = ((var)->field.tqe_next))
+
+#define TAILQ_FOREACH_SAFE(var, head, field, next) \
+ for ((var) = ((head)->tqh_first); \
+ (var) != TAILQ_END(head) && \
+ ((next) = TAILQ_NEXT(var, field), 1); (var) = (next))
+
+#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \
+ for ((var) = (*(((struct headname *)((head)->tqh_last))->tqh_last));\
+ (var) != TAILQ_END(head); \
+ (var) = (*(((struct headname *)((var)->field.tqe_prev))->tqh_last)))
+
+#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, prev) \
+ for ((var) = TAILQ_LAST((head), headname); \
+ (var) != TAILQ_END(head) && \
+ ((prev) = TAILQ_PREV((var), headname, field), 1); (var) = (prev))
+
+/*
+ * Tail queue functions.
+ */
+#if defined(QUEUEDEBUG)
+#define QUEUEDEBUG_TAILQ_INSERT_HEAD(head, elm, field) \
+ if ((head)->tqh_first && \
+ (head)->tqh_first->field.tqe_prev != &(head)->tqh_first) \
+ QUEUEDEBUG_ABORT("TAILQ_INSERT_HEAD %p %s:%d", (head), \
+ __FILE__, __LINE__);
+#define QUEUEDEBUG_TAILQ_INSERT_TAIL(head, elm, field) \
+ if (*(head)->tqh_last != NULL) \
+ QUEUEDEBUG_ABORT("TAILQ_INSERT_TAIL %p %s:%d", (head), \
+ __FILE__, __LINE__);
+#define QUEUEDEBUG_TAILQ_OP(elm, field) \
+ if ((elm)->field.tqe_next && \
+ (elm)->field.tqe_next->field.tqe_prev != \
+ &(elm)->field.tqe_next) \
+ QUEUEDEBUG_ABORT("TAILQ_* forw %p %s:%d", (elm), \
+ __FILE__, __LINE__); \
+ if (*(elm)->field.tqe_prev != (elm)) \
+ QUEUEDEBUG_ABORT("TAILQ_* back %p %s:%d", (elm), \
+ __FILE__, __LINE__);
+#define QUEUEDEBUG_TAILQ_PREREMOVE(head, elm, field) \
+ if ((elm)->field.tqe_next == NULL && \
+ (head)->tqh_last != &(elm)->field.tqe_next) \
+ QUEUEDEBUG_ABORT("TAILQ_PREREMOVE head %p elm %p %s:%d",\
+ (head), (elm), __FILE__, __LINE__);
+#define QUEUEDEBUG_TAILQ_POSTREMOVE(elm, field) \
+ (elm)->field.tqe_next = (void *)1L; \
+ (elm)->field.tqe_prev = (void *)1L;
+#else
+#define QUEUEDEBUG_TAILQ_INSERT_HEAD(head, elm, field)
+#define QUEUEDEBUG_TAILQ_INSERT_TAIL(head, elm, field)
+#define QUEUEDEBUG_TAILQ_OP(elm, field)
+#define QUEUEDEBUG_TAILQ_PREREMOVE(head, elm, field)
+#define QUEUEDEBUG_TAILQ_POSTREMOVE(elm, field)
+#endif
+
+#define TAILQ_INIT(head) do { \
+ (head)->tqh_first = TAILQ_END(head); \
+ (head)->tqh_last = &(head)->tqh_first; \
+} while (/*CONSTCOND*/0)
+
+#define TAILQ_INSERT_HEAD(head, elm, field) do { \
+ QUEUEDEBUG_TAILQ_INSERT_HEAD((head), (elm), field) \
+ if (((elm)->field.tqe_next = (head)->tqh_first) != TAILQ_END(head))\
+ (head)->tqh_first->field.tqe_prev = \
+ &(elm)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+ (head)->tqh_first = (elm); \
+ (elm)->field.tqe_prev = &(head)->tqh_first; \
+} while (/*CONSTCOND*/0)
+
+#define TAILQ_INSERT_TAIL(head, elm, field) do { \
+ QUEUEDEBUG_TAILQ_INSERT_TAIL((head), (elm), field) \
+ (elm)->field.tqe_next = TAILQ_END(head); \
+ (elm)->field.tqe_prev = (head)->tqh_last; \
+ *(head)->tqh_last = (elm); \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+} while (/*CONSTCOND*/0)
+
+#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ QUEUEDEBUG_TAILQ_OP((listelm), field) \
+ if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != \
+ TAILQ_END(head)) \
+ (elm)->field.tqe_next->field.tqe_prev = \
+ &(elm)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+ (listelm)->field.tqe_next = (elm); \
+ (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \
+} while (/*CONSTCOND*/0)
+
+#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
+ QUEUEDEBUG_TAILQ_OP((listelm), field) \
+ (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
+ (elm)->field.tqe_next = (listelm); \
+ *(listelm)->field.tqe_prev = (elm); \
+ (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \
+} while (/*CONSTCOND*/0)
+
+#define TAILQ_REMOVE(head, elm, field) do { \
+ QUEUEDEBUG_TAILQ_PREREMOVE((head), (elm), field) \
+ QUEUEDEBUG_TAILQ_OP((elm), field) \
+ if (((elm)->field.tqe_next) != TAILQ_END(head)) \
+ (elm)->field.tqe_next->field.tqe_prev = \
+ (elm)->field.tqe_prev; \
+ else \
+ (head)->tqh_last = (elm)->field.tqe_prev; \
+ *(elm)->field.tqe_prev = (elm)->field.tqe_next; \
+ QUEUEDEBUG_TAILQ_POSTREMOVE((elm), field); \
+} while (/*CONSTCOND*/0)
+
+#define TAILQ_REPLACE(head, elm, elm2, field) do { \
+ if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != \
+ TAILQ_END(head)) \
+ (elm2)->field.tqe_next->field.tqe_prev = \
+ &(elm2)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm2)->field.tqe_next; \
+ (elm2)->field.tqe_prev = (elm)->field.tqe_prev; \
+ *(elm2)->field.tqe_prev = (elm2); \
+ QUEUEDEBUG_TAILQ_POSTREMOVE((elm), field); \
+} while (/*CONSTCOND*/0)
+
+#define TAILQ_CONCAT(head1, head2, field) do { \
+ if (!TAILQ_EMPTY(head2)) { \
+ *(head1)->tqh_last = (head2)->tqh_first; \
+ (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \
+ (head1)->tqh_last = (head2)->tqh_last; \
+ TAILQ_INIT((head2)); \
+ } \
+} while (/*CONSTCOND*/0)
+
+/*
+ * Singly-linked Tail queue declarations.
+ */
+#define STAILQ_HEAD(name, type) \
+struct name { \
+ struct type *stqh_first; /* first element */ \
+ struct type **stqh_last; /* addr of last next element */ \
+}
+
+#define STAILQ_HEAD_INITIALIZER(head) \
+ { NULL, &(head).stqh_first }
+
+#define STAILQ_ENTRY(type) \
+struct { \
+ struct type *stqe_next; /* next element */ \
+}
+
+/*
+ * Singly-linked Tail queue access methods.
+ */
+#define STAILQ_FIRST(head) ((head)->stqh_first)
+#define STAILQ_END(head) NULL
+#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next)
+#define STAILQ_EMPTY(head) (STAILQ_FIRST(head) == STAILQ_END(head))
+
+/*
+ * Singly-linked Tail queue functions.
+ */
+#define STAILQ_INIT(head) do { \
+ (head)->stqh_first = NULL; \
+ (head)->stqh_last = &(head)->stqh_first; \
+} while (/*CONSTCOND*/0)
+
+#define STAILQ_INSERT_HEAD(head, elm, field) do { \
+ if (((elm)->field.stqe_next = (head)->stqh_first) == NULL) \
+ (head)->stqh_last = &(elm)->field.stqe_next; \
+ (head)->stqh_first = (elm); \
+} while (/*CONSTCOND*/0)
+
+#define STAILQ_INSERT_TAIL(head, elm, field) do { \
+ (elm)->field.stqe_next = NULL; \
+ *(head)->stqh_last = (elm); \
+ (head)->stqh_last = &(elm)->field.stqe_next; \
+} while (/*CONSTCOND*/0)
+
+#define STAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ if (((elm)->field.stqe_next = (listelm)->field.stqe_next) == NULL)\
+ (head)->stqh_last = &(elm)->field.stqe_next; \
+ (listelm)->field.stqe_next = (elm); \
+} while (/*CONSTCOND*/0)
+
+#define STAILQ_REMOVE_HEAD(head, field) do { \
+ if (((head)->stqh_first = (head)->stqh_first->field.stqe_next) == NULL) \
+ (head)->stqh_last = &(head)->stqh_first; \
+} while (/*CONSTCOND*/0)
+
+#define STAILQ_REMOVE(head, elm, type, field) do { \
+ if ((head)->stqh_first == (elm)) { \
+ STAILQ_REMOVE_HEAD((head), field); \
+ } else { \
+ struct type *curelm = (head)->stqh_first; \
+ while (curelm->field.stqe_next != (elm)) \
+ curelm = curelm->field.stqe_next; \
+ if ((curelm->field.stqe_next = \
+ curelm->field.stqe_next->field.stqe_next) == NULL) \
+ (head)->stqh_last = &(curelm)->field.stqe_next; \
+ } \
+} while (/*CONSTCOND*/0)
+
+#define STAILQ_FOREACH(var, head, field) \
+ for ((var) = ((head)->stqh_first); \
+ (var); \
+ (var) = ((var)->field.stqe_next))
+
+#define STAILQ_FOREACH_SAFE(var, head, field, tvar) \
+ for ((var) = STAILQ_FIRST((head)); \
+ (var) && ((tvar) = STAILQ_NEXT((var), field), 1); \
+ (var) = (tvar))
+
+#define STAILQ_CONCAT(head1, head2) do { \
+ if (!STAILQ_EMPTY((head2))) { \
+ *(head1)->stqh_last = (head2)->stqh_first; \
+ (head1)->stqh_last = (head2)->stqh_last; \
+ STAILQ_INIT((head2)); \
+ } \
+} while (/*CONSTCOND*/0)
+
+#define STAILQ_LAST(head, type, field) \
+ (STAILQ_EMPTY((head)) ? \
+ NULL : \
+ ((struct type *)(void *) \
+ ((char *)((head)->stqh_last) - offsetof(struct type, field))))
+
+
+#ifndef _KERNEL
+/*
+ * Circular queue definitions. Do not use. We still keep the macros
+ * for compatibility but because of pointer aliasing issues their use
+ * is discouraged!
+ */
+
+/*
+ * __launder_type(): We use this ugly hack to work around the the compiler
+ * noticing that two types may not alias each other and elide tests in code.
+ * We hit this in the CIRCLEQ macros when comparing 'struct name *' and
+ * 'struct type *' (see CIRCLEQ_HEAD()). Modern compilers (such as GCC
+ * 4.8) declare these comparisons as always false, causing the code to
+ * not run as designed.
+ *
+ * This hack is only to be used for comparisons and thus can be fully const.
+ * Do not use for assignment.
+ *
+ * If we ever choose to change the ABI of the CIRCLEQ macros, we could fix
+ * this by changing the head/tail sentinal values, but see the note above
+ * this one.
+ */
+static __inline const void * __launder_type(const void *);
+static __inline const void *
+__launder_type(const void *__x)
+{
+ __asm __volatile("" : "+r" (__x));
+ return __x;
+}
+
+#if defined(QUEUEDEBUG)
+#define QUEUEDEBUG_CIRCLEQ_HEAD(head, field) \
+ if ((head)->cqh_first != CIRCLEQ_ENDC(head) && \
+ (head)->cqh_first->field.cqe_prev != CIRCLEQ_ENDC(head)) \
+ QUEUEDEBUG_ABORT("CIRCLEQ head forw %p %s:%d", (head), \
+ __FILE__, __LINE__); \
+ if ((head)->cqh_last != CIRCLEQ_ENDC(head) && \
+ (head)->cqh_last->field.cqe_next != CIRCLEQ_ENDC(head)) \
+ QUEUEDEBUG_ABORT("CIRCLEQ head back %p %s:%d", (head), \
+ __FILE__, __LINE__);
+#define QUEUEDEBUG_CIRCLEQ_ELM(head, elm, field) \
+ if ((elm)->field.cqe_next == CIRCLEQ_ENDC(head)) { \
+ if ((head)->cqh_last != (elm)) \
+ QUEUEDEBUG_ABORT("CIRCLEQ elm last %p %s:%d", \
+ (elm), __FILE__, __LINE__); \
+ } else { \
+ if ((elm)->field.cqe_next->field.cqe_prev != (elm)) \
+ QUEUEDEBUG_ABORT("CIRCLEQ elm forw %p %s:%d", \
+ (elm), __FILE__, __LINE__); \
+ } \
+ if ((elm)->field.cqe_prev == CIRCLEQ_ENDC(head)) { \
+ if ((head)->cqh_first != (elm)) \
+ QUEUEDEBUG_ABORT("CIRCLEQ elm first %p %s:%d", \
+ (elm), __FILE__, __LINE__); \
+ } else { \
+ if ((elm)->field.cqe_prev->field.cqe_next != (elm)) \
+ QUEUEDEBUG_ABORT("CIRCLEQ elm prev %p %s:%d", \
+ (elm), __FILE__, __LINE__); \
+ }
+#define QUEUEDEBUG_CIRCLEQ_POSTREMOVE(elm, field) \
+ (elm)->field.cqe_next = (void *)1L; \
+ (elm)->field.cqe_prev = (void *)1L;
+#else
+#define QUEUEDEBUG_CIRCLEQ_HEAD(head, field)
+#define QUEUEDEBUG_CIRCLEQ_ELM(head, elm, field)
+#define QUEUEDEBUG_CIRCLEQ_POSTREMOVE(elm, field)
+#endif
+
+#define CIRCLEQ_HEAD(name, type) \
+struct name { \
+ struct type *cqh_first; /* first element */ \
+ struct type *cqh_last; /* last element */ \
+}
+
+#define CIRCLEQ_HEAD_INITIALIZER(head) \
+ { CIRCLEQ_END(&head), CIRCLEQ_END(&head) }
+
+#define CIRCLEQ_ENTRY(type) \
+struct { \
+ struct type *cqe_next; /* next element */ \
+ struct type *cqe_prev; /* previous element */ \
+}
+
+/*
+ * Circular queue functions.
+ */
+#define CIRCLEQ_INIT(head) do { \
+ (head)->cqh_first = CIRCLEQ_END(head); \
+ (head)->cqh_last = CIRCLEQ_END(head); \
+} while (/*CONSTCOND*/0)
+
+#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ QUEUEDEBUG_CIRCLEQ_HEAD((head), field) \
+ QUEUEDEBUG_CIRCLEQ_ELM((head), (listelm), field) \
+ (elm)->field.cqe_next = (listelm)->field.cqe_next; \
+ (elm)->field.cqe_prev = (listelm); \
+ if ((listelm)->field.cqe_next == CIRCLEQ_ENDC(head)) \
+ (head)->cqh_last = (elm); \
+ else \
+ (listelm)->field.cqe_next->field.cqe_prev = (elm); \
+ (listelm)->field.cqe_next = (elm); \
+} while (/*CONSTCOND*/0)
+
+#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \
+ QUEUEDEBUG_CIRCLEQ_HEAD((head), field) \
+ QUEUEDEBUG_CIRCLEQ_ELM((head), (listelm), field) \
+ (elm)->field.cqe_next = (listelm); \
+ (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \
+ if ((listelm)->field.cqe_prev == CIRCLEQ_ENDC(head)) \
+ (head)->cqh_first = (elm); \
+ else \
+ (listelm)->field.cqe_prev->field.cqe_next = (elm); \
+ (listelm)->field.cqe_prev = (elm); \
+} while (/*CONSTCOND*/0)
+
+#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \
+ QUEUEDEBUG_CIRCLEQ_HEAD((head), field) \
+ (elm)->field.cqe_next = (head)->cqh_first; \
+ (elm)->field.cqe_prev = CIRCLEQ_END(head); \
+ if ((head)->cqh_last == CIRCLEQ_ENDC(head)) \
+ (head)->cqh_last = (elm); \
+ else \
+ (head)->cqh_first->field.cqe_prev = (elm); \
+ (head)->cqh_first = (elm); \
+} while (/*CONSTCOND*/0)
+
+#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \
+ QUEUEDEBUG_CIRCLEQ_HEAD((head), field) \
+ (elm)->field.cqe_next = CIRCLEQ_END(head); \
+ (elm)->field.cqe_prev = (head)->cqh_last; \
+ if ((head)->cqh_first == CIRCLEQ_ENDC(head)) \
+ (head)->cqh_first = (elm); \
+ else \
+ (head)->cqh_last->field.cqe_next = (elm); \
+ (head)->cqh_last = (elm); \
+} while (/*CONSTCOND*/0)
+
+#define CIRCLEQ_REMOVE(head, elm, field) do { \
+ QUEUEDEBUG_CIRCLEQ_HEAD((head), field) \
+ QUEUEDEBUG_CIRCLEQ_ELM((head), (elm), field) \
+ if ((elm)->field.cqe_next == CIRCLEQ_ENDC(head)) \
+ (head)->cqh_last = (elm)->field.cqe_prev; \
+ else \
+ (elm)->field.cqe_next->field.cqe_prev = \
+ (elm)->field.cqe_prev; \
+ if ((elm)->field.cqe_prev == CIRCLEQ_ENDC(head)) \
+ (head)->cqh_first = (elm)->field.cqe_next; \
+ else \
+ (elm)->field.cqe_prev->field.cqe_next = \
+ (elm)->field.cqe_next; \
+ QUEUEDEBUG_CIRCLEQ_POSTREMOVE((elm), field) \
+} while (/*CONSTCOND*/0)
+
+#define CIRCLEQ_FOREACH(var, head, field) \
+ for ((var) = ((head)->cqh_first); \
+ (var) != CIRCLEQ_ENDC(head); \
+ (var) = ((var)->field.cqe_next))
+
+#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \
+ for ((var) = ((head)->cqh_last); \
+ (var) != CIRCLEQ_ENDC(head); \
+ (var) = ((var)->field.cqe_prev))
+
+/*
+ * Circular queue access methods.
+ */
+#define CIRCLEQ_FIRST(head) ((head)->cqh_first)
+#define CIRCLEQ_LAST(head) ((head)->cqh_last)
+/* For comparisons */
+#define CIRCLEQ_ENDC(head) (__launder_type(head))
+/* For assignments */
+#define CIRCLEQ_END(head) ((void *)(head))
+#define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next)
+#define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev)
+#define CIRCLEQ_EMPTY(head) \
+ (CIRCLEQ_FIRST(head) == CIRCLEQ_ENDC(head))
+
+#define CIRCLEQ_LOOP_NEXT(head, elm, field) \
+ (((elm)->field.cqe_next == CIRCLEQ_ENDC(head)) \
+ ? ((head)->cqh_first) \
+ : (elm->field.cqe_next))
+#define CIRCLEQ_LOOP_PREV(head, elm, field) \
+ (((elm)->field.cqe_prev == CIRCLEQ_ENDC(head)) \
+ ? ((head)->cqh_last) \
+ : (elm->field.cqe_prev))
+#endif /* !_KERNEL */
+
+#endif /* !_SYS_QUEUE_H_ */
diff --git a/src/shared/schedules.c b/src/shared/schedules.c
new file mode 100644
index 00000000..b1eb85ad
--- /dev/null
+++ b/src/shared/schedules.c
@@ -0,0 +1,433 @@
+/*
+ * The functions in this file control the stopping of daemons by
+ * start-stop-daemon and supervise-daemon.
+ */
+
+/*
+ * Copyright (c) 2015 The OpenRC Authors.
+ * See the Authors file at the top-level directory of this distribution and
+ * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS
+ *
+ * This file is part of OpenRC. It is subject to the license terms in
+ * the LICENSE file found in the top-level directory of this
+ * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE
+ * This file may not be copied, modified, propagated, or distributed
+ * except according to the terms contained in the LICENSE file.
+ */
+
+/* nano seconds */
+#define POLL_INTERVAL 20000000
+#define WAIT_PIDFILE 500000000
+#define ONE_SECOND 1000000000
+#define ONE_MS 1000000
+
+#include <ctype.h>
+#include <errno.h>
+#include <signal.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include "einfo.h"
+#include "queue.h"
+#include "rc.h"
+#include "misc.h"
+#include "schedules.h"
+#include "helpers.h"
+
+typedef struct scheduleitem {
+ enum {
+ SC_TIMEOUT,
+ SC_SIGNAL,
+ SC_GOTO,
+ SC_FOREVER,
+ } type;
+ int value;
+ struct scheduleitem *gotoitem;
+ TAILQ_ENTRY(scheduleitem) entries;
+} SCHEDULEITEM;
+
+static TAILQ_HEAD(, scheduleitem) schedule;
+
+void free_schedulelist(void)
+{
+ SCHEDULEITEM *s1 = TAILQ_FIRST(&schedule);
+ SCHEDULEITEM *s2;
+
+ while (s1) {
+ s2 = TAILQ_NEXT(s1, entries);
+ free(s1);
+ s1 = s2;
+ }
+ TAILQ_INIT(&schedule);
+}
+
+int parse_signal(const char *applet, const char *sig)
+{
+ typedef struct signalpair
+ {
+ const char *name;
+ int signal;
+ } SIGNALPAIR;
+
+#define signalpair_item(name) { #name, SIG##name },
+
+ static const SIGNALPAIR signallist[] = {
+ signalpair_item(HUP)
+ signalpair_item(INT)
+ signalpair_item(QUIT)
+ signalpair_item(ILL)
+ signalpair_item(TRAP)
+ signalpair_item(ABRT)
+ signalpair_item(BUS)
+ signalpair_item(FPE)
+ signalpair_item(KILL)
+ signalpair_item(USR1)
+ signalpair_item(SEGV)
+ signalpair_item(USR2)
+ signalpair_item(PIPE)
+ signalpair_item(ALRM)
+ signalpair_item(TERM)
+ signalpair_item(CHLD)
+ signalpair_item(CONT)
+ signalpair_item(STOP)
+ signalpair_item(TSTP)
+ signalpair_item(TTIN)
+ signalpair_item(TTOU)
+ signalpair_item(URG)
+ signalpair_item(XCPU)
+ signalpair_item(XFSZ)
+ signalpair_item(VTALRM)
+ signalpair_item(PROF)
+#ifdef SIGWINCH
+ signalpair_item(WINCH)
+#endif
+#ifdef SIGIO
+ signalpair_item(IO)
+#endif
+#ifdef SIGPWR
+ signalpair_item(PWR)
+#endif
+ signalpair_item(SYS)
+ { "NULL", 0 },
+ };
+
+ unsigned int i = 0;
+ const char *s;
+
+ if (!sig || *sig == '\0')
+ return -1;
+
+ if (sscanf(sig, "%u", &i) == 1) {
+ if (i < NSIG)
+ return i;
+ eerrorx("%s: `%s' is not a valid signal", applet, sig);
+ }
+
+ if (strncmp(sig, "SIG", 3) == 0)
+ s = sig + 3;
+ else
+ s = NULL;
+
+ for (i = 0; i < ARRAY_SIZE(signallist); ++i)
+ if (strcmp(sig, signallist[i].name) == 0 ||
+ (s && strcmp(s, signallist[i].name) == 0))
+ return signallist[i].signal;
+
+ eerrorx("%s: `%s' is not a valid signal", applet, sig);
+ /* NOTREACHED */
+}
+
+static SCHEDULEITEM *parse_schedule_item(const char *applet, const char *string)
+{
+ const char *after_hyph;
+ int sig;
+ SCHEDULEITEM *item = xmalloc(sizeof(*item));
+
+ item->value = 0;
+ item->gotoitem = NULL;
+ if (strcmp(string,"forever") == 0)
+ item->type = SC_FOREVER;
+ else if (isdigit((unsigned char)string[0])) {
+ item->type = SC_TIMEOUT;
+ errno = 0;
+ if (sscanf(string, "%d", &item->value) != 1)
+ eerrorx("%s: invalid timeout value in schedule `%s'",
+ applet, string);
+ } else if ((after_hyph = string + (string[0] == '-')) &&
+ ((sig = parse_signal(applet, after_hyph)) != -1))
+ {
+ item->type = SC_SIGNAL;
+ item->value = (int)sig;
+ } else
+ eerrorx("%s: invalid schedule item `%s'", applet, string);
+
+ return item;
+}
+
+void parse_schedule(const char *applet, const char *string, int timeout)
+{
+ char buffer[20];
+ const char *slash;
+ int count = 0;
+ SCHEDULEITEM *repeatat = NULL;
+ size_t len;
+ SCHEDULEITEM *item;
+
+ TAILQ_INIT(&schedule);
+ if (string)
+ for (slash = string; *slash; slash++)
+ if (*slash == '/')
+ count++;
+
+ free_schedulelist();
+
+ if (count == 0) {
+ item = xmalloc(sizeof(*item));
+ item->type = SC_SIGNAL;
+ item->value = timeout;
+ item->gotoitem = NULL;
+ TAILQ_INSERT_TAIL(&schedule, item, entries);
+
+ item = xmalloc(sizeof(*item));
+ item->type = SC_TIMEOUT;
+ item->gotoitem = NULL;
+ TAILQ_INSERT_TAIL(&schedule, item, entries);
+ if (string) {
+ if (sscanf(string, "%d", &item->value) != 1)
+ eerrorx("%s: invalid timeout in schedule",
+ applet);
+ } else
+ item->value = 5;
+
+ return;
+ }
+
+ while (string != NULL) {
+ if ((slash = strchr(string, '/')))
+ len = slash - string;
+ else
+ len = strlen(string);
+
+ if (len >= (ptrdiff_t)sizeof(buffer))
+ eerrorx("%s: invalid schedule item, far too long",
+ applet);
+
+ memcpy(buffer, string, len);
+ buffer[len] = 0;
+ string = slash ? slash + 1 : NULL;
+
+ item = parse_schedule_item(applet, buffer);
+ TAILQ_INSERT_TAIL(&schedule, item, entries);
+ if (item->type == SC_FOREVER) {
+ if (repeatat)
+ eerrorx("%s: invalid schedule, `forever' "
+ "appears more than once", applet);
+
+ repeatat = item;
+ continue;
+ }
+ }
+
+ if (repeatat) {
+ item = xmalloc(sizeof(*item));
+ item->type = SC_GOTO;
+ item->value = 0;
+ item->gotoitem = repeatat;
+ TAILQ_INSERT_TAIL(&schedule, item, entries);
+ }
+
+ return;
+}
+
+/* return number of processes killed, -1 on error */
+int do_stop(const char *applet, const char *exec, const char *const *argv,
+ pid_t pid, uid_t uid,int sig, bool test, bool quiet)
+{
+ RC_PIDLIST *pids;
+ RC_PID *pi;
+ RC_PID *np;
+ bool killed;
+ int nkilled = 0;
+
+ if (pid > 0)
+ pids = rc_find_pids(NULL, NULL, 0, pid);
+ else
+ pids = rc_find_pids(exec, argv, uid, 0);
+
+ if (!pids)
+ return 0;
+
+ LIST_FOREACH_SAFE(pi, pids, entries, np) {
+ if (test) {
+ einfo("Would send signal %d to PID %d", sig, pi->pid);
+ nkilled++;
+ } else {
+ if (sig) {
+ syslog(LOG_DEBUG, "Sending signal %d to PID %d", sig, pi->pid);
+ if (!quiet)
+ ebeginv("Sending signal %d to PID %d", sig, pi->pid);
+ }
+ errno = 0;
+ killed = (kill(pi->pid, sig) == 0 ||
+ errno == ESRCH ? true : false);
+ if (!quiet)
+ eendv(killed ? 0 : 1,
+ "%s: failed to send signal %d to PID %d: %s",
+ applet, sig, pi->pid, strerror(errno));
+ else if (!killed)
+ syslog(LOG_ERR, "Failed to send signal %d to PID %d: %s",
+ sig, pi->pid, strerror(errno));
+ if (!killed) {
+ nkilled = -1;
+ } else {
+ if (nkilled != -1)
+ nkilled++;
+ }
+ }
+ free(pi);
+ }
+
+ free(pids);
+ return nkilled;
+}
+
+int run_stop_schedule(const char *applet,
+ const char *exec, const char *const *argv,
+ pid_t pid, uid_t uid,
+ bool test, bool progress, bool quiet)
+{
+ SCHEDULEITEM *item = TAILQ_FIRST(&schedule);
+ int nkilled = 0;
+ int tkilled = 0;
+ int nrunning = 0;
+ long nloops, nsecs;
+ struct timespec ts;
+ const char *const *p;
+ bool progressed = false;
+
+ if (!(pid > 0 || exec || uid || (argv && *argv)))
+ return 0;
+
+ if (exec) {
+ einfov("Will stop %s", exec);
+ syslog(LOG_DEBUG, "Will stop %s", exec);
+ }
+ if (pid > 0) {
+ einfov("Will stop PID %d", pid);
+ syslog(LOG_DEBUG, "Will stop PID %d", pid);
+ }
+ if (uid) {
+ einfov("Will stop processes owned by UID %d", uid);
+ syslog(LOG_DEBUG, "Will stop processes owned by UID %d", uid);
+ }
+ if (argv && *argv) {
+ einfovn("Will stop processes of `");
+ if (rc_yesno(getenv("EINFO_VERBOSE"))) {
+ for (p = argv; p && *p; p++) {
+ if (p != argv)
+ printf(" ");
+ printf("%s", *p);
+ }
+ printf("'\n");
+ }
+ }
+
+ while (item) {
+ switch (item->type) {
+ case SC_GOTO:
+ item = item->gotoitem;
+ continue;
+
+ case SC_SIGNAL:
+ nrunning = 0;
+ nkilled = do_stop(applet, exec, argv, pid, uid, item->value, test,
+ quiet);
+ if (nkilled == 0) {
+ if (tkilled == 0) {
+ if (progressed)
+ printf("\n");
+ eerror("%s: no matching processes found", applet);
+ }
+ return tkilled;
+ }
+ else if (nkilled == -1)
+ return 0;
+
+ tkilled += nkilled;
+ break;
+ case SC_FOREVER:
+ case SC_TIMEOUT:
+ if (item->type == SC_TIMEOUT && item->value < 1) {
+ item = NULL;
+ break;
+ }
+
+ ts.tv_sec = 0;
+ ts.tv_nsec = POLL_INTERVAL;
+
+ for (nsecs = 0; item->type == SC_FOREVER || nsecs < item->value; nsecs++) {
+ for (nloops = 0;
+ nloops < ONE_SECOND / POLL_INTERVAL;
+ nloops++)
+ {
+ if ((nrunning = do_stop(applet, exec, argv,
+ pid, uid, 0, test, quiet)) == 0)
+ return 0;
+
+
+ if (nanosleep(&ts, NULL) == -1) {
+ if (progressed) {
+ printf("\n");
+ progressed = false;
+ }
+ if (errno != EINTR) {
+ eerror("%s: nanosleep: %s",
+ applet, strerror(errno));
+ return 0;
+ }
+ }
+ }
+ if (progress) {
+ printf(".");
+ fflush(stdout);
+ progressed = true;
+ }
+ }
+ break;
+ default:
+ if (progressed) {
+ printf("\n");
+ progressed = false;
+ }
+ eerror("%s: invalid schedule item `%d'",
+ applet, item->type);
+ return 0;
+ }
+
+ if (item)
+ item = TAILQ_NEXT(item, entries);
+ }
+
+ if (test || (tkilled > 0 && nrunning == 0))
+ return nkilled;
+
+ if (progressed)
+ printf("\n");
+ if (!quiet) {
+ if (nrunning == 1)
+ eerror("%s: %d process refused to stop", applet, nrunning);
+ else
+ eerror("%s: %d process(es) refused to stop", applet, nrunning);
+ }
+
+ return -nrunning;
+}
diff --git a/src/shared/schedules.h b/src/shared/schedules.h
new file mode 100644
index 00000000..5fb9e526
--- /dev/null
+++ b/src/shared/schedules.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2017 The OpenRC Authors.
+ * See the Authors file at the top-level directory of this distribution and
+ * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS
+ *
+ * This file is part of OpenRC. It is subject to the license terms in
+ * the LICENSE file found in the top-level directory of this
+ * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE
+ * This file may not be copied, modified, propagated, or distributed
+ * except according to the terms contained in the LICENSE file.
+ */
+
+#ifndef __RC_SCHEDULES_H
+#define __RC_SCHEDULES_H
+
+void free_schedulelist(void);
+int parse_signal(const char *applet, const char *sig);
+void parse_schedule(const char *applet, const char *string, int timeout);
+int do_stop(const char *applet, const char *exec, const char *const *argv,
+ pid_t pid, uid_t uid,int sig, bool test, bool quiet);
+int run_stop_schedule(const char *applet,
+ const char *exec, const char *const *argv,
+ pid_t pid, uid_t uid,
+ bool test, bool progress, bool quiet);
+
+#endif
diff --git a/src/shared/selinux.c b/src/shared/selinux.c
new file mode 100644
index 00000000..213a00f1
--- /dev/null
+++ b/src/shared/selinux.c
@@ -0,0 +1,417 @@
+/*
+ * selinux.c
+ * SELinux helpers to get and set contexts.
+ */
+
+/*
+ * Copyright (c) 2014-2015 The OpenRC Authors.
+ * See the Authors file at the top-level directory of this distribution and
+ * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS
+ *
+ * This file is part of OpenRC. It is subject to the license terms in
+ * the LICENSE file found in the top-level directory of this
+ * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE
+ * This file may not be copied, modified, propagated, or distributed
+ * except according to the terms contained in the LICENSE file.
+ */
+
+#include <stddef.h>
+#include <errno.h>
+#include <dlfcn.h>
+#include <ctype.h>
+#include <limits.h>
+#include <pwd.h>
+#include <unistd.h>
+
+#include <selinux/selinux.h>
+#include <selinux/label.h>
+#include <selinux/get_default_type.h>
+#include <selinux/context.h>
+
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "einfo.h"
+#include "queue.h"
+#include "rc.h"
+#include "misc.h"
+#include "plugin.h"
+#include "selinux.h"
+
+/* the context files for selinux */
+#define INITRC_FILE "initrc_context"
+
+#ifdef HAVE_AUDIT
+#include <libaudit.h>
+#endif
+
+/* PAM or shadow for authentication */
+#ifdef HAVE_PAM
+# define PAM_SERVICE_NAME "run_init" /* the name of this program for PAM */
+# include <security/pam_appl.h>
+# include <security/pam_misc.h>
+#else
+# define PASSWORD_PROMPT "Password:"
+# include <crypt.h>
+# include <shadow.h>
+# include <string.h>
+#endif
+
+
+/* The handle for the fcontext lookups */
+static struct selabel_handle *hnd = NULL;
+
+int selinux_util_label(const char *path)
+{
+ int retval = 0;
+ int enforce;
+ struct stat st;
+ char *con;
+
+ enforce = security_getenforce();
+ if (retval < 0)
+ return retval;
+
+ if (!hnd)
+ return (enforce) ? -1 : 0;
+
+ retval = lstat(path, &st);
+ if (retval < 0) {
+ if (errno == ENOENT)
+ return 0;
+ return (enforce) ? -1 : 0;
+ }
+
+ /* lookup the context */
+ retval = selabel_lookup_raw(hnd, &con, path, st.st_mode);
+ if (retval < 0) {
+ if (errno == ENOENT)
+ return 0;
+ return (enforce) ? -1 : 0;
+ }
+
+ /* apply the context */
+ retval = lsetfilecon(path, con);
+ freecon(con);
+ if (retval < 0) {
+ if (errno == ENOENT)
+ return 0;
+ if (errno == ENOTSUP)
+ return 0;
+ return (enforce) ? -1 : 0;
+ }
+
+ return 0;
+}
+
+/*
+ * Open the label handle
+ * returns 1 on success, 0 if no selinux, negative on error
+ */
+int selinux_util_open(void)
+{
+ int retval = 0;
+
+ retval = is_selinux_enabled();
+ if (retval <= 0)
+ return retval;
+
+ hnd = selabel_open(SELABEL_CTX_FILE, NULL, 0);
+ if (!hnd)
+ return -2;
+
+ return 1;
+}
+
+/*
+ * Close the label handle
+ * returns 1 on success, 0 if no selinux, negative on error
+ */
+int selinux_util_close(void)
+{
+ int retval = 0;
+
+ retval = is_selinux_enabled();
+ if (retval <= 0)
+ return retval;
+
+ if (hnd) {
+ selabel_close(hnd);
+ hnd = NULL;
+ }
+
+ return 0;
+}
+
+/*
+ * This will check the users password and return 0 on success or -1 on fail
+ *
+ * We ask for the password to make sure it is intended vs run by malicious software.
+ * Actual authorization is covered by the policy itself.
+ */
+static int check_password(char *username)
+{
+ int ret = 1;
+#ifdef HAVE_PAM
+ pam_handle_t *pamh;
+ int pam_err = 0;
+ const struct pam_conv pconv = {
+ misc_conv,
+ NULL
+ };
+
+ pam_err = pam_start(PAM_SERVICE_NAME, username, &pconv, &pamh);
+ if (pam_err != PAM_SUCCESS) {
+ ret = -1;
+ goto outpam;
+ }
+
+ pam_err = pam_authenticate(pamh, PAM_DISALLOW_NULL_AUTHTOK);
+ if (pam_err != PAM_SUCCESS) {
+ ret = -1;
+ goto outpam;
+ }
+
+ ret = 0;
+outpam:
+ pam_end(pamh, pam_err);
+ pamh = NULL;
+
+#else /* authenticating via /etc/shadow instead */
+ struct spwd *spw;
+ char *password;
+ char *attempt;
+
+ spw = getspnam(username);
+ if (!spw) {
+ eerror("Failed to read shadow entry");
+ ret = -1;
+ goto outshadow;
+ }
+
+ attempt = getpass(PASSWORD_PROMPT);
+ if (!attempt) {
+ ret = -1;
+ goto outshadow;
+ }
+
+ if (*spw->sp_pwdp == '\0' && *attempt == '\0') {
+ ret = -1;
+ goto outshadow;
+ }
+
+ /* salt must be at least two characters long */
+ if (!(spw->sp_pwdp[0] && spw->sp_pwdp[1])) {
+ ret = -1;
+ goto outshadow;
+ }
+
+ /* encrypt the password attempt */
+ password = crypt(attempt, spw->sp_pwdp);
+
+ if (password && strcmp(password, spw->sp_pwdp) == 0)
+ ret = 0;
+ else
+ ret = -1;
+outshadow:
+#endif
+ return ret;
+}
+
+/* Authenticates the user, returns 0 on success, 1 on fail */
+static int check_auth()
+{
+ struct passwd *pw;
+ uid_t uid;
+
+#ifdef HAVE_AUDIT
+ uid = audit_getloginuid();
+ if (uid == (uid_t) -1)
+ uid = getuid();
+#else
+ uid = getuid();
+#endif
+
+ pw = getpwuid(uid);
+ if (!pw) {
+ eerror("cannot find your entry in the passwd file.");
+ return (-1);
+ }
+
+ printf("Authenticating %s.\n", pw->pw_name);
+
+ /* do the actual check */
+ if (check_password(pw->pw_name) == 0) {
+ return 0;
+ }
+
+ eerrorx("Authentication failed for %s", pw->pw_name);
+ return 1;
+}
+
+/*
+ * Read the context from the given context file. context must be free'd by the user.
+ */
+static int read_context_file(const char *filename, char **context)
+{
+ int ret = -1;
+ FILE *fp;
+ char *filepath = NULL;
+ char *line = NULL;
+ char *p;
+ char *p2;
+ size_t len = 0;
+ ssize_t read;
+
+ xasprintf(&filepath, "%s/%s", selinux_contexts_path(), filename);
+
+ fp = fopen(filepath, "r");
+ if (fp == NULL) {
+ eerror("Failed to open context file: %s", filename);
+ free(filepath);
+ return -1;
+ }
+
+ while ((read = getline(&line, &len, fp)) != -1) {
+ /* cut off spaces before the string */
+ p = line;
+ while (isspace(*p) && *p != '\0')
+ p++;
+
+ /* empty string, skip */
+ if (*p == '\0')
+ continue;
+
+ /* cut off spaces after the string */
+ p2 = p;
+ while (!isspace(*p2) && *p2 != '\0')
+ p2++;
+ *p2 = '\0';
+
+ *context = xstrdup(p);
+ ret = 0;
+ break;
+ }
+
+ free(line);
+ free(filepath);
+ fclose(fp);
+ return ret;
+}
+
+static int read_run_init_context(char **context)
+{
+ int ret = -1;
+ RC_STRINGLIST *list;
+ char *value = NULL;
+
+ list = rc_config_list(selinux_openrc_contexts_path());
+ if (list == NULL)
+ return ret;
+
+ value = rc_config_value(list, "run_init");
+ if (value != NULL && strlen(value) > 0) {
+ *context = xstrdup(value);
+ ret = 0;
+ }
+
+ rc_stringlist_free(list);
+ return ret;
+}
+
+void selinux_setup(char **argv)
+{
+ char *new_context = NULL;
+ char *curr_context = NULL;
+ context_t curr_con;
+ char *curr_t = NULL;
+ char *run_init_t = NULL;
+
+ /* Return, if selinux is disabled. */
+ if (is_selinux_enabled() < 1) {
+ return;
+ }
+
+ if (read_run_init_context(&run_init_t) != 0) {
+ /* assume a reasonable default, rather than bailing out */
+ run_init_t = xstrdup("run_init_t");
+ ewarn("Assuming SELinux run_init type is %s", run_init_t);
+ }
+
+ /* Get our current context. */
+ if (getcon(&curr_context) < 0) {
+ if (errno == ENOENT) {
+ /* should only hit this if proc is not mounted. this
+ * happens on Gentoo right after init starts, when
+ * the init script processing starts.
+ */
+ goto out;
+ } else {
+ perror("getcon");
+ exit(1);
+ }
+ }
+
+ /* extract the type from the context */
+ curr_con = context_new(curr_context);
+ if (!curr_con) {
+ free(curr_context);
+ goto out;
+ }
+
+ curr_t = xstrdup(context_type_get(curr_con));
+ if (!curr_t) {
+ context_free(curr_con);
+ free(curr_context);
+ goto out;
+ }
+
+ /* dont need them anymore so free() now */
+ context_free(curr_con);
+ free(curr_context);
+
+ /* if we are not in the run_init domain, we should not do anything */
+ if (strncmp(run_init_t, curr_t, strlen(run_init_t)) != 0) {
+ goto out;
+ }
+
+ free(curr_t);
+ free(run_init_t);
+
+ if (check_auth() != 0) {
+ eerrorx("Authentication failed.");
+ }
+
+ /* Get the context for the script to be run in. */
+ if (read_context_file(INITRC_FILE, &new_context) != 0) {
+ /* assume a reasonable default, rather than bailing out */
+ new_context = xstrdup("system_u:system_r:initrc_t");
+ ewarn("Assuming SELinux initrc context is %s", new_context);
+ }
+
+ /* Set the new context */
+ if (setexeccon(new_context) < 0) {
+ eerrorx("Could not set SELinux exec context to %s.", new_context);
+ }
+
+ free(new_context);
+
+ /*
+ * exec will recycle ptys so try and use open_init_pty if it exists
+ * which will open the pty with initrc_devpts_t, if it doesnt exist,
+ * fall back to plain exec
+ */
+ if (!access("/usr/sbin/open_init_pty", X_OK)) {
+ if (execvp("/usr/sbin/open_init_pty", argv)) {
+ perror("execvp");
+ exit(-1);
+ }
+ } else if (execvp(argv[1], argv + 1)) {
+ perror("execvp");
+ exit(-1);
+ }
+
+out:
+ free(run_init_t);
+ free(curr_t);
+}
diff --git a/src/shared/selinux.h b/src/shared/selinux.h
new file mode 100644
index 00000000..627c87bc
--- /dev/null
+++ b/src/shared/selinux.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2014-2015 The OpenRC Authors.
+ * See the Authors file at the top-level directory of this distribution and
+ * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS
+ *
+ * This file is part of OpenRC. It is subject to the license terms in
+ * the LICENSE file found in the top-level directory of this
+ * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE
+ * This file may not be copied, modified, propagated, or distributed
+ * except according to the terms contained in the LICENSE file.
+ */
+
+#ifndef RC_SELINUX_UTIL_H
+#define RC_SELINUX_UTIL_H
+
+#ifdef HAVE_SELINUX
+
+int selinux_util_open(void);
+int selinux_util_label(const char *path);
+int selinux_util_close(void);
+
+void selinux_setup(char **argv);
+
+#else
+
+/* always return false for selinux_util_open() */
+#define selinux_util_open() (0)
+#define selinux_util_label(x) do { } while (0)
+#define selinux_util_close() do { } while (0)
+
+#define selinux_setup(x) do { } while (0)
+
+#endif
+
+
+#endif
diff --git a/src/shared/version.h.in b/src/shared/version.h.in
new file mode 100644
index 00000000..4f228f55
--- /dev/null
+++ b/src/shared/version.h.in
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2007-2015 The OpenRC Authors.
+ * See the Authors file at the top-level directory of this distribution and
+ * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS
+ *
+ * This file is part of OpenRC. It is subject to the license terms in
+ * the LICENSE file found in the top-level directory of this
+ * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE
+ * This file may not be copied, modified, propagated, or distributed
+ * except according to the terms contained in the LICENSE file.
+ */
+
+#ifndef _VERSION_H_
+#define _VERSION_H_
+
+#define VERSION "@VCS_TAG@"
+
+#endif
diff --git a/src/shared/version.in b/src/shared/version.in
new file mode 100644
index 00000000..42179405
--- /dev/null
+++ b/src/shared/version.in
@@ -0,0 +1 @@
+@VCS_TAG@
diff --git a/src/shared/wtmp.c b/src/shared/wtmp.c
new file mode 100644
index 00000000..5881aeba
--- /dev/null
+++ b/src/shared/wtmp.c
@@ -0,0 +1,51 @@
+/*
+ * wtmp.c
+ * This file contains routines to deal with the wtmp file.
+ */
+
+/*
+ * Copyright 2017 The OpenRC Authors.
+ * See the Authors file at the top-level directory of this distribution and
+ * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS
+ *
+ * This file is part of OpenRC. It is subject to the license terms in
+ * the LICENSE file found in the top-level directory of this
+ * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE
+ * This file may not be copied, modified, propagated, or distributed
+ * except according to the terms contained in the LICENSE file.
+ */
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/utsname.h>
+
+#include "wtmp.h"
+
+void log_wtmp(const char *user, const char *id, pid_t pid, int type,
+ const char *line)
+{
+ struct timeval tv;
+ struct utmp utmp;
+ struct utsname uname_buf;
+
+ memset(&utmp, 0, sizeof(utmp));
+ gettimeofday(&tv, NULL);
+ utmp.ut_tv.tv_sec = tv.tv_sec;
+ utmp.ut_tv.tv_usec = tv.tv_usec;
+ utmp.ut_pid = pid;
+ utmp.ut_type = type;
+ strncpy(utmp.ut_name, user, sizeof(utmp.ut_name));
+ strncpy(utmp.ut_id , id , sizeof(utmp.ut_id ));
+ strncpy(utmp.ut_line, line, sizeof(utmp.ut_line));
+
+ /* Put the OS version in place of the hostname */
+ if (uname(&uname_buf) == 0)
+ strncpy(utmp.ut_host, uname_buf.release, sizeof(utmp.ut_host));
+
+ updwtmp(WTMP_FILE, &utmp);
+}
diff --git a/src/shared/wtmp.h b/src/shared/wtmp.h
new file mode 100644
index 00000000..edab322c
--- /dev/null
+++ b/src/shared/wtmp.h
@@ -0,0 +1,26 @@
+/*
+ * rc-wtmp.h
+ * This is private to us and not for user consumption
+*/
+
+/*
+ * Copyright (c) 2017 The OpenRC Authors.
+ * See the Authors file at the top-level directory of this distribution and
+ * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS
+ *
+ * This file is part of OpenRC. It is subject to the license terms in
+ * the LICENSE file found in the top-level directory of this
+ * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE
+ * This file may not be copied, modified, propagated, or distributed
+ * except according to the terms contained in the LICENSE file.
+ */
+
+#ifndef __RC_WTMP_H__
+#define __RC_WTMP_H__
+
+#include <utmp.h>
+
+void log_wtmp(const char *user, const char *id, pid_t pid, int type,
+ const char *line);
+
+#endif