diff options
Diffstat (limited to 'src/shared')
-rw-r--r-- | src/shared/_usage.c | 95 | ||||
-rw-r--r-- | src/shared/_usage.h | 56 | ||||
-rw-r--r-- | src/shared/helpers.h | 174 | ||||
-rw-r--r-- | src/shared/meson.build | 35 | ||||
-rw-r--r-- | src/shared/misc.c | 492 | ||||
-rw-r--r-- | src/shared/misc.h | 74 | ||||
-rw-r--r-- | src/shared/plugin.c | 245 | ||||
-rw-r--r-- | src/shared/plugin.h | 41 | ||||
-rw-r--r-- | src/shared/queue.h | 846 | ||||
-rw-r--r-- | src/shared/schedules.c | 433 | ||||
-rw-r--r-- | src/shared/schedules.h | 26 | ||||
-rw-r--r-- | src/shared/selinux.c | 417 | ||||
-rw-r--r-- | src/shared/selinux.h | 36 | ||||
-rw-r--r-- | src/shared/version.h.in | 18 | ||||
-rw-r--r-- | src/shared/version.in | 1 | ||||
-rw-r--r-- | src/shared/wtmp.c | 51 | ||||
-rw-r--r-- | src/shared/wtmp.h | 26 |
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 |