diff options
| author | William Hubbs <w.d.hubbs@gmail.com> | 2014-12-03 10:13:41 -0600 |
|---|---|---|
| committer | William Hubbs <w.d.hubbs@gmail.com> | 2014-12-03 10:13:41 -0600 |
| commit | 1267025fb76af18e31b2c7de16606abbb9b87ea3 (patch) | |
| tree | 8cc3a36b1dd7e1438d05a293cae8eac572126a7c /src/rc | |
| parent | 30cc3cdb76a66c7c0f89a52db4e5cff77b570e31 (diff) | |
initial commitorigin/gh-pagesgithub/gh-pages
Diffstat (limited to 'src/rc')
| -rw-r--r-- | src/rc/.gitignore | 61 | ||||
| -rw-r--r-- | src/rc/Makefile | 99 | ||||
| -rw-r--r-- | src/rc/_usage.c | 105 | ||||
| -rw-r--r-- | src/rc/_usage.h | 56 | ||||
| -rw-r--r-- | src/rc/builtins.h | 47 | ||||
| -rw-r--r-- | src/rc/checkpath.c | 302 | ||||
| -rw-r--r-- | src/rc/fstabinfo.c | 348 | ||||
| -rw-r--r-- | src/rc/mountinfo.c | 496 | ||||
| -rw-r--r-- | src/rc/rc-applets.c | 570 | ||||
| -rw-r--r-- | src/rc/rc-depend.c | 244 | ||||
| -rw-r--r-- | src/rc/rc-logger.c | 320 | ||||
| -rw-r--r-- | src/rc/rc-logger.h | 36 | ||||
| -rw-r--r-- | src/rc/rc-misc.c | 347 | ||||
| -rw-r--r-- | src/rc/rc-plugin.c | 257 | ||||
| -rw-r--r-- | src/rc/rc-plugin.h | 54 | ||||
| -rw-r--r-- | src/rc/rc-selinux.c | 399 | ||||
| -rw-r--r-- | src/rc/rc-selinux.h | 35 | ||||
| -rw-r--r-- | src/rc/rc-service.c | 135 | ||||
| -rw-r--r-- | src/rc/rc-status.c | 388 | ||||
| -rw-r--r-- | src/rc/rc-update.c | 366 | ||||
| -rw-r--r-- | src/rc/rc.c | 1150 | ||||
| -rw-r--r-- | src/rc/runscript.c | 1396 | ||||
| -rw-r--r-- | src/rc/start-stop-daemon.c | 1361 | ||||
| -rw-r--r-- | src/rc/start-stop-daemon.pam | 6 | ||||
| -rw-r--r-- | src/rc/swclock.c | 119 |
25 files changed, 0 insertions, 8697 deletions
diff --git a/src/rc/.gitignore b/src/rc/.gitignore deleted file mode 100644 index bbfede6a..00000000 --- a/src/rc/.gitignore +++ /dev/null @@ -1,61 +0,0 @@ -version.h -rc-status -rc-service -rc-update -runscript -service -start-stop-daemon -einfon -einfo -ewarnn -ewarn -eerrorn -eerror -ebegin -eend -ewend -eindent -eoutdent -esyslog -eval_ecolors -ewaitfile -veinfo -vewarn -vebegin -veend -vewend -veindent -veoutdent -service_starting -service_started -service_stopping -service_stopped -service_inactive -service_wasinactive -service_hotplugged -service_started_daemon -service_crashed -checkpath -fstabinfo -mountinfo -swclock -rc-depend -service_get_value -service_set_value -get_options -save_options -shell_var -is_newer_than -is_older_than -mark_service_starting -mark_service_started -mark_service_stopping -mark_service_stopped -mark_service_inactive -mark_service_wasinactive -mark_service_hotplugged -mark_service_failed -rc-abort -rc -openrc -openrc-run diff --git a/src/rc/Makefile b/src/rc/Makefile deleted file mode 100644 index bd8b942c..00000000 --- a/src/rc/Makefile +++ /dev/null @@ -1,99 +0,0 @@ -PROG= openrc -SRCS= checkpath.c fstabinfo.c mountinfo.c start-stop-daemon.c \ - rc-applets.c rc-depend.c rc-logger.c \ - rc-misc.c rc-plugin.c rc-service.c rc-status.c rc-update.c \ - runscript.c rc.c swclock.c - -ifeq (${MKSELINUX},yes) -SRCS+= rc-selinux.c -endif - -CLEANFILES= version.h rc-selinux.o - -BINDIR= ${PREFIX}/bin -SBINDIR= ${PREFIX}/sbin -LINKDIR= ${LIBEXECDIR} - -BINLINKS= rc-status -SBINLINKS= rc rc-service rc-update openrc-run runscript service \ - start-stop-daemon -RC_BINLINKS= einfon einfo ewarnn ewarn eerrorn eerror ebegin eend ewend \ - eindent eoutdent esyslog eval_ecolors ewaitfile \ - veinfo vewarn vebegin veend vewend veindent veoutdent \ - service_starting service_started \ - service_stopping service_stopped \ - service_inactive service_wasinactive \ - service_hotplugged service_started_daemon service_crashed \ - checkpath fstabinfo mountinfo rc-depend \ - service_get_value service_set_value get_options save_options \ - shell_var is_newer_than is_older_than -RC_SBINLINKS= mark_service_starting mark_service_started \ - mark_service_stopping mark_service_stopped \ - mark_service_inactive mark_service_wasinactive \ - mark_service_hotplugged mark_service_failed \ - rc-abort swclock -ALL_LINKS= ${BINLINKS} ${SBINLINKS} ${RC_BINLINKS} ${RC_SBINLINKS} -CLEANFILES+= ${ALL_LINKS} - -CPPFLAGS+= -I../includes -I../librc -I../libeinfo -LDFLAGS+= -L../librc -L../libeinfo -LDADD+= -lutil -lrc -leinfo - -include ../../Makefile.inc -MK= ../../mk -include ${MK}/prog.mk -include ${MK}/git.mk -include ${MK}/cc.mk - -include ${MK}/termcap.mk -LDADD+= ${LIBDL} ${LIBKVM} -include ${MK}/pam.mk - -# create symlinks to rc if not an SELINUX system, otherwise create a wrapper -# script to call rc with the proper name of the applet to execute. -# $1 is a list of the links -# $2 is the path+name of the target to link to (usually 'rc' or '/sbin/rc') -# $3 is the path where the links are created -define make-links - for x in $(1); do \ - if [ "${MKSELINUX}" = yes ]; then \ - printf '#!/bin/sh\nexec ${2} --applet %s "$$@"\n' $$x >${3}/$$x; \ - chmod ${BINMODE} ${3}/$$x; \ - else \ - ln -sf ${2} ${3}/$$x; \ - fi; \ - done; -endef - -${SRCS}: version.h - -.PHONY: version.h.tmp -version.h.tmp: - echo "#define VERSION \"${VERSION}${GITVER}\"" >$@ - if test -n "${BRANDING}"; then \ - echo "#define BRANDING \"${BRANDING}\"" >> $@; \ - fi - -version.h: version.h.tmp - cmp -s $@.tmp $@ && rm $@.tmp || mv $@.tmp $@ - -install: all - ${INSTALL} -d ${DESTDIR}${SBINDIR} - ${INSTALL} -m ${BINMODE} ${PROG} ${DESTDIR}${SBINDIR} - ${INSTALL} -d ${DESTDIR}${BINDIR} - $(call make-links,${BINLINKS},${SBINDIR}/${PROG},${DESTDIR}${BINDIR}) - ${INSTALL} -d ${DESTDIR}${SBINDIR} - $(call make-links,${SBINLINKS},${SBINDIR}/${PROG},${DESTDIR}${SBINDIR}) - ${INSTALL} -d ${DESTDIR}${LINKDIR}/bin - $(call make-links,${RC_BINLINKS},${SBINDIR}/${PROG},${DESTDIR}${LINKDIR}/bin) - ${INSTALL} -d ${DESTDIR}${LINKDIR}/sbin - $(call make-links, ${RC_SBINLINKS},${SBINDIR}/${PROG},${DESTDIR}${LINKDIR}/sbin) - if test "${MKPAM}" = pam; then \ - ${INSTALL} -d ${DESTDIR}${PAMDIR}; \ - ${INSTALL} -m ${PAMMODE} start-stop-daemon.pam ${DESTDIR}${PAMDIR}/start-stop-daemon; \ - fi - -check test:: - -links: ${PROG} - $(call make-links,${ALL_LINKS},${PROG},.) diff --git a/src/rc/_usage.c b/src/rc/_usage.c deleted file mode 100644 index a95e93f5..00000000 --- a/src/rc/_usage.c +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (c) 2007-2008 Roy Marples <roy@marples.name> - * - * 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. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. - */ - -#include "version.h" -#include <ctype.h> - -#if lint -# define _noreturn -#endif -#if __GNUC__ > 2 || defined(__INTEL_COMPILER) -# define _noreturn __attribute__ ((__noreturn__)) -#else -# define _noreturn -#endif - -static 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 static void -show_version(void) -{ - const char *bootlevel = NULL; - - printf("%s (OpenRC", applet); - if ((bootlevel = rc_sys())) - printf(" [%s]", bootlevel); - printf(") %s", VERSION); -#ifdef BRANDING - printf(" (%s)", BRANDING); -#endif - printf("\n"); - exit(EXIT_SUCCESS); -} - -_noreturn static 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] = "-?,"; - -#ifdef usagestring - printf(usagestring); -#else - printf("Usage: %s [options] ", applet); -#endif -#ifdef extraopts - printf(extraopts); -#endif - printf("\n\nOptions: [" getoptstring "]\n"); - 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/rc/_usage.h b/src/rc/_usage.h deleted file mode 100644 index 0560e89f..00000000 --- a/src/rc/_usage.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (c) 2007-2008 Roy Marples <roy@marples.name> - * - * 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. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. - */ - -#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; diff --git a/src/rc/builtins.h b/src/rc/builtins.h deleted file mode 100644 index d9bb9849..00000000 --- a/src/rc/builtins.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2007-2009 Roy Marples <roy@marples.name> - * - * 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. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. - */ - -#include "queue.h" -#include "rc.h" - -int checkpath(int, char **); -int fstabinfo(int, char **); -int mountinfo(int, char **); -int openrc_run(int, char **); -int rc_depend(int, char **); -int rc_service(int, char **); -int rc_status(int, char **); -int rc_update(int, char **); -int runscript(int, char **); -int start_stop_daemon(int, char **); -int swclock(int, char **); - -void run_applets(int, char **); - -/* Handy function so we can wrap einfo around our deptree */ -RC_DEPTREE *_rc_deptree_load (int, int *); - -/* Test to see if we can see pid 1 or not */ -bool _rc_can_find_pids(void); diff --git a/src/rc/checkpath.c b/src/rc/checkpath.c deleted file mode 100644 index 94ab4742..00000000 --- a/src/rc/checkpath.c +++ /dev/null @@ -1,302 +0,0 @@ -/* - checkpath.c - Checks for the existance of a file or directory and creates it - if necessary. It can also correct its ownership. -*/ - -/* - * Copyright (c) 2007-2008 Roy Marples <roy@marples.name> - * - * 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. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. - */ - -#include <sys/types.h> -#include <sys/stat.h> - -#include <errno.h> -#include <fcntl.h> -#include <getopt.h> -#include <grp.h> -#include <pwd.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -#include "builtins.h" -#include "einfo.h" -#include "rc-misc.h" - -#ifdef HAVE_SELINUX -#include "rc-selinux.h" -#endif - -typedef enum { - inode_unknown = 0, - inode_file = 1, - inode_dir = 2, - inode_fifo = 3, -} inode_t; - -extern const char *applet; - -static int do_check(char *path, uid_t uid, gid_t gid, mode_t mode, - inode_t type, bool trunc, bool chowner, bool selinux_on) -{ - struct stat st; - int fd, flags; - int r; - int u; - - memset(&st, 0, sizeof(st)); - if (stat(path, &st) || trunc) { - if (type == inode_file) { - einfo("%s: creating file", path); - if (!mode) /* 664 */ - mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH; - flags = O_CREAT|O_NDELAY|O_WRONLY|O_NOCTTY; -#ifdef O_CLOEXEC - flags |= O_CLOEXEC; -#endif -#ifdef O_NOFOLLOW - flags |= O_NOFOLLOW; -#endif - if (trunc) - flags |= O_TRUNC; - u = umask(0); - fd = open(path, flags, mode); - umask(u); - if (fd == -1) { - eerror("%s: open: %s", applet, strerror(errno)); - return -1; - } - close (fd); - } else if (type == inode_dir) { - einfo("%s: creating directory", path); - if (!mode) /* 775 */ - mode = S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH; - u = umask(0); - /* We do not recursively create parents */ - r = mkdir(path, mode); - umask(u); - if (r == -1 && errno != EEXIST) { - eerror("%s: mkdir: %s", applet, - strerror (errno)); - return -1; - } - mode = 0; - } else if (type == inode_fifo) { - einfo("%s: creating fifo", path); - if (!mode) /* 600 */ - mode = S_IRUSR | S_IWUSR; - u = umask(0); - r = mkfifo(path, mode); - umask(u); - if (r == -1 && errno != EEXIST) { - eerror("%s: mkfifo: %s", applet, - strerror (errno)); - return -1; - } - } - } else { - if (type != inode_dir && S_ISDIR(st.st_mode)) { - eerror("%s: is a directory", path); - return 1; - } - if (type != inode_file && S_ISREG(st.st_mode)) { - eerror("%s: is a file", path); - return 1; - } - if (type != inode_fifo && S_ISFIFO(st.st_mode)) { - eerror("%s: is a fifo", path); - return -1; - } - } - - if (mode && (st.st_mode & 0777) != mode) { - einfo("%s: correcting mode", path); - if (chmod(path, mode)) { - eerror("%s: chmod: %s", applet, strerror(errno)); - return -1; - } - } - - if (chowner && (st.st_uid != uid || st.st_gid != gid)) { - einfo("%s: correcting owner", path); - if (chown(path, uid, gid)) { - eerror("%s: chown: %s", applet, strerror(errno)); - return -1; - } - } - -#ifdef HAVE_SELINUX - if (selinux_on) - selinux_util_label(path); -#endif - - return 0; -} - -static int parse_owner(struct passwd **user, struct group **group, - const char *owner) -{ - char *u = xstrdup (owner); - char *g = strchr (u, ':'); - int id = 0; - int retval = 0; - - if (g) - *g++ = '\0'; - - if (user && *u) { - if (sscanf(u, "%d", &id) == 1) - *user = getpwuid((uid_t) id); - else - *user = getpwnam(u); - if (*user == NULL) - retval = -1; - } - - if (group && g && *g) { - if (sscanf(g, "%d", &id) == 1) - *group = getgrgid((gid_t) id); - else - *group = getgrnam(g); - if (*group == NULL) - retval = -1; - } - - free(u); - return retval; -} - -#include "_usage.h" -#define extraopts "path1 [path2] [...]" -#define getoptstring "dDfFpm:o:W" getoptstring_COMMON -static const struct option longopts[] = { - { "directory", 0, NULL, 'd'}, - { "directory-truncate", 0, NULL, 'D'}, - { "file", 0, NULL, 'f'}, - { "file-truncate", 0, NULL, 'F'}, - { "pipe", 0, NULL, 'p'}, - { "mode", 1, NULL, 'm'}, - { "owner", 1, NULL, 'o'}, - { "writable", 0, NULL, 'W'}, - longopts_COMMON -}; -static const char * const longopts_help[] = { - "Create a directory if not exists", - "Create/empty directory", - "Create a file if not exists", - "Truncate file", - "Create a named pipe (FIFO) if not exists", - "Mode to check", - "Owner to check (user:group)", - "Check whether the path is writable or not", - longopts_help_COMMON -}; -#include "_usage.c" - -int checkpath(int argc, char **argv) -{ - int opt; - uid_t uid = geteuid(); - gid_t gid = getgid(); - mode_t mode = 0; - struct passwd *pw = NULL; - struct group *gr = NULL; - inode_t type = inode_unknown; - int retval = EXIT_SUCCESS; - bool trunc = false; - bool chowner = false; - bool writable = false; - bool selinux_on = false; - - while ((opt = getopt_long(argc, argv, getoptstring, - longopts, (int *) 0)) != -1) - { - switch (opt) { - case 'D': - trunc = true; - case 'd': - type = inode_dir; - break; - case 'F': - trunc = true; - case 'f': - type = inode_file; - break; - case 'p': - type = inode_fifo; - break; - case 'm': - if (parse_mode(&mode, optarg) != 0) - eerrorx("%s: invalid mode `%s'", - applet, optarg); - break; - case 'o': - chowner = true; - if (parse_owner(&pw, &gr, optarg) != 0) - eerrorx("%s: owner `%s' not found", - applet, optarg); - break; - case 'W': - writable = true; - break; - - case_RC_COMMON_GETOPT - } - } - - if (optind >= argc) - usage(EXIT_FAILURE); - - if (writable && type != inode_unknown) - eerrorx("%s: -W cannot be specified along with -d, -f or -p", applet); - - if (pw) { - uid = pw->pw_uid; - gid = pw->pw_gid; - } - if (gr) - gid = gr->gr_gid; - -#ifdef HAVE_SELINUX - if (selinux_util_open() == 1) - selinux_on = true; -#endif - - while (optind < argc) { - if (writable) - exit(!is_writable(argv[optind])); - if (do_check(argv[optind], uid, gid, mode, type, trunc, chowner, selinux_on)) - retval = EXIT_FAILURE; - optind++; - } - -#ifdef HAVE_SELINUX - if (selinux_on) - selinux_util_close(); -#endif - - return retval; -} diff --git a/src/rc/fstabinfo.c b/src/rc/fstabinfo.c deleted file mode 100644 index 34fea304..00000000 --- a/src/rc/fstabinfo.c +++ /dev/null @@ -1,348 +0,0 @@ -/* - fstabinfo.c - Gets information about /etc/fstab. -*/ - -/* - * Copyright (c) 2007-2008 Roy Marples <roy@marples.name> - * - * 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. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. - */ - -#include <sys/wait.h> - -#include <errno.h> -#include <getopt.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -/* Yay for linux and its non liking of POSIX functions. - Okay, we could use getfsent but the man page says use getmntent instead - AND we don't have getfsent on uclibc or dietlibc for some odd reason. */ -#ifdef __linux__ -# define HAVE_GETMNTENT -# include <mntent.h> -# define ENT mntent -# define START_ENT fp = setmntent ("/etc/fstab", "r"); -# define GET_ENT getmntent (fp) -# define GET_ENT_FILE(_name) getmntfile (_name) -# define END_ENT endmntent (fp) -# define ENT_BLOCKDEVICE(_ent) ent->mnt_fsname -# define ENT_FILE(_ent) ent->mnt_dir -# define ENT_TYPE(_ent) ent->mnt_type -# define ENT_OPTS(_ent) ent->mnt_opts -# define ENT_PASS(_ent) ent->mnt_passno -#else -# define HAVE_GETFSENT -# include <fstab.h> -# define ENT fstab -# define START_ENT -# define GET_ENT getfsent () -# define GET_ENT_FILE(_name) getfsfile (_name) -# define END_ENT endfsent () -# define ENT_BLOCKDEVICE(_ent) ent->fs_spec -# define ENT_TYPE(_ent) ent->fs_vfstype -# define ENT_FILE(_ent) ent->fs_file -# define ENT_OPTS(_ent) ent->fs_mntops -# define ENT_PASS(_ent) ent->fs_passno -#endif - -#include "builtins.h" -#include "einfo.h" -#include "queue.h" -#include "rc.h" -#include "rc-misc.h" - -#ifdef HAVE_GETMNTENT -static struct mntent * -getmntfile(const char *file) -{ - struct mntent *ent; - FILE *fp; - - START_ENT; - while ((ent = getmntent(fp))) - if (strcmp(file, ent->mnt_dir) == 0) - break; - END_ENT; - - return ent; -} -#endif - -extern const char *applet; - -static int -do_mount(struct ENT *ent, bool remount) -{ - char *argv[10]; - pid_t pid; - int status; - - argv[0] = UNCONST("mount"); - argv[1] = UNCONST("-o"); - argv[2] = ENT_OPTS(*ent); - argv[3] = UNCONST("-t"); - argv[4] = ENT_TYPE(*ent); - if (!remount) { - argv[5] = ENT_BLOCKDEVICE(*ent); - argv[6] = ENT_FILE(*ent); - argv[7] = NULL; - } else { -#ifdef __linux__ - argv[5] = UNCONST("-o"); - argv[6] = UNCONST("remount"); - argv[7] = ENT_BLOCKDEVICE(*ent); - argv[8] = ENT_FILE(*ent); - argv[9] = NULL; -#else - argv[5] = UNCONST("-u"); - argv[6] = ENT_BLOCKDEVICE(*ent); - argv[7] = ENT_FILE(*ent); - argv[8] = NULL; -#endif - } - switch (pid = vfork()) { - case -1: - eerrorx("%s: vfork: %s", applet, strerror(errno)); - /* NOTREACHED */ - case 0: - execvp(argv[0], argv); - eerror("%s: execv: %s", applet, strerror(errno)); - _exit(EXIT_FAILURE); - /* NOTREACHED */ - default: - waitpid(pid, &status, 0); - if (WIFEXITED(status)) - return WEXITSTATUS(status); - else - return -1; - /* NOTREACHED */ - } -} - -#include "_usage.h" -#define getoptstring "MRbmop:t:" getoptstring_COMMON -static const struct option longopts[] = { - { "mount", 0, NULL, 'M' }, - { "remount", 0, NULL, 'R' }, - { "blockdevice", 0, NULL, 'b' }, - { "mountargs", 0, NULL, 'm' }, - { "options", 0, NULL, 'o' }, - { "passno", 1, NULL, 'p' }, - { "fstype", 1, NULL, 't' }, - longopts_COMMON -}; -static const char * const longopts_help[] = { - "Mounts the filesytem from the mountpoint", - "Remounts the filesystem based on the information in fstab", - "Extract the block device", - "Show arguments needed to mount the entry", - "Extract the options field", - "Extract or query the pass number field", - "List entries with matching file system type", - longopts_help_COMMON -}; -#include "_usage.c" - -#define OUTPUT_FILE (1 << 1) -#define OUTPUT_MOUNTARGS (1 << 2) -#define OUTPUT_OPTIONS (1 << 3) -#define OUTPUT_PASSNO (1 << 4) -#define OUTPUT_BLOCKDEV (1 << 5) -#define OUTPUT_MOUNT (1 << 6) -#define OUTPUT_REMOUNT (1 << 7) - -int -fstabinfo(int argc, char **argv) -{ - struct ENT *ent; - int result = EXIT_SUCCESS; - char *token; - int i, p; - int opt; - int output = OUTPUT_FILE; - RC_STRINGLIST *files = rc_stringlist_new(); - RC_STRING *file, *file_np; - bool filtered = false; - -#ifdef HAVE_GETMNTENT - FILE *fp; -#endif - - /* Ensure that we are only quiet when explicitly told to be */ - unsetenv("EINFO_QUIET"); - - while ((opt = getopt_long(argc, argv, getoptstring, - longopts, (int *) 0)) != -1) - { - switch (opt) { - case 'M': - output = OUTPUT_MOUNT; - break; - case 'R': - output = OUTPUT_REMOUNT; - break; - case 'b': - output = OUTPUT_BLOCKDEV; - break; - case 'o': - output = OUTPUT_OPTIONS; - break; - case 'm': - output = OUTPUT_MOUNTARGS; - break; - - case 'p': - switch (optarg[0]) { - case '=': - case '<': - case '>': - if (sscanf(optarg + 1, "%d", &i) != 1) - eerrorx("%s: invalid passno %s", - argv[0], optarg + 1); - - filtered = true; - opt = optarg[0]; - START_ENT; - while ((ent = GET_ENT)) { - if (strcmp(ENT_FILE(ent), "none") == 0) - continue; - p = ENT_PASS(ent); - if ((opt == '=' && i == p) || - (opt == '<' && i > p && p != 0) || - (opt == '>' && i < p && p != 0)) - rc_stringlist_add(files, - ENT_FILE(ent)); - } - END_ENT; - break; - - default: - rc_stringlist_add(files, optarg); - output = OUTPUT_PASSNO; - break; - } - break; - - case 't': - filtered = true; - while ((token = strsep(&optarg, ","))) { - START_ENT; - while ((ent = GET_ENT)) - if (strcmp(token, ENT_TYPE(ent)) == 0) - rc_stringlist_add(files, - ENT_FILE(ent)); - END_ENT; - } - break; - - case_RC_COMMON_GETOPT - } - } - - if (optind < argc) { - if (TAILQ_FIRST(files)) { - TAILQ_FOREACH_SAFE(file, files, entries, file_np) { - for (i = optind; i < argc; i++) - if (strcmp(argv[i], file->value) == 0) - break; - if (i >= argc) - rc_stringlist_delete(files, - file->value); - } - } else { - while (optind < argc) - rc_stringlist_add(files, argv[optind++]); - } - } else if (!filtered) { - START_ENT; - while ((ent = GET_ENT)) - rc_stringlist_add(files, ENT_FILE(ent)); - END_ENT; - - if (!TAILQ_FIRST(files)) - eerrorx("%s: empty fstab", argv[0]); - } - - if (!TAILQ_FIRST(files)) { - rc_stringlist_free(files); - return (EXIT_FAILURE); - } - - /* Ensure we always display something */ - START_ENT; - TAILQ_FOREACH(file, files, entries) { - if (!(ent = GET_ENT_FILE(file->value))) { - result = EXIT_FAILURE; - continue; - } - - /* mount or remount? */ - switch (output) { - case OUTPUT_MOUNT: - result += do_mount(ent, false); - break; - - case OUTPUT_REMOUNT: - result += do_mount(ent, true); - break; - } - - /* No point in outputting if quiet */ - if (rc_yesno(getenv("EINFO_QUIET"))) - continue; - - switch (output) { - case OUTPUT_BLOCKDEV: - printf("%s\n", ENT_BLOCKDEVICE(ent)); - break; - - case OUTPUT_MOUNTARGS: - printf("-o %s -t %s %s %s\n", - ENT_OPTS(ent), - ENT_TYPE(ent), - ENT_BLOCKDEVICE(ent), - file->value); - break; - - case OUTPUT_OPTIONS: - printf("%s\n", ENT_OPTS(ent)); - break; - - case OUTPUT_FILE: - printf("%s\n", file->value); - break; - - case OUTPUT_PASSNO: - printf("%d\n", ENT_PASS(ent)); - break; - } - } - END_ENT; - - rc_stringlist_free(files); - exit(result); - /* NOTREACHED */ -} diff --git a/src/rc/mountinfo.c b/src/rc/mountinfo.c deleted file mode 100644 index 8790dfee..00000000 --- a/src/rc/mountinfo.c +++ /dev/null @@ -1,496 +0,0 @@ -/* - mountinfo.c - Obtains information about mounted filesystems. -*/ - -/* - * Copyright (c) 2007-2008 Roy Marples <roy@marples.name> - * - * 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. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. - */ - -#include <sys/types.h> -#include <sys/param.h> - -#if defined(__DragonFly__) || defined(__FreeBSD__) -# include <sys/ucred.h> -# include <sys/mount.h> -# define F_FLAGS f_flags -#elif defined(BSD) && !defined(__GNU__) -# include <sys/statvfs.h> -# define statfs statvfs -# define F_FLAGS f_flag -#elif defined (__linux__) || (defined(__FreeBSD_kernel__) && \ - defined(__GLIBC__)) || defined(__GNU__) -# include <mntent.h> -#endif - -#include <errno.h> -#include <getopt.h> -#include <limits.h> -#include <regex.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#include "builtins.h" -#include "einfo.h" -#include "queue.h" -#include "rc.h" -#include "rc-misc.h" - -extern const char *applet; - -typedef enum { - mount_from, - mount_to, - mount_fstype, - mount_options -} mount_type; - -typedef enum { - net_ignore, - net_yes, - net_no -} net_opts; - -struct args { - regex_t *node_regex; - regex_t *skip_node_regex; - regex_t *fstype_regex; - regex_t *skip_fstype_regex; - regex_t *options_regex; - regex_t *skip_options_regex; - RC_STRINGLIST *mounts; - mount_type mount_type; - net_opts netdev; -}; - -static int -process_mount(RC_STRINGLIST *list, struct args *args, - char *from, char *to, char *fstype, char *options, - int netdev) -{ - char *p; - RC_STRING *s; - - errno = ENOENT; - -#ifdef __linux__ - /* Skip the really silly rootfs */ - if (strcmp(fstype, "rootfs") == 0) - return -1; -#endif - - if (args->netdev == net_yes && - (netdev != -1 || TAILQ_FIRST(args->mounts))) - { - if (netdev != 0) - return 1; - } else if (args->netdev == net_no && - (netdev != -1 || TAILQ_FIRST(args->mounts))) - { - if (netdev != 1) - return 1; - } else { - if (args->node_regex && - regexec(args->node_regex, from, 0, NULL, 0) != 0) - return 1; - if (args->skip_node_regex && - regexec(args->skip_node_regex, from, 0, NULL, 0) == 0) - return 1; - - if (args->fstype_regex && - regexec(args->fstype_regex, fstype, 0, NULL, 0) != 0) - return -1; - if (args->skip_fstype_regex && - regexec(args->skip_fstype_regex, fstype, 0, NULL, 0) == 0) - return -1; - - if (args->options_regex && - regexec(args->options_regex, options, 0, NULL, 0) != 0) - return -1; - if (args->skip_options_regex && - regexec(args->skip_options_regex, options, 0, NULL, 0) == 0) - return -1; - } - - if (TAILQ_FIRST(args->mounts)) { - TAILQ_FOREACH(s, args->mounts, entries) - if (strcmp(s->value, to) == 0) - break; - if (! s) - return -1; - } - - switch (args->mount_type) { - case mount_from: - p = from; - break; - case mount_to: - p = to; - break; - case mount_fstype: - p = fstype; - break; - case mount_options: - p = options; - break; - default: - p = NULL; - errno = EINVAL; - break; - } - - if (p) { - errno = 0; - rc_stringlist_add(list, p); - return 0; - } - - return -1; -} - -#if defined(BSD) && !defined(__GNU__) - -/* Translate the mounted options to english - * This is taken directly from FreeBSD mount.c */ -static struct opt { - int o_opt; - const char *o_name; -} optnames[] = { - { MNT_ASYNC, "asynchronous" }, - { MNT_EXPORTED, "NFS exported" }, - { MNT_LOCAL, "local" }, - { MNT_NOATIME, "noatime" }, - { MNT_NOEXEC, "noexec" }, - { MNT_NOSUID, "nosuid" }, -#ifdef MNT_NOSYMFOLLOW - { MNT_NOSYMFOLLOW, "nosymfollow" }, -#endif - { MNT_QUOTA, "with quotas" }, - { MNT_RDONLY, "read-only" }, - { MNT_SYNCHRONOUS, "synchronous" }, - { MNT_UNION, "union" }, -#ifdef MNT_NOCLUSTERR - { MNT_NOCLUSTERR, "noclusterr" }, -#endif -#ifdef MNT_NOCLUSTERW - { MNT_NOCLUSTERW, "noclusterw" }, -#endif -#ifdef MNT_SUIDDIR - { MNT_SUIDDIR, "suiddir" }, -#endif - { MNT_SOFTDEP, "soft-updates" }, -#ifdef MNT_MULTILABEL - { MNT_MULTILABEL, "multilabel" }, -#endif -#ifdef MNT_ACLS - { MNT_ACLS, "acls" }, -#endif -#ifdef MNT_GJOURNAL - { MNT_GJOURNAL, "gjournal" }, -#endif - { 0, NULL } -}; - -static RC_STRINGLIST * -find_mounts(struct args *args) -{ - struct statfs *mnts; - int nmnts; - int i; - RC_STRINGLIST *list; - char *options = NULL; - uint64_t flags; - struct opt *o; - int netdev; - char *tmp; - size_t l; - - if ((nmnts = getmntinfo(&mnts, MNT_NOWAIT)) == 0) - eerrorx("getmntinfo: %s", strerror (errno)); - - list = rc_stringlist_new(); - for (i = 0; i < nmnts; i++) { - netdev = 0; - flags = mnts[i].F_FLAGS & MNT_VISFLAGMASK; - for (o = optnames; flags && o->o_opt; o++) { - if (flags & o->o_opt) { - if (o->o_opt == MNT_LOCAL) - netdev = 1; - if (! options) - options = xstrdup(o->o_name); - else { - l = strlen(options) + - strlen(o->o_name) + 2; - tmp = xmalloc(sizeof (char) * l); - snprintf(tmp, l, "%s,%s", options, - o->o_name); - free(options); - options = tmp; - } - } - flags &= ~o->o_opt; - } - - process_mount(list, args, - mnts[i].f_mntfromname, - mnts[i].f_mntonname, - mnts[i].f_fstypename, - options, - netdev); - - free(options); - options = NULL; - } - - return list; -} - -#elif defined (__linux__) || (defined (__FreeBSD_kernel__) && \ - defined(__GLIBC__)) -static struct mntent * -getmntfile(const char *file) -{ - struct mntent *ent = NULL; - FILE *fp; - - fp = setmntent("/etc/fstab", "r"); - while ((ent = getmntent(fp))) - if (strcmp(file, ent->mnt_dir) == 0) - break; - endmntent(fp); - - return ent; -} - -static RC_STRINGLIST * -find_mounts(struct args *args) -{ - FILE *fp; - char *buffer; - char *p; - char *from; - char *to; - char *fst; - char *opts; - struct mntent *ent; - int netdev; - RC_STRINGLIST *list; - - if ((fp = fopen("/proc/mounts", "r")) == NULL) - eerrorx("getmntinfo: %s", strerror(errno)); - - list = rc_stringlist_new(); - - buffer = xmalloc(sizeof(char) * PATH_MAX * 3); - while (fgets(buffer, PATH_MAX * 3, fp)) { - netdev = -1; - p = buffer; - from = strsep(&p, " "); - to = strsep(&p, " "); - fst = strsep(&p, " "); - opts = strsep(&p, " "); - - if ((ent = getmntfile(to))) { - if (strstr(ent->mnt_opts, "_netdev")) - netdev = 0; - } - - process_mount(list, args, from, to, fst, opts, netdev); - } - free(buffer); - fclose(fp); - - return list; -} - -#else -# error "Operating system not supported!" -#endif - -static regex_t * -get_regex(const char *string) -{ - regex_t *reg = xmalloc(sizeof (*reg)); - int result; - char buffer[256]; - - if ((result = regcomp(reg, string, REG_EXTENDED | REG_NOSUB)) != 0) - { - regerror(result, reg, buffer, sizeof(buffer)); - eerrorx("%s: invalid regex `%s'", applet, buffer); - } - - return reg; -} - -#include "_usage.h" -#define extraopts "[mount1] [mount2] ..." -#define getoptstring "f:F:n:N:o:O:p:P:ist" getoptstring_COMMON -static const struct option longopts[] = { - { "fstype-regex", 1, NULL, 'f'}, - { "skip-fstype-regex", 1, NULL, 'F'}, - { "node-regex", 1, NULL, 'n'}, - { "skip-node-regex", 1, NULL, 'N'}, - { "options-regex", 1, NULL, 'o'}, - { "skip-options-regex", 1, NULL, 'O'}, - { "point-regex", 1, NULL, 'p'}, - { "skip-point-regex", 1, NULL, 'P'}, - { "options", 0, NULL, 'i'}, - { "fstype", 0, NULL, 's'}, - { "node", 0, NULL, 't'}, - { "netdev", 0, NULL, 'e'}, - { "nonetdev", 0, NULL, 'E'}, - longopts_COMMON -}; -static const char * const longopts_help[] = { - "fstype regex to find", - "fstype regex to skip", - "node regex to find", - "node regex to skip", - "options regex to find", - "options regex to skip", - "point regex to find", - "point regex to skip", - "print options", - "print fstype", - "print node", - "is it a network device", - "is it not a network device", - longopts_help_COMMON -}; -#include "_usage.c" - -int -mountinfo(int argc, char **argv) -{ - struct args args; - regex_t *point_regex = NULL; - regex_t *skip_point_regex = NULL; - RC_STRINGLIST *nodes; - RC_STRING *s; - char real_path[PATH_MAX + 1]; - int opt; - int result; - char *this_path; - -#define DO_REG(_var) \ - if (_var) free(_var); \ - _var = get_regex(optarg); -#define REG_FREE(_var) \ - if (_var) { regfree(_var); free(_var); } - - memset (&args, 0, sizeof(args)); - args.mount_type = mount_to; - args.netdev = net_ignore; - args.mounts = rc_stringlist_new(); - - while ((opt = getopt_long(argc, argv, getoptstring, - longopts, (int *) 0)) != -1) - { - switch (opt) { - case 'e': - args.netdev = net_yes; - break; - case 'E': - args.netdev = net_no; - break; - case 'f': - DO_REG(args.fstype_regex); - break; - case 'F': - DO_REG(args.skip_fstype_regex); - break; - case 'n': - DO_REG(args.node_regex); - break; - case 'N': - DO_REG(args.skip_node_regex); - break; - case 'o': - DO_REG(args.options_regex); - break; - case 'O': - DO_REG(args.skip_options_regex); - break; - case 'p': - DO_REG(point_regex); - break; - case 'P': - DO_REG(skip_point_regex); - break; - case 'i': - args.mount_type = mount_options; - break; - case 's': - args.mount_type = mount_fstype; - break; - case 't': - args.mount_type = mount_from; - break; - - case_RC_COMMON_GETOPT - } - } - - while (optind < argc) { - if (argv[optind][0] != '/') - eerrorx("%s: `%s' is not a mount point", - argv[0], argv[optind]); - this_path = argv[optind++]; - if (realpath(this_path, real_path)) - this_path = real_path; - rc_stringlist_add(args.mounts, this_path); - } - nodes = find_mounts(&args); - rc_stringlist_free(args.mounts); - - REG_FREE(args.fstype_regex); - REG_FREE(args.skip_fstype_regex); - REG_FREE(args.node_regex); - REG_FREE(args.skip_node_regex); - REG_FREE(args.options_regex); - REG_FREE(args.skip_options_regex); - - result = EXIT_FAILURE; - - /* We should report the mounts in reverse order to ease unmounting */ - TAILQ_FOREACH_REVERSE(s, nodes, rc_stringlist, entries) { - if (point_regex && - regexec(point_regex, s->value, 0, NULL, 0) != 0) - continue; - if (skip_point_regex && - regexec(skip_point_regex, s->value, 0, NULL, 0) == 0) - continue; - if (! rc_yesno(getenv("EINFO_QUIET"))) - printf("%s\n", s->value); - result = EXIT_SUCCESS; - } - rc_stringlist_free(nodes); - - REG_FREE(point_regex); - REG_FREE(skip_point_regex); - - return result; -} diff --git a/src/rc/rc-applets.c b/src/rc/rc-applets.c deleted file mode 100644 index 8fe2d223..00000000 --- a/src/rc/rc-applets.c +++ /dev/null @@ -1,570 +0,0 @@ -/* - rc-applets.c - - Handle multicall applets for use in our init scripts. - Basically this makes us a lot faster for the most part, and removes - any shell incompatabilities we might otherwise encounter. -*/ - -/* - * Copyright (c) 2007-2009 Roy Marples <roy@marples.name> - * - * 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. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. - */ - -#define SYSLOG_NAMES - -#include <sys/types.h> -#include <sys/time.h> - -#include <errno.h> -#include <ctype.h> -#include <inttypes.h> -#include <stdbool.h> -#include <stdio.h> -#include <stdlib.h> -#include <signal.h> -#include <string.h> -#include <syslog.h> -#include <time.h> -#include <unistd.h> - -#include "builtins.h" -#include "einfo.h" -#include "rc-misc.h" - -/* usecs to wait while we poll the file existance */ -#define WAIT_INTERVAL 20000000 -#define ONE_SECOND 690000000 - -/* Applet is first parsed in rc.c - no point in doing it again */ -extern const char *applet; - -static int -syslog_decode(char *name, CODE *codetab) -{ - CODE *c; - - if (isdigit((unsigned char)*name)) - return atoi(name); - - for (c = codetab; c->c_name; c++) - if (! strcasecmp(name, c->c_name)) - return c->c_val; - - return -1; -} - -static int -do_e(int argc, char **argv) -{ - int retval = EXIT_SUCCESS; - int i; - size_t l = 0; - char *message = NULL; - char *p; - int level = 0; - struct timespec ts; - struct timeval stop, now; - int (*e) (const char *, ...) EINFO_PRINTF(1, 2) = NULL; - int (*ee) (int, const char *, ...) EINFO_PRINTF(2, 3) = NULL; - - /* Punt applet */ - argc--; - argv++; - - if (strcmp(applet, "eval_ecolors") == 0) { - printf("GOOD='%s'\nWARN='%s'\nBAD='%s'\nHILITE='%s'\nBRACKET='%s'\nNORMAL='%s'\n", - ecolor(ECOLOR_GOOD), - ecolor(ECOLOR_WARN), - ecolor(ECOLOR_BAD), - ecolor(ECOLOR_HILITE), - ecolor(ECOLOR_BRACKET), - ecolor(ECOLOR_NORMAL)); - exit(EXIT_SUCCESS); - } - - if (argc > 0) { - if (strcmp(applet, "eend") == 0 || - strcmp(applet, "ewend") == 0 || - strcmp(applet, "veend") == 0 || - strcmp(applet, "vweend") == 0 || - strcmp(applet, "ewaitfile") == 0) - { - errno = 0; - retval = (int)strtoimax(argv[0], &p, 0); - if (!p || *p != '\0') - errno = EINVAL; - if (errno) - retval = EXIT_FAILURE; - else { - argc--; - argv++; - } - } else if (strcmp(applet, "esyslog") == 0 || - strcmp(applet, "elog") == 0) { - p = strchr(argv[0], '.'); - if (!p || - (level = syslog_decode(p + 1, prioritynames)) == -1) - eerrorx("%s: invalid log level `%s'", applet, argv[0]); - - if (argc < 3) - eerrorx("%s: not enough arguments", applet); - - unsetenv("EINFO_LOG"); - setenv("EINFO_LOG", argv[1], 1); - - argc -= 2; - argv += 2; - } - } - - if (strcmp(applet, "ewaitfile") == 0) { - if (errno) - eerrorx("%s: invalid timeout", applet); - if (argc == 0) - eerrorx("%s: not enough arguments", applet); - - gettimeofday(&stop, NULL); - /* retval stores the timeout */ - stop.tv_sec += retval; - ts.tv_sec = 0; - ts.tv_nsec = WAIT_INTERVAL; - for (i = 0; i < argc; i++) { - ebeginv("Waiting for %s", argv[i]); - for (;;) { - if (exists(argv[i])) - break; - if (nanosleep(&ts, NULL) == -1) - return EXIT_FAILURE; - gettimeofday(&now, NULL); - if (retval <= 0) - continue; - if (timercmp(&now, &stop, <)) - continue; - eendv(EXIT_FAILURE, - "timed out waiting for %s", argv[i]); - return EXIT_FAILURE; - } - eendv(EXIT_SUCCESS, NULL); - } - return EXIT_SUCCESS; - } - - if (argc > 0) { - for (i = 0; i < argc; i++) - l += strlen(argv[i]) + 1; - - message = xmalloc(l); - p = message; - - for (i = 0; i < argc; i++) { - if (i > 0) - *p++ = ' '; - l = strlen(argv[i]); - memcpy(p, argv[i], l); - p += l; - } - *p = 0; - } - - if (strcmp(applet, "einfo") == 0) - e = einfo; - else if (strcmp(applet, "einfon") == 0) - e = einfon; - else if (strcmp(applet, "ewarn") == 0) - e = ewarn; - else if (strcmp(applet, "ewarnn") == 0) - e = ewarnn; - else if (strcmp(applet, "eerror") == 0) { - e = eerror; - retval = 1; - } else if (strcmp(applet, "eerrorn") == 0) { - e = eerrorn; - retval = 1; - } else if (strcmp(applet, "ebegin") == 0) - e = ebegin; - else if (strcmp(applet, "eend") == 0) - ee = eend; - else if (strcmp(applet, "ewend") == 0) - ee = ewend; - else if (strcmp(applet, "esyslog") == 0) { - elog(retval, "%s", message); - retval = 0; - } else if (strcmp(applet, "veinfo") == 0) - e = einfov; - else if (strcmp(applet, "veinfon") == 0) - e = einfovn; - else if (strcmp(applet, "vewarn") == 0) - e = ewarnv; - else if (strcmp(applet, "vewarnn") == 0) - e = ewarnvn; - else if (strcmp(applet, "vebegin") == 0) - e = ebeginv; - else if (strcmp(applet, "veend") == 0) - ee = eendv; - else if (strcmp(applet, "vewend") == 0) - ee = ewendv; - else if (strcmp(applet, "eindent") == 0) - eindent(); - else if (strcmp(applet, "eoutdent") == 0) - eoutdent(); - else if (strcmp(applet, "veindent") == 0) - eindentv(); - else if (strcmp(applet, "veoutdent") == 0) - eoutdentv(); - else { - eerror("%s: unknown applet", applet); - retval = EXIT_FAILURE; - } - - if (message) { - if (e) - e("%s", message); - else if (ee) - ee(retval, "%s", message); - } else { - if (e) - e(NULL); - else if (ee) - ee(retval, NULL); - } - - free(message); - return retval; -} - -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, }, -}; - -static 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; -} - -static int -do_service(int argc, char **argv) -{ - bool ok = false; - char *service; - char *exec; - int idx; - RC_SERVICE state, bit; - - if (argc > 1) - service = argv[1]; - else - service = getenv("RC_SVCNAME"); - - if (service == NULL || *service == '\0') - eerrorx("%s: no service specified", applet); - - state = rc_service_state(service); - bit = lookup_service_state(applet); - if (bit) { - ok = (state & bit); - } else if (strcmp(applet, "service_started_daemon") == 0) { - service = getenv("RC_SVCNAME"); - exec = argv[1]; - if (argc > 3) { - service = argv[1]; - exec = argv[2]; - sscanf(argv[3], "%d", &idx); - } else if (argc == 3) { - if (sscanf(argv[2], "%d", &idx) != 1) { - service = argv[1]; - exec = argv[2]; - } - } - ok = rc_service_started_daemon(service, exec, NULL, idx); - - } else if (strcmp(applet, "service_crashed") == 0) { - ok = (_rc_can_find_pids() && - rc_service_daemons_crashed(service) && - errno != EACCES); - } else - eerrorx("%s: unknown applet", applet); - - return ok ? EXIT_SUCCESS : EXIT_FAILURE; -} - -static int -do_mark_service(int argc, char **argv) -{ - bool ok = false; - char *svcname = getenv("RC_SVCNAME"); - char *service = NULL; - char *runscript_pid; - /* char *mtime; */ - pid_t pid; - RC_SERVICE bit; - /* size_t l; */ - - if (argc > 1) - service = argv[1]; - else - service = svcname; - - if (service == NULL || *service == '\0') - eerrorx("%s: no service specified", applet); - - if (!strncmp(applet, "mark_", 5) && - (bit = lookup_service_state(applet + 5))) - ok = rc_service_mark(service, bit); - else - eerrorx("%s: unknown applet", applet); - - /* If we're marking ourselves then we need to inform our parent - runscript process so they do not mark us based on our exit code */ - /* - * FIXME: svcname and service are almost always equal except called from a - * shell with just argv[1] - So that doesn't seem to do what Roy initially - * expected. - * See 20120424041423.GA23657@odin.qasl.de (Tue, 24 Apr 2012 06:14:23 +0200, - * openrc@gentoo.org). - */ - if (ok && svcname && strcmp(svcname, service) == 0) { - runscript_pid = getenv("RC_RUNSCRIPT_PID"); - if (runscript_pid && sscanf(runscript_pid, "%d", &pid) == 1) - if (kill(pid, SIGHUP) != 0) - eerror("%s: failed to signal parent %d: %s", - applet, pid, strerror(errno)); - - /* Remove the exclusive time test. This ensures that it's not - in control as well */ - /* - l = strlen(RC_SVCDIR "/exclusive") + strlen(svcname) + - strlen(runscript_pid) + 4; - mtime = xmalloc(l); - snprintf(mtime, l, RC_SVCDIR "/exclusive/%s.%s", - svcname, runscript_pid); - if (exists(mtime) && unlink(mtime) != 0) - eerror("%s: unlink: %s", applet, strerror(errno)); - free(mtime); - */ - } - - return ok ? EXIT_SUCCESS : EXIT_FAILURE; -} - -static int -do_value(int argc, char **argv) -{ - bool ok = false; - char *service = getenv("RC_SVCNAME"); - char *option; - - if (service == NULL) - eerrorx("%s: no service specified", applet); - - if (argc < 2 || ! argv[1] || *argv[1] == '\0') - eerrorx("%s: no option specified", applet); - - if (strcmp(applet, "service_get_value") == 0 || - strcmp(applet, "get_options") == 0) - { - option = rc_service_value_get(service, argv[1]); - if (option) { - printf("%s", option); - free(option); - ok = true; - } - } else if (strcmp(applet, "service_set_value") == 0 || - strcmp(applet, "save_options") == 0) - ok = rc_service_value_set(service, argv[1], argv[2]); - else - eerrorx("%s: unknown applet", applet); - - return ok ? EXIT_SUCCESS : EXIT_FAILURE; -} - -static int -shell_var(int argc, char **argv) -{ - int i; - char *p; - int c; - - for (i = 1; i < argc; i++) { - p = argv[i]; - if (i != 1) - putchar(' '); - while (*p) { - c = (unsigned char)*p++; - if (! isalnum(c)) - c = '_'; - putchar(c); - } - } - putchar('\n'); - return EXIT_SUCCESS; -} - -static int -is_older_than(int argc, char **argv) -{ - int i; - - if (argc < 3) - return EXIT_FAILURE; - - /* This test is perverted - historically the baselayout function - * returns 0 on *failure*, which is plain wrong */ - for (i = 2; i < argc; ++i) - if (!rc_newer_than(argv[1], argv[i], NULL, NULL)) - return EXIT_SUCCESS; - - return EXIT_FAILURE; -} - -static int -is_newer_than(int argc, char **argv) -{ - int i; - - if (argc < 3) - return EXIT_FAILURE; - - /* This test is correct as it's not present in baselayout */ - for (i = 2; i < argc; ++i) - if (!rc_newer_than(argv[1], argv[i], NULL, NULL)) - return EXIT_FAILURE; - - return EXIT_SUCCESS; -} - -static int -is_runlevel_start(_unused int argc, _unused char **argv) -{ - return rc_runlevel_starting() ? 0 : 1; -} - -static int -is_runlevel_stop(_unused int argc, _unused char **argv) -{ - return rc_runlevel_stopping() ? 0 : 1; -} - -static int -rc_abort(_unused int argc, _unused char **argv) -{ - const char *p = getenv("RC_PID"); - int pid; - - if (p && sscanf(p, "%d", &pid) == 1) { - if (kill(pid, SIGUSR1) != 0) - eerrorx("rc-abort: failed to signal parent %d: %s", - pid, strerror(errno)); - return EXIT_SUCCESS; - } - - return EXIT_FAILURE; -} - -static const struct { - const char * const name; - int (* const applet)(int argc, char **argv); -} applets[] = { -#define A(a) { #a, a } - A(fstabinfo), - A(mountinfo), - { "openrc-run", openrc_run, }, - { "rc-depend", rc_depend, }, - { "rc-service", rc_service, }, - { "rc-status", rc_status, }, - { "rc-update", rc_update, }, - { "service", rc_service, }, - { "update-rc", rc_update, }, - A(runscript), - { "start-stop-daemon", start_stop_daemon, }, - A(checkpath), - A(swclock), - A(shell_var), - A(is_older_than), - A(is_newer_than), - A(is_runlevel_start), - A(is_runlevel_stop), - { "rc-abort", rc_abort, }, - /* These are purely for init scripts and do not make sense as - * anything else */ - { "service_get_value", do_value, }, - { "service_set_value", do_value, }, - { "get_options", do_value, }, - { "save_options", do_value, }, -#undef A -}; - -void -run_applets(int argc, char **argv) -{ - size_t i; - - /* - * The "rc" applet is deprecated and should be referred to as - * "openrc", so output a warning. - */ - if (strcmp(applet, "rc") == 0) - ewarnv("The 'rc' applet is deprecated; please use 'openrc' instead."); - /* Bug 351712: We need an extra way to explicitly select an applet OTHER - * than trusting argv[0], as argv[0] is not going to be the applet value if - * we are doing SELinux context switching. For this, we allow calls such as - * 'rc --applet APPLET', and shift ALL of argv down by two array items. */ - if ((strcmp(applet, "rc") == 0 || strcmp(applet, "openrc") == 0) && - argc >= 3 && - (strcmp(argv[1],"--applet") == 0 || strcmp(argv[1], "-a") == 0)) { - applet = argv[2]; - argv += 2; - argc -= 2; - } - - for (i = 0; i < ARRAY_SIZE(applets); ++i) - if (!strcmp(applet, applets[i].name)) - exit(applets[i].applet(argc, argv)); - - if (applet[0] == 'e' || (applet[0] == 'v' && applet[1] == 'e')) - exit(do_e(argc, argv)); - - if (strncmp(applet, "service_", strlen("service_")) == 0) - exit(do_service(argc, argv)); - - if (strncmp(applet, "mark_service_", strlen("mark_service_")) == 0) - exit(do_mark_service(argc, argv)); - - if (strcmp(applet, "rc") != 0 && strcmp(applet, "openrc") != 0) - eerrorx("%s: unknown applet", applet); -} diff --git a/src/rc/rc-depend.c b/src/rc/rc-depend.c deleted file mode 100644 index 8e7e3883..00000000 --- a/src/rc/rc-depend.c +++ /dev/null @@ -1,244 +0,0 @@ -/* - rc-depend - rc service dependency and ordering -*/ - -/* - * Copyright (c) 2007-2009 Roy Marples <roy@marples.name> - * - * 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. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. - */ - -#include <sys/stat.h> -#include <sys/time.h> -#include <sys/types.h> - -#include <errno.h> -#include <fcntl.h> -#include <getopt.h> -#include <limits.h> -#include <stdbool.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <time.h> -#include <unistd.h> -#include <utime.h> - -#include "builtins.h" -#include "einfo.h" -#include "queue.h" -#include "rc.h" -#include "rc-misc.h" - -extern const char *applet; - -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) { - stat(RC_DEPTREE_CACHE, &st); - 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(); -} - -#include "_usage.h" -#define getoptstring "aot:suTF:" getoptstring_COMMON -static const struct option longopts[] = { - { "starting", 0, NULL, 'a'}, - { "stopping", 0, NULL, 'o'}, - { "type", 1, NULL, 't'}, - { "notrace", 0, NULL, 'T'}, - { "strict", 0, NULL, 's'}, - { "update", 0, NULL, 'u'}, - { "deptree-file", 1, NULL, 'F'}, - longopts_COMMON -}; -static const char * const longopts_help[] = { - "Order services as if runlevel is starting", - "Order services as if runlevel is stopping", - "Type(s) of dependency to list", - "Don't trace service dependencies", - "Only use what is in the runlevels", - "Force an update of the dependency tree", - "File to load cached deptree from", - longopts_help_COMMON -}; -#include "_usage.c" - -int -rc_depend(int argc, char **argv) -{ - RC_STRINGLIST *list; - RC_STRINGLIST *types; - RC_STRINGLIST *services; - RC_STRINGLIST *depends; - RC_STRING *s; - RC_DEPTREE *deptree = NULL; - int options = RC_DEP_TRACE, update = 0; - bool first = true; - char *runlevel = xstrdup(getenv("RC_RUNLEVEL")); - int opt; - char *token; - char *deptree_file = NULL; - - types = rc_stringlist_new(); - while ((opt = getopt_long(argc, argv, getoptstring, - longopts, (int *) 0)) != -1) - { - switch (opt) { - case 'a': - options |= RC_DEP_START; - break; - case 'o': - options |= RC_DEP_STOP; - break; - case 's': - options |= RC_DEP_STRICT; - break; - case 't': - while ((token = strsep(&optarg, ","))) - rc_stringlist_add(types, token); - break; - case 'u': - update = 1; - break; - case 'T': - options &= RC_DEP_TRACE; - break; - case 'F': - deptree_file = xstrdup(optarg); - break; - - case_RC_COMMON_GETOPT - } - } - - if (deptree_file) { - if (!(deptree = rc_deptree_load_file(deptree_file))) - eerrorx("failed to load deptree"); - } else { - if (!(deptree = _rc_deptree_load(update, NULL))) - eerrorx("failed to load deptree"); - } - - if (!runlevel) - runlevel = rc_runlevel_get(); - - services = rc_stringlist_new(); - while (optind < argc) { - list = rc_stringlist_new(); - rc_stringlist_add(list, argv[optind]); - errno = 0; - depends = rc_deptree_depends(deptree, NULL, list, runlevel, 0); - if (!depends && errno == ENOENT) - eerror("no dependency info for service `%s'", - argv[optind]); - else - rc_stringlist_add(services, argv[optind]); - - rc_stringlist_free(depends); - rc_stringlist_free(list); - optind++; - } - if (!TAILQ_FIRST(services)) { - rc_stringlist_free(services); - rc_stringlist_free(types); - rc_deptree_free(deptree); - free(runlevel); - if (update) - return EXIT_SUCCESS; - eerrorx("no services specified"); - } - - /* If we don't have any types, then supply some defaults */ - if (!TAILQ_FIRST(types)) { - rc_stringlist_add(types, "ineed"); - rc_stringlist_add(types, "iuse"); - } - - depends = rc_deptree_depends(deptree, types, services, - runlevel, options); - - if (TAILQ_FIRST(depends)) { - TAILQ_FOREACH(s, depends, entries) { - if (first) - first = false; - else - printf (" "); - printf ("%s", s->value); - - } - printf ("\n"); - } - - rc_stringlist_free(types); - rc_stringlist_free(services); - rc_stringlist_free(depends); - rc_deptree_free(deptree); - free(runlevel); - return EXIT_SUCCESS; -} diff --git a/src/rc/rc-logger.c b/src/rc/rc-logger.c deleted file mode 100644 index e72f1818..00000000 --- a/src/rc/rc-logger.c +++ /dev/null @@ -1,320 +0,0 @@ -/* - rc-logger.c - Spawns a logging daemon to capture stdout and stderr so we can log - them to a buffer and/or files. -*/ - -/* - * Copyright (c) 2007-2008 Roy Marples <roy@marples.name> - * - * 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. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. - */ - -#include <sys/types.h> -#include <sys/ioctl.h> -#include <sys/wait.h> - -#include <ctype.h> -#include <fcntl.h> -#include <poll.h> -#include <signal.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <termios.h> -#include <time.h> -#include <unistd.h> - -#if defined(__linux__) || (defined(__FreeBSD_kernel__) && defined(__GLIBC__)) -# include <pty.h> -#elif defined(__NetBSD__) || defined(__OpenBSD__) -# include <util.h> -#else -# include <libutil.h> -#endif - -#include "einfo.h" -#include "rc-logger.h" -#include "queue.h" -#include "rc.h" -#include "rc-misc.h" - -#define TMPLOG RC_SVCDIR "/rc.log" -#define DEFAULTLOG "/var/log/rc.log" - -static int signal_pipe[2] = { -1, -1 }; -static int fd_stdout = -1; -static int fd_stderr = -1; -static const char *runlevel = NULL; -static bool in_escape = false; -static bool in_term = false; - -static char *logbuf = NULL; -static size_t logbuf_size = 0; -static size_t logbuf_len = 0; - -pid_t rc_logger_pid = -1; -int rc_logger_tty = -1; -bool rc_in_logger = false; - -static void -write_log(int logfd, const char *buffer, size_t bytes) -{ - const char *p = buffer; - - while ((size_t)(p - buffer) < bytes) { - switch (*p) { - case '\r': - goto cont; - case '\033': - in_escape = true; - in_term = false; - goto cont; - case '\n': - in_escape = in_term = false; - break; - case '[': - if (in_escape) - in_term = true; - break; - } - - if (!in_escape) { - if (write(logfd, p++, 1) == -1) - eerror("write: %s", strerror(errno)); - continue; - } - - if (! in_term || isalpha((unsigned char)*p)) - in_escape = in_term = false; -cont: - p++; - } -} - -static void -write_time(FILE *f, const char *s) -{ - time_t now = time(NULL); - struct tm *tm = localtime(&now); - - fprintf(f, "\nrc %s logging %s at %s\n", runlevel, s, asctime(tm)); - fflush(f); -} - -void -rc_logger_close(void) -{ - int sig = SIGTERM; - - if (signal_pipe[1] > -1) { - if (write(signal_pipe[1], &sig, sizeof(sig)) == -1) - eerror("write: %s", strerror(errno)); - close(signal_pipe[1]); - signal_pipe[1] = -1; - } - - if (rc_logger_pid > 0) - waitpid(rc_logger_pid, 0, 0); - - if (fd_stdout > -1) - dup2(fd_stdout, STDOUT_FILENO); - if (fd_stderr > -1) - dup2(fd_stderr, STDERR_FILENO); -} - -void -rc_logger_open(const char *level) -{ - int slave_tty; - struct termios tt; - struct winsize ws; - char buffer[BUFSIZ]; - struct pollfd fd[2]; - int s = 0; - size_t bytes; - int i; - FILE *log = NULL; - FILE *plog = NULL; - const char *logfile; - int log_error = 0; - - if (!rc_conf_yesno("rc_logger")) - return; - - if (pipe(signal_pipe) == -1) - eerrorx("pipe: %s", strerror(errno)); - for (i = 0; i < 2; i++) - if ((s = fcntl (signal_pipe[i], F_GETFD, 0) == -1 || - fcntl (signal_pipe[i], F_SETFD, s | FD_CLOEXEC) == -1)) - eerrorx("fcntl: %s", strerror (errno)); - - if (isatty(STDOUT_FILENO)) { - tcgetattr(STDOUT_FILENO, &tt); - ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws); - if (openpty(&rc_logger_tty, &slave_tty, NULL, &tt, &ws)) - return; - } else - if (openpty(&rc_logger_tty, &slave_tty, NULL, NULL, NULL)) - return; - - if ((s = fcntl(rc_logger_tty, F_GETFD, 0)) == 0) - fcntl(rc_logger_tty, F_SETFD, s | FD_CLOEXEC); - - if ((s = fcntl(slave_tty, F_GETFD, 0)) == 0) - fcntl(slave_tty, F_SETFD, s | FD_CLOEXEC); - - rc_logger_pid = fork(); - switch (rc_logger_pid) { - case -1: - eerror("fork: %s", strerror(errno)); - break; - case 0: - rc_in_logger = true; - close(signal_pipe[1]); - signal_pipe[1] = -1; - - runlevel = level; - if ((log = fopen(TMPLOG, "ae"))) - write_time(log, "started"); - else { - free(logbuf); - logbuf_size = BUFSIZ * 10; - logbuf = xmalloc(sizeof (char) * logbuf_size); - logbuf_len = 0; - } - - fd[0].fd = signal_pipe[0]; - fd[0].events = fd[1].events = POLLIN; - fd[0].revents = fd[1].revents = 0; - if (rc_logger_tty >= 0) - fd[1].fd = rc_logger_tty; - for (;;) { - if ((s = poll(fd, - rc_logger_tty >= 0 ? 2 : 1, -1)) == -1) - { - eerror("poll: %s", strerror(errno)); - break; - } else if (s == 0) - continue; - - if (fd[1].revents & (POLLIN | POLLHUP)) { - memset(buffer, 0, BUFSIZ); - bytes = read(rc_logger_tty, buffer, BUFSIZ); - if (write(STDOUT_FILENO, buffer, bytes) == -1) - eerror("write: %s", strerror(errno)); - - if (log) - write_log(fileno (log), buffer, bytes); - else { - if (logbuf_size - logbuf_len < bytes) { - logbuf_size += BUFSIZ * 10; - logbuf = xrealloc(logbuf, - sizeof(char ) * - logbuf_size); - } - - memcpy(logbuf + logbuf_len, - buffer, bytes); - logbuf_len += bytes; - } - } - - /* Only SIGTERMS signals come down this pipe */ - if (fd[0].revents & (POLLIN | POLLHUP)) - break; - } - if (logbuf) { - if ((log = fopen(TMPLOG, "ae"))) { - write_time(log, "started"); - write_log(fileno(log), logbuf, logbuf_len); - } - free(logbuf); - } - if (log) { - write_time(log, "stopped"); - fclose(log); - } - - /* Append the temporary log to the real log */ - logfile = rc_conf_value("rc_log_path"); - if (logfile == NULL) - logfile = DEFAULTLOG; - - if ((plog = fopen(logfile, "ae"))) { - if ((log = fopen(TMPLOG, "re"))) { - while ((bytes = fread(buffer, sizeof(*buffer), BUFSIZ, log)) > 0) { - if (fwrite(buffer, sizeof(*buffer), bytes, plog) < bytes) { - log_error = 1; - eerror("Error: write(%s) failed: %s", logfile, strerror(errno)); - break; - } - } - } else { - log_error = 1; - eerror("Error: fopen(%s) failed: %s", TMPLOG, strerror(errno)); - } - - fclose(log); - fclose(plog); - } else { - /* - * logfile or its basedir may be read-only during sysinit and - * shutdown so skip the error in this case - */ - if (errno != EROFS && ((strcmp(level, RC_LEVEL_SHUTDOWN) != 0) && (strcmp(level, RC_LEVEL_SYSINIT) != 0))) { - log_error = 1; - eerror("Error: fopen(%s) failed: %s", logfile, strerror(errno)); - } - } - - /* Try to keep the temporary log in case of errors */ - if (!log_error) { - if (errno != EROFS && ((strcmp(level, RC_LEVEL_SHUTDOWN) != 0) && (strcmp(level, RC_LEVEL_SYSINIT) != 0))) - if (unlink(TMPLOG) == -1) - eerror("Error: unlink(%s) failed: %s", TMPLOG, strerror(errno)); - } else if (exists(TMPLOG)) - eerrorx("Warning: temporary logfile left behind: %s", TMPLOG); - - exit(0); - /* NOTREACHED */ - - default: - setpgid(rc_logger_pid, 0); - fd_stdout = dup(STDOUT_FILENO); - fd_stderr = dup(STDERR_FILENO); - if ((s = fcntl(fd_stdout, F_GETFD, 0)) == 0) - fcntl(fd_stdout, F_SETFD, s | FD_CLOEXEC); - - if ((s = fcntl(fd_stderr, F_GETFD, 0)) == 0) - fcntl(fd_stderr, F_SETFD, s | FD_CLOEXEC); - dup2(slave_tty, STDOUT_FILENO); - dup2(slave_tty, STDERR_FILENO); - if (slave_tty != STDIN_FILENO && - slave_tty != STDOUT_FILENO && - slave_tty != STDERR_FILENO) - close(slave_tty); - close(signal_pipe[0]); - signal_pipe[0] = -1; - break; - } -} diff --git a/src/rc/rc-logger.h b/src/rc/rc-logger.h deleted file mode 100644 index 8c0cc2a8..00000000 --- a/src/rc/rc-logger.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2007-2008 Roy Marples <roy@marples.name> - * - * 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. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. - */ - -#ifndef RC_LOGGER_H -#define RC_LOGGER_H - -pid_t rc_logger_pid; -int rc_logger_tty; -extern bool rc_in_logger; - -void rc_logger_open(const char *runlevel); -void rc_logger_close(void); - -#endif diff --git a/src/rc/rc-misc.c b/src/rc/rc-misc.c deleted file mode 100644 index f187158a..00000000 --- a/src/rc/rc-misc.c +++ /dev/null @@ -1,347 +0,0 @@ -/* - librc-misc.c - rc misc functions -*/ - -/* - * Copyright (c) 2007-2008 Roy Marples <roy@marples.name> - * - * 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. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. - */ - -#include <sys/file.h> -#include <sys/types.h> -#include <sys/utsname.h> - -#ifdef __linux__ -# include <sys/sysinfo.h> -# include <regex.h> -#endif - -#include <ctype.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 "rc-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[] = { - "CONSOLE", "PATH", "SHELL", "USER", "HOME", "TERM", - "LANG", "LC_CTYPE", "LC_NUMERIC", "LC_TIME", "LC_COLLATE", - "LC_MONETARY", "LC_MESSAGES", "LC_PAPER", "LC_NAME", "LC_ADDRESS", - "LC_TELEPHONE", "LC_MEASUREMENT", "LC_IDENTIFICATION", "LC_ALL", - "IN_HOTPLUG", "IN_BACKGROUND", "RC_INTERFACE_KEEP_CONFIG", - 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"), " "); - 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); - } - -#ifdef DEBUG_MEMORY - rc_stringlist_free(env_list); - rc_stringlist_free(env_allow); - rc_stringlist_free(profile); -#endif -} - -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[PATH_MAX]; - - /* 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"))) { - memset(buffer, 0, sizeof (buffer)); - if (fgets(buffer, sizeof (buffer), fp)) { - 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); - - 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 -svc_lock(const char *applet) -{ - char file[PATH_MAX]; - int fd; - - snprintf(file, sizeof(file), RC_SVCDIR "/exclusive/%s", applet); - fd = open(file, O_WRONLY | O_CREAT | O_NONBLOCK, 0664); - if (fd == -1) - return -1; - if (flock(fd, LOCK_EX | LOCK_NB) == -1) { - close(fd); - return -1; - } - return fd; -} - -int -svc_unlock(const char *applet, int fd) -{ - char file[PATH_MAX]; - - snprintf(file, sizeof(file), RC_SVCDIR "/exclusive/%s", applet); - close(fd); - unlink(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; -} diff --git a/src/rc/rc-plugin.c b/src/rc/rc-plugin.c deleted file mode 100644 index d981afd8..00000000 --- a/src/rc/rc-plugin.c +++ /dev/null @@ -1,257 +0,0 @@ -/* - librc-plugin.c - Simple plugin handler -*/ - -/* - * Copyright (c) 2007-2008 Roy Marples <roy@marples.name> - * - * 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. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. - */ - -#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 "rc-misc.h" -#include "rc-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[PATH_MAX]; - 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; - - snprintf(file, sizeof(file), RC_PLUGINDIR "/%s", d->d_name); - h = dlopen(file, RTLD_LAZY); - 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/rc/rc-plugin.h b/src/rc/rc-plugin.h deleted file mode 100644 index b4e40ab4..00000000 --- a/src/rc/rc-plugin.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - librc-plugin.h - Private instructions to use plugins - */ - -/* - * Copyright (c) 2007-2008 Roy Marples <roy@marples.name> - * - * 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. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. - */ - -#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/rc/rc-selinux.c b/src/rc/rc-selinux.c deleted file mode 100644 index 7124e83e..00000000 --- a/src/rc/rc-selinux.c +++ /dev/null @@ -1,399 +0,0 @@ -/* - * rc-selinux.c - * SELinux helpers to get and set contexts. - */ - -/* - * Copyright (c) 2014 Jason Zaman <jason@perfinion.com> - * - * 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. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. - */ - -#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 "rc-misc.h" -#include "rc-plugin.h" -#include "rc-selinux.h" - -/* the context files for selinux */ -#define RUN_INIT_FILE "run_init_type" -#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; - security_context_t 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[PATH_MAX]; - char *line = NULL; - char *p; - char *p2; - size_t len = 0; - ssize_t read; - - memset(filepath, '\0', PATH_MAX); - snprintf(filepath, PATH_MAX - 1, "%s/%s", selinux_contexts_path(), filename); - - fp = fopen(filepath, "r"); - if (fp == NULL) { - eerror("Failed to open context file: %s", filename); - 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); - fclose(fp); - 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_context_file(RUN_INIT_FILE, &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); - curr_t = xstrdup(context_type_get(curr_con)); - /* 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/rc/rc-selinux.h b/src/rc/rc-selinux.h deleted file mode 100644 index e28f3339..00000000 --- a/src/rc/rc-selinux.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2014 Jason Zaman <jason@perfinion.com> - * - * 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. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. - */ - -#ifndef RC_SELINUX_UTIL_H -#define RC_SELINUX_UTIL_H - -int selinux_util_open(void); -int selinux_util_label(const char *path); -int selinux_util_close(void); - -void selinux_setup(char **argv); - -#endif diff --git a/src/rc/rc-service.c b/src/rc/rc-service.c deleted file mode 100644 index ff725cd8..00000000 --- a/src/rc/rc-service.c +++ /dev/null @@ -1,135 +0,0 @@ -/* - rc-service.c - Finds all OpenRC services -*/ - -/* - * Copyright (c) 2008 Roy Marples <roy@marples.name> - * - * 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. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. - */ - -#include <getopt.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -#include "builtins.h" -#include "einfo.h" -#include "queue.h" -#include "rc.h" -#include "rc-misc.h" - -extern char *applet; - -#include "_usage.h" -#define usagestring "" \ - "Usage: rc-service [options] [-i] <service> <cmd>...\n" \ - " or: rc-service [options] -e <service>\n" \ - " or: rc-service [options] -l\n" \ - " or: rc-service [options] -r <service>" -#define getoptstring "e:ilr:" getoptstring_COMMON -static const struct option longopts[] = { - { "exists", 1, NULL, 'e' }, - { "ifexists", 0, NULL, 'i' }, - { "list", 0, NULL, 'l' }, - { "resolve", 1, NULL, 'r' }, - longopts_COMMON -}; -static const char * const longopts_help[] = { - "tests if the service exists or not", - "if the service exists then run the command", - "list all available services", - "resolve the service name to an init script", - longopts_help_COMMON -}; -#include "_usage.c" - -int -rc_service(int argc, char **argv) -{ - int opt; - char *service; - RC_STRINGLIST *list; - RC_STRING *s; - bool if_exists = false; - - /* Ensure that we are only quiet when explicitly told to be */ - unsetenv("EINFO_QUIET"); - - while ((opt = getopt_long(argc, argv, getoptstring, - longopts, (int *) 0)) != -1) - { - switch (opt) { - case 'e': - service = rc_service_resolve(optarg); - opt = service ? EXIT_SUCCESS : EXIT_FAILURE; -#ifdef DEBUG_MEMORY - free(service); -#endif - return opt; - /* NOTREACHED */ - case 'i': - if_exists = true; - break; - case 'l': - list = rc_services_in_runlevel(NULL); - if (TAILQ_FIRST(list) == NULL) - return EXIT_FAILURE; - rc_stringlist_sort(&list); - TAILQ_FOREACH(s, list, entries) - printf("%s\n", s->value); -#ifdef DEBUG_MEMORY - rc_stringlist_free(list); -#endif - return EXIT_SUCCESS; - /* NOTREACHED */ - case 'r': - service = rc_service_resolve(optarg); - if (service == NULL) - return EXIT_FAILURE; - printf("%s\n", service); -#ifdef DEBUG_MEMORY - free(service); -#endif - return EXIT_SUCCESS; - /* NOTREACHED */ - - case_RC_COMMON_GETOPT - } - } - - argc -= optind; - argv += optind; - if (*argv == NULL) - eerrorx("%s: you need to specify a service", applet); - if ((service = rc_service_resolve(*argv)) == NULL) { - if (if_exists) - return 0; - eerrorx("%s: service `%s' does not exist", applet, *argv); - } - *argv = service; - execv(*argv, argv); - eerrorx("%s: %s", applet, strerror(errno)); - /* NOTREACHED */ -} diff --git a/src/rc/rc-status.c b/src/rc/rc-status.c deleted file mode 100644 index 1f67b756..00000000 --- a/src/rc/rc-status.c +++ /dev/null @@ -1,388 +0,0 @@ -/* - rc-status - Display the status of the services in runlevels - */ - -/* - * Copyright (c) 2007-2009 Roy Marples <roy@marples.name> - * - * 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. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. - */ - -#include <getopt.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -#include "builtins.h" -#include "einfo.h" -#include "queue.h" -#include "rc.h" -#include "rc-misc.h" - -extern const char *applet; -static bool test_crashed = false; -static RC_DEPTREE *deptree; -static RC_STRINGLIST *types; - -static RC_STRINGLIST *levels, *services, *tmp, *alist; -static RC_STRINGLIST *sservices, *nservices, *needsme; - -bool -_rc_can_find_pids(void) -{ - RC_PIDLIST *pids; - RC_PID *pid; - RC_PID *pid2; - bool retval = false; - - if (geteuid() == 0) - return true; - - /* If we cannot see process 1, then we don't test to see if - * services crashed or not */ - pids = rc_find_pids(NULL, NULL, 0, 1); - if (pids) { - pid = LIST_FIRST(pids); - if (pid) { - retval = true; - while (pid) { - pid2 = LIST_NEXT(pid, entries); - free(pid); - pid = pid2; - } - } - free(pids); - } - return retval; -} - -static void -print_level(const char *prefix, const char *level) -{ - if (prefix) - printf("%s ", prefix); - printf ("Runlevel: "); - if (isatty(fileno(stdout))) - printf("%s%s%s\n", - ecolor(ECOLOR_HILITE), - level, - ecolor(ECOLOR_NORMAL)); - else - printf("%s\n", level); -} - -static void -print_service(const char *service) -{ - char status[10]; - int cols = printf(" %s", service); - const char *c = ecolor(ECOLOR_GOOD); - RC_SERVICE state = rc_service_state(service); - ECOLOR color = ECOLOR_BAD; - - if (state & RC_SERVICE_STOPPING) - snprintf(status, sizeof(status), "stopping "); - else if (state & RC_SERVICE_STARTING) { - snprintf(status, sizeof(status), "starting "); - color = ECOLOR_WARN; - } else if (state & RC_SERVICE_INACTIVE) { - snprintf(status, sizeof(status), "inactive "); - color = ECOLOR_WARN; - } else if (state & RC_SERVICE_STARTED) { - errno = 0; - if (test_crashed && - rc_service_daemons_crashed(service) && - errno != EACCES) - { - snprintf(status, sizeof(status), " crashed "); - } else { - snprintf(status, sizeof(status), " started "); - color = ECOLOR_GOOD; - } - } else if (state & RC_SERVICE_SCHEDULED) { - snprintf(status, sizeof(status), "scheduled"); - color = ECOLOR_WARN; - } else - snprintf(status, sizeof(status), " stopped "); - - errno = 0; - if (c && *c && isatty(fileno(stdout))) - printf("\n"); - ebracket(cols, color, status); -} - -static void -print_services(const char *runlevel, RC_STRINGLIST *svcs) -{ - RC_STRINGLIST *l = NULL; - RC_STRING *s; - char *r = NULL; - - if (!svcs) - return; - if (!deptree) - deptree = _rc_deptree_load(0, NULL); - if (!deptree) { - TAILQ_FOREACH(s, svcs, entries) - if (!runlevel || - rc_service_in_runlevel(s->value, runlevel)) - print_service(s->value); - return; - } - if (!types) { - types = rc_stringlist_new(); - rc_stringlist_add(types, "ineed"); - rc_stringlist_add(types, "iuse"); - rc_stringlist_add(types, "iafter"); - } - if (!runlevel) - r = rc_runlevel_get(); - l = rc_deptree_depends(deptree, types, svcs, r ? r : runlevel, - RC_DEP_STRICT | RC_DEP_TRACE | RC_DEP_START); - free(r); - if (!l) - return; - TAILQ_FOREACH(s, l, entries) { - if (!rc_stringlist_find(svcs, s->value)) - continue; - if (!runlevel || rc_service_in_runlevel(s->value, runlevel)) - print_service(s->value); - } - rc_stringlist_free(l); -} - -static void -print_stacked_services(const char *runlevel) -{ - RC_STRINGLIST *stackedlevels, *servicelist; - RC_STRING *stackedlevel; - - stackedlevels = rc_runlevel_stacks(runlevel); - TAILQ_FOREACH(stackedlevel, stackedlevels, entries) { - if (rc_stringlist_find(levels, stackedlevel->value) != NULL) - continue; - print_level("Stacked", stackedlevel->value); - servicelist = rc_services_in_runlevel(stackedlevel->value); - print_services(stackedlevel->value, servicelist); - rc_stringlist_free(servicelist); - } - rc_stringlist_free(stackedlevels); - stackedlevels = NULL; -} - -#include "_usage.h" -#define usagestring "" \ - "Usage: rc-status [options] <runlevel>...\n" \ - " or: rc-status [options] [-a | -c | -l | -r | -s | -u]" -#define getoptstring "aclrsu" getoptstring_COMMON -static const struct option longopts[] = { - {"all", 0, NULL, 'a'}, - {"crashed", 0, NULL, 'c'}, - {"list", 0, NULL, 'l'}, - {"runlevel", 0, NULL, 'r'}, - {"servicelist", 0, NULL, 's'}, - {"unused", 0, NULL, 'u'}, - longopts_COMMON -}; -static const char * const longopts_help[] = { - "Show services from all run levels", - "Show crashed services", - "Show list of run levels", - "Show the name of the current runlevel", - "Show service list", - "Show services not assigned to any runlevel", - longopts_help_COMMON -}; -#include "_usage.c" - -int -rc_status(int argc, char **argv) -{ - RC_STRING *s, *l, *t, *level; - - char *p, *runlevel = NULL; - int opt, aflag = 0, retval = 0; - - test_crashed = _rc_can_find_pids(); - - while ((opt = getopt_long(argc, argv, getoptstring, longopts, - (int *) 0)) != -1) - switch (opt) { - case 'a': - aflag++; - levels = rc_runlevel_list(); - break; - case 'c': - services = rc_services_in_state(RC_SERVICE_STARTED); - retval = 1; - TAILQ_FOREACH(s, services, entries) - if (rc_service_daemons_crashed(s->value)) { - printf("%s\n", s->value); - retval = 0; - } - goto exit; - /* NOTREACHED */ - case 'l': - levels = rc_runlevel_list(); - TAILQ_FOREACH(l, levels, entries) - printf("%s\n", l->value); - goto exit; - case 'r': - runlevel = rc_runlevel_get(); - printf("%s\n", runlevel); - goto exit; - /* NOTREACHED */ - case 's': - services = rc_services_in_runlevel(NULL); - print_services(NULL, services); - goto exit; - /* NOTREACHED */ - case 'u': - services = rc_services_in_runlevel(NULL); - levels = rc_runlevel_list(); - TAILQ_FOREACH_SAFE(s, services, entries, t) { - TAILQ_FOREACH(l, levels, entries) - if (rc_service_in_runlevel(s->value, l->value)) { - TAILQ_REMOVE(services, s, entries); - free(s->value); - free(s); - break; - } - } - print_services(NULL, services); - goto exit; - /* NOTREACHED */ - - case_RC_COMMON_GETOPT - } - - if (!levels) - levels = rc_stringlist_new(); - opt = (optind < argc) ? 0 : 1; - while (optind < argc) { - if (rc_runlevel_exists(argv[optind])) { - rc_stringlist_add(levels, argv[optind++]); - opt++; - } else - eerror("runlevel `%s' does not exist", argv[optind++]); - } - if (opt == 0) - exit(EXIT_FAILURE); - if (!TAILQ_FIRST(levels)) { - runlevel = rc_runlevel_get(); - rc_stringlist_add(levels, runlevel); - } - - /* Output the services in the order in which they would start */ - deptree = _rc_deptree_load(0, NULL); - - TAILQ_FOREACH(l, levels, entries) { - print_level(NULL, l->value); - services = rc_services_in_runlevel(l->value); - print_services(l->value, services); - print_stacked_services(l->value); - rc_stringlist_free(nservices); - nservices = NULL; - rc_stringlist_free(services); - services = NULL; - } - - if (aflag || argc < 2) { - /* Show hotplugged services */ - print_level("Dynamic", "hotplugged"); - services = rc_services_in_state(RC_SERVICE_HOTPLUGGED); - print_services(NULL, services); - rc_stringlist_free(services); - services = NULL; - - /* Show manually started and unassigned depended services */ - if (aflag) { - rc_stringlist_free(levels); - levels = rc_stringlist_new(); - if (!runlevel) - runlevel = rc_runlevel_get(); - rc_stringlist_add(levels, runlevel); - } - rc_stringlist_add(levels, RC_LEVEL_SYSINIT); - rc_stringlist_add(levels, RC_LEVEL_BOOT); - services = rc_services_in_runlevel(NULL); - sservices = rc_stringlist_new(); - TAILQ_FOREACH(l, levels, entries) { - nservices = rc_services_in_runlevel_stacked(l->value); - TAILQ_CONCAT(sservices, nservices, entries); - free(nservices); - } - TAILQ_FOREACH_SAFE(s, services, entries, t) { - if ((rc_stringlist_find(sservices, s->value) || - (rc_service_state(s->value) & ( RC_SERVICE_STOPPED | RC_SERVICE_HOTPLUGGED)))) { - TAILQ_REMOVE(services, s, entries); - free(s->value); - free(s); - } - } - needsme = rc_stringlist_new(); - rc_stringlist_add(needsme, "needsme"); - nservices = rc_stringlist_new(); - alist = rc_stringlist_new(); - l = rc_stringlist_add(alist, ""); - p = l->value; - TAILQ_FOREACH(level, levels, entries) { - TAILQ_FOREACH_SAFE(s, services, entries, t) { - l->value = s->value; - setenv("RC_SVCNAME", l->value, 1); - tmp = rc_deptree_depends(deptree, needsme, alist, level->value, RC_DEP_TRACE); - if (TAILQ_FIRST(tmp)) { - TAILQ_REMOVE(services, s, entries); - TAILQ_INSERT_TAIL(nservices, s, entries); - } - rc_stringlist_free(tmp); - } - } - l->value = p; - /* - * we are unsetting RC_SVCNAME because last loaded service will not - * be added to the list - */ - unsetenv("RC_SVCNAME"); - print_level("Dynamic", "needed"); - print_services(NULL, nservices); - print_level("Dynamic", "manual"); - print_services(NULL, services); - } - -exit: - free(runlevel); -#ifdef DEBUG_MEMORY - rc_stringlist_free(alist); - rc_stringlist_free(needsme); - rc_stringlist_free(sservices); - rc_stringlist_free(nservices); - rc_stringlist_free(services); - rc_stringlist_free(types); - rc_stringlist_free(levels); - rc_deptree_free(deptree); -#endif - - return retval; -} diff --git a/src/rc/rc-update.c b/src/rc/rc-update.c deleted file mode 100644 index 48bb4dc7..00000000 --- a/src/rc/rc-update.c +++ /dev/null @@ -1,366 +0,0 @@ -/* - rc-update - Manage init scripts and runlevels -*/ - -/* - * Copyright (c) 2007-2009 Roy Marples <roy@marples.name> - * - * 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. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. - */ - -#include <errno.h> -#include <getopt.h> -#include <limits.h> -#include <stdbool.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -#include "builtins.h" -#include "einfo.h" -#include "queue.h" -#include "rc.h" -#include "rc-misc.h" - -extern const char *applet; - -/* Return the number of changes made: - * -1 = no changes (error) - * 0 = no changes (nothing to do) - * 1+ = number of runlevels updated - */ -static int -add(const char *runlevel, const char *service) -{ - int retval = -1; - - if (!rc_service_exists(service)) { - if (errno == ENOEXEC) - eerror("%s: service `%s' is not executeable", - applet, service); - else - eerror("%s: service `%s' does not exist", - applet, service); - } else if (rc_service_in_runlevel(service, runlevel)) { - einfo("%s: %s already installed in runlevel `%s'; skipping", - applet, service, runlevel); - retval = 0; - } else if (rc_service_add(runlevel, service)) { - einfo("service %s added to runlevel %s", service, runlevel); - retval = 1; - } else - eerror("%s: failed to add service `%s' to runlevel `%s': %s", - applet, service, runlevel, strerror (errno)); - - return retval; -} - -static int -delete(const char *runlevel, const char *service) -{ - int retval = -1; - - errno = 0; - if (rc_service_delete(runlevel, service)) { - einfo("service %s removed from runlevel %s", - service, runlevel); - return 1; - } - - if (errno == ENOENT) - eerror("%s: service `%s' is not in the runlevel `%s'", - applet, service, runlevel); - else - eerror("%s: failed to remove service `%s' from runlevel `%s': %s", - applet, service, runlevel, strerror (errno)); - - return retval; -} - -static int -addstack(const char *runlevel, const char *stack) -{ - if (!rc_runlevel_exists(runlevel)) { - eerror("%s: runlevel `%s' does not exist", applet, runlevel); - return -1; - } - if (!rc_runlevel_exists(stack)) { - eerror("%s: runlevel `%s' does not exist", applet, stack); - return -1; - } - if (strcmp(runlevel, stack) == 0) { - eerror("%s: cannot stack `%s' onto itself", applet, stack); - return -1; - } - if (strcmp(runlevel, RC_LEVEL_SYSINIT) == 0 || - strcmp(stack, RC_LEVEL_SYSINIT) == 0 || - strcmp(runlevel, RC_LEVEL_BOOT) == 0 || - strcmp(stack, RC_LEVEL_BOOT) == 0 || - strcmp(runlevel, RC_LEVEL_SINGLE) == 0 || - strcmp(stack, RC_LEVEL_SINGLE) == 0 || - strcmp(runlevel, RC_LEVEL_SHUTDOWN) == 0 || - strcmp(stack, RC_LEVEL_SHUTDOWN) == 0) - { - eerror("%s: cannot stack the %s runlevel", - applet, RC_LEVEL_SYSINIT); - return -1; - } - if (!rc_runlevel_stack(runlevel, stack)) { - eerror("%s: failed to stack `%s' to `%s': %s", - applet, stack, runlevel, strerror(errno)); - return -1; - } - einfo("runlevel %s added to runlevel %s", stack, runlevel); - return 1; -} - -static int -delstack(const char *runlevel, const char *stack) -{ - if (rc_runlevel_unstack(runlevel, stack)) { - einfo("runlevel %s removed from runlevel %s", stack, runlevel); - return 1; - } - - if (errno == ENOENT) - eerror("%s: runlevel `%s' is not in the runlevel `%s'", - applet, stack, runlevel); - else - eerror("%s: failed to remove runlevel `%s' from runlevel `%s': %s", - applet, stack, runlevel, strerror (errno)); - - return -1; -} - -static void -show(RC_STRINGLIST *runlevels, bool verbose) -{ - RC_STRINGLIST *services = rc_services_in_runlevel(NULL); - RC_STRING *service; - RC_STRING *runlevel; - RC_STRINGLIST *in; - bool inone; - char buffer[PATH_MAX]; - size_t l; - - rc_stringlist_sort(&services); - TAILQ_FOREACH(service, services, entries) { - in = rc_stringlist_new(); - inone = false; - - TAILQ_FOREACH(runlevel, runlevels, entries) { - if (rc_service_in_runlevel(service->value, - runlevel->value)) - { - rc_stringlist_add(in, runlevel->value); - inone = true; - } else { - l = strlen(runlevel->value); - memset (buffer, ' ', l); - buffer[l] = 0; - rc_stringlist_add (in, buffer); - } - } - - if (inone || verbose) { - printf(" %20s |", service->value); - TAILQ_FOREACH(runlevel, in, entries) - printf (" %s", runlevel->value); - printf ("\n"); - } - rc_stringlist_free(in); - } - - rc_stringlist_free (services); -} - -#include "_usage.h" -#define usagestring "" \ - "Usage: rc-update [options] add <service> [<runlevel>...]\n" \ - " or: rc-update [options] del <service> [<runlevel>...]\n" \ - " or: rc-update [options] [show [<runlevel>...]]" -#define getoptstring "asu" getoptstring_COMMON -static const struct option longopts[] = { - { "all", 0, NULL, 'a' }, - { "stack", 0, NULL, 's' }, - { "update", 0, NULL, 'u' }, - longopts_COMMON -}; -static const char * const longopts_help[] = { - "Process all runlevels", - "Stack a runlevel instead of a service", - "Force an update of the dependency tree", - longopts_help_COMMON -}; -#include "_usage.c" - -#define DOADD (1 << 1) -#define DODELETE (1 << 2) -#define DOSHOW (1 << 3) - -int -rc_update(int argc, char **argv) -{ - RC_DEPTREE *deptree; - RC_STRINGLIST *runlevels; - RC_STRING *runlevel; - char *service = NULL; - char *p; - int action = 0; - bool verbose = false, stack = false, all_runlevels = false; - int opt; - int retval = EXIT_FAILURE; - int num_updated = 0; - int (*actfunc)(const char *, const char *); - int ret; - - while ((opt = getopt_long(argc, argv, getoptstring, - longopts, (int *)0)) != -1) - switch (opt) { - case 'a': - all_runlevels = true; - break; - case 's': - stack = true; - break; - case 'u': - deptree = _rc_deptree_load(-1, &ret); - if (deptree) - rc_deptree_free(deptree); - return ret; - case_RC_COMMON_GETOPT - } - - verbose = rc_yesno(getenv ("EINFO_VERBOSE")); - - if ((action & DOSHOW && action != DOSHOW) || - (action & DOADD && action != DOADD) || - (action & DODELETE && action != DODELETE)) - eerrorx("%s: cannot mix commands", applet); - - /* We need to be backwards compatible */ - if (optind < argc) { - if (strcmp(argv[optind], "add") == 0) - action = DOADD; - else if (strcmp(argv[optind], "delete") == 0 || - strcmp(argv[optind], "del") == 0) - action = DODELETE; - else if (strcmp(argv[optind], "show") == 0) - action = DOSHOW; - if (action) - optind++; - else - eerrorx("%s: invalid command `%s'", - applet, argv[optind]); - } - if (!action) - action = DOSHOW; - - runlevels = rc_stringlist_new(); - - if (optind >= argc) { - if (!(action & DOSHOW)) - eerrorx("%s: no service specified", applet); - } else { - service = argv[optind]; - optind++; - - while (optind < argc) - if (rc_runlevel_exists(argv[optind])) - rc_stringlist_add(runlevels, argv[optind++]); - else { - rc_stringlist_free(runlevels); - eerrorx ("%s: `%s' is not a valid runlevel", - applet, argv[optind]); - } - } - - retval = EXIT_SUCCESS; - if (action & DOSHOW) { - if (service) - rc_stringlist_add(runlevels, service); - if (!TAILQ_FIRST(runlevels)) { - free(runlevels); - runlevels = rc_runlevel_list(); - } - - rc_stringlist_sort(&runlevels); - show (runlevels, verbose); - } else { - if (!service) - eerror ("%s: no service specified", applet); - else { - if (action & DOADD) { - if (all_runlevels) { - rc_stringlist_free(runlevels); - eerrorx("%s: the -a option is invalid with add", applet); - } - actfunc = stack ? addstack : add; - } else if (action & DODELETE) { - actfunc = stack ? delstack : delete; - } else { - rc_stringlist_free(runlevels); - eerrorx("%s: invalid action", applet); - } - - if (!TAILQ_FIRST(runlevels)) { - if (all_runlevels) { - free(runlevels); - runlevels = rc_runlevel_list(); - } else { - p = rc_runlevel_get(); - rc_stringlist_add(runlevels, p); - free(p); - } - } - - if (!TAILQ_FIRST(runlevels)) { - free(runlevels); - eerrorx("%s: no runlevels found", applet); - } - - TAILQ_FOREACH(runlevel, runlevels, entries) { - if (!rc_runlevel_exists(runlevel->value)) { - eerror ("%s: runlevel `%s' does not exist", - applet, runlevel->value); - continue; - } - - ret = actfunc(runlevel->value, service); - if (ret < 0) - retval = EXIT_FAILURE; - num_updated += ret; - } - - if (retval == EXIT_SUCCESS && - num_updated == 0 && action & DODELETE) - ewarnx("%s: service `%s' not found in any" - " of the specified runlevels", - applet, service); - } - } - - rc_stringlist_free(runlevels); - return retval; -} diff --git a/src/rc/rc.c b/src/rc/rc.c deleted file mode 100644 index e3301c6c..00000000 --- a/src/rc/rc.c +++ /dev/null @@ -1,1150 +0,0 @@ -/* - * rc.c - * rc - manager for init scripts which control the startup, shutdown - * and the running of daemons. - * - * Also a multicall binary for various commands that can be used in shell - * scripts to query service state, mark service state and provide the - * einfo family of informational functions. - */ - -/* - * Copyright (c) 2007-2009 Roy Marples <roy@marples.name> - * - * 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. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. - */ - -const char rc_copyright[] = "Copyright (c) 2007-2008 Roy Marples"; - -#include <sys/types.h> -#include <sys/ioctl.h> -#include <sys/param.h> -#include <sys/stat.h> -#include <sys/utsname.h> -#include <sys/wait.h> - -#include <errno.h> -#include <dirent.h> -#include <ctype.h> -#include <getopt.h> -#include <libgen.h> -#include <limits.h> -#include <pwd.h> -#include <stdbool.h> -#include <stdio.h> -#include <stdlib.h> -#include <signal.h> -#include <string.h> -#include <strings.h> -#include <termios.h> -#include <unistd.h> - -#include "builtins.h" -#include "einfo.h" -#include "queue.h" -#include "rc.h" -#include "rc-logger.h" -#include "rc-misc.h" -#include "rc-plugin.h" - -#include "version.h" - -#define INITSH RC_LIBEXECDIR "/sh/init.sh" -#define INITEARLYSH RC_LIBEXECDIR "/sh/init-early.sh" - -#define SHUTDOWN "/sbin/shutdown" -#define SULOGIN "/sbin/sulogin" - -#define INTERACTIVE RC_SVCDIR "/interactive" - -#define DEVBOOT "/dev/.rcboot" - -const char *applet = NULL; -static char *runlevel; -static RC_HOOK hook_out; - -struct termios *termios_orig = NULL; - -RC_PIDLIST service_pids; - -static void -clean_failed(void) -{ - DIR *dp; - struct dirent *d; - size_t l; - char *path; - - /* Clean the failed services state dir now */ - if ((dp = opendir(RC_SVCDIR "/failed"))) { - while ((d = readdir(dp))) { - if (d->d_name[0] == '.' && - (d->d_name[1] == '\0' || - (d->d_name[1] == '.' && d->d_name[2] == '\0'))) - continue; - - l = strlen(RC_SVCDIR "/failed/") + - strlen(d->d_name) + 1; - path = xmalloc(sizeof(char) * l); - snprintf(path, l, RC_SVCDIR "/failed/%s", d->d_name); - if (path) { - if (unlink(path)) - eerror("%s: unlink `%s': %s", - applet, path, strerror(errno)); - free(path); - } - } - closedir(dp); - } -} - -static void -cleanup(void) -{ -#ifdef DEBUG_MEMORY - RC_PID *p1 = LIST_FIRST(&service_pids); - RC_PID *p2; -#endif - - if (!rc_in_logger && !rc_in_plugin && - applet && (strcmp(applet, "rc") == 0 || strcmp(applet, "openrc") == 0)) - { - if (hook_out) - rc_plugin_run(hook_out, runlevel); - - rc_plugin_unload(); - - if (termios_orig) { - tcsetattr(STDIN_FILENO, TCSANOW, termios_orig); - free(termios_orig); - } - - /* Clean runlevel start, stop markers */ - rmdir(RC_STARTING); - rmdir(RC_STOPPING); - clean_failed(); - rc_logger_close(); - } - -#ifdef DEBUG_MEMORY - while (p1) { - p2 = LIST_NEXT(p1, entries); - free(p1); - p1 = p2; - } - - rc_stringlist_free(hotplugged_services); - rc_stringlist_free(stop_services); - rc_stringlist_free(start_services); - rc_stringlist_free(types_n); - rc_stringlist_free(types_nua); - rc_deptree_free(deptree); - free(runlevel); -#endif -} - -static char -read_key(bool block) -{ - struct termios termios; - char c = 0; - int fd = STDIN_FILENO; - - if (!isatty(fd)) - return false; - - /* Now save our terminal settings. We need to restore them at exit as - we will be changing it for non-blocking reads for Interactive */ - if (!termios_orig) { - termios_orig = xmalloc(sizeof(*termios_orig)); - tcgetattr(fd, termios_orig); - } - - tcgetattr(fd, &termios); - termios.c_lflag &= ~(ICANON | ECHO); - if (block) - termios.c_cc[VMIN] = 1; - else { - termios.c_cc[VMIN] = 0; - termios.c_cc[VTIME] = 0; - } - tcsetattr(fd, TCSANOW, &termios); - if (read(fd, &c, 1) == -1) - eerror("read: %s", strerror(errno)); - tcsetattr(fd, TCSANOW, termios_orig); - return c; -} - -static bool -want_interactive(void) -{ - char c; - static bool gotinteractive; - static bool interactive; - - if (rc_yesno(getenv("EINFO_QUIET"))) - return false; - if (!gotinteractive) { - gotinteractive = true; - interactive = rc_conf_yesno("rc_interactive"); - } - if (!interactive) - return false; - c = read_key(false); - return (c == 'I' || c == 'i') ? true : false; -} - -static void -mark_interactive(void) -{ - FILE *fp = fopen(INTERACTIVE, "w"); - if (fp) - fclose(fp); -} - -static void -run_program(const char *prog) -{ - struct sigaction sa; - sigset_t full; - sigset_t old; - pid_t pid; - - /* 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); - pid = fork(); - - if (pid == -1) - eerrorx("%s: fork: %s", applet, strerror(errno)); - 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); - - /* Unmask signals */ - sigprocmask(SIG_SETMASK, &old, NULL); - - if (termios_orig) - tcsetattr(STDIN_FILENO, TCSANOW, termios_orig); - - execl(prog, prog, (char *)NULL); - eerror("%s: unable to exec `%s': %s", applet, prog, - strerror(errno)); - _exit(EXIT_FAILURE); - } - - /* Unmask signals and wait for child */ - sigprocmask(SIG_SETMASK, &old, NULL); - if (rc_waitpid(pid) == -1) - eerrorx("%s: failed to exec `%s'", applet, prog); -} - -static void -open_shell(void) -{ - const char *shell; - struct passwd *pw; - -#ifdef __linux__ - const char *sys = rc_sys(); - - /* VSERVER and OPENVZ systems cannot really drop to shells */ - if (sys && - (strcmp(sys, "VSERVER") == 0 || strcmp(sys, "OPENVZ") == 0)) - { - execl("/sbin/halt", "/sbin/halt", "-f", (char *) NULL); - eerrorx("%s: unable to exec `/sbin/halt': %s", - applet, strerror(errno)); - } -#endif - - shell = rc_conf_value("rc_shell"); - /* No shell set, so obey env, then passwd, then default to /bin/sh */ - if (shell == NULL) { - shell = getenv("SHELL"); - if (shell == NULL) { - pw = getpwuid(getuid()); - if (pw) - shell = pw->pw_shell; - if (shell == NULL) - shell = "/bin/sh"; - } - } - run_program(shell); -} - -static bool -set_krunlevel(const char *level) -{ - FILE *fp; - - if (!level || - strcmp(level, getenv ("RC_BOOTLEVEL")) == 0 || - strcmp(level, RC_LEVEL_SINGLE) == 0 || - strcmp(level, RC_LEVEL_SYSINIT) == 0) - { - if (exists(RC_KRUNLEVEL) && - unlink(RC_KRUNLEVEL) != 0) - eerror("unlink `%s': %s", RC_KRUNLEVEL, - strerror(errno)); - return false; - } - - if (!(fp = fopen(RC_KRUNLEVEL, "w"))) { - eerror("fopen `%s': %s", RC_KRUNLEVEL, strerror(errno)); - return false; - } - - fprintf(fp, "%s", level); - fclose(fp); - return true; -} - -static size_t -get_krunlevel(char *buffer, int buffer_len) -{ - FILE *fp; - size_t i = 0; - - if (!exists(RC_KRUNLEVEL)) - return 0; - if (!(fp = fopen(RC_KRUNLEVEL, "r"))) { - eerror("fopen `%s': %s", RC_KRUNLEVEL, strerror(errno)); - return 0; - } - - if (fgets(buffer, buffer_len, fp)) { - i = strlen(buffer); - if (buffer[i - 1] == '\n') - buffer[i - 1] = 0; - } - fclose(fp); - return i; -} - -static void -add_pid(pid_t pid) -{ - RC_PID *p = xmalloc(sizeof(*p)); - p->pid = pid; - LIST_INSERT_HEAD(&service_pids, p, entries); -} - -static void -remove_pid(pid_t pid) -{ - RC_PID *p; - - LIST_FOREACH(p, &service_pids, entries) - if (p->pid == pid) { - LIST_REMOVE(p, entries); - free(p); - return; - } -} - -static void -wait_for_services(void) -{ - for (;;) { - while (waitpid(0, 0, 0) != -1) - ; - if (errno != EINTR) - break; - } -} - -static void -handle_signal(int sig) -{ - int serrno = errno; - char signame[10] = { '\0' }; - pid_t pid; - RC_PID *pi; - int status = 0; - struct winsize ws; - sigset_t sset; - - switch (sig) { - case SIGCHLD: - do { - pid = waitpid(-1, &status, WNOHANG); - if (pid < 0) { - if (errno != ECHILD) - eerror("waitpid: %s", strerror(errno)); - return; - } - } while (!WIFEXITED(status) && !WIFSIGNALED(status)); - - /* Remove that pid from our list */ - if (pid > 0) - remove_pid(pid); - break; - - case SIGWINCH: - if (rc_logger_tty >= 0) { - ioctl(STDIN_FILENO, TIOCGWINSZ, &ws); - ioctl(rc_logger_tty, TIOCSWINSZ, &ws); - } - break; - - case SIGINT: - if (!signame[0]) - snprintf(signame, sizeof(signame), "SIGINT"); - /* FALLTHROUGH */ - case SIGTERM: - if (!signame[0]) - snprintf(signame, sizeof(signame), "SIGTERM"); - /* FALLTHROUGH */ - case SIGQUIT: - if (!signame[0]) - snprintf(signame, sizeof(signame), "SIGQUIT"); - eerrorx("%s: caught %s, aborting", applet, signame); - /* NOTREACHED */ - case SIGUSR1: - eerror("rc: Aborting!"); - - /* Block child signals */ - sigemptyset(&sset); - sigaddset(&sset, SIGCHLD); - sigprocmask(SIG_BLOCK, &sset, NULL); - - /* Kill any running services we have started */ - LIST_FOREACH(pi, &service_pids, entries) - kill(pi->pid, SIGTERM); - - /* Notify plugins we are aborting */ - rc_plugin_run(RC_HOOK_ABORT, NULL); - - exit(EXIT_FAILURE); - /* NOTREACHED */ - - default: - eerror("%s: caught unknown signal %d", applet, sig); - } - - /* Restore errno */ - errno = serrno; -} - -static void -do_sysinit() -{ - struct utsname uts; - const char *sys; - - /* exec init-early.sh if it exists - * This should just setup the console to use the correct - * font. Maybe it should setup the keyboard too? */ - if (exists(INITEARLYSH)) - run_program(INITEARLYSH); - - uname(&uts); - printf("\n %sOpenRC %s" VERSION "%s is starting up %s", - ecolor(ECOLOR_GOOD), ecolor(ECOLOR_HILITE), - ecolor(ECOLOR_NORMAL), ecolor(ECOLOR_BRACKET)); -#ifdef BRANDING - printf(BRANDING " (%s)", uts.machine); -#else - printf("%s %s (%s)", - uts.sysname, - uts.release, - uts.machine); -#endif - - if ((sys = rc_sys())) - printf(" [%s]", sys); - - printf("%s\n\n", ecolor(ECOLOR_NORMAL)); - - if (!rc_yesno(getenv ("EINFO_QUIET")) && - rc_conf_yesno("rc_interactive")) - printf("Press %sI%s to enter interactive boot mode\n\n", - ecolor(ECOLOR_GOOD), ecolor(ECOLOR_NORMAL)); - - setenv("RC_RUNLEVEL", RC_LEVEL_SYSINIT, 1); - run_program(INITSH); - - /* init may have mounted /proc so we can now detect or real - * sys */ - if ((sys = rc_sys())) - setenv("RC_SYS", sys, 1); -} - -static bool -runlevel_config(const char *service, const char *level) -{ - char *init = rc_service_resolve(service); - char *conf, *dir; - size_t l; - bool retval; - - dir = dirname(init); - dir = dirname(init); - l = strlen(dir) + strlen(level) + strlen(service) + 10; - conf = xmalloc(sizeof(char) * l); - snprintf(conf, l, "%s/conf.d/%s.%s", dir, service, level); - retval = exists(conf); - free(conf); - free(init); - return retval; -} - -static void -do_stop_services(const RC_STRINGLIST *types_n, const RC_STRINGLIST *start_services, - const RC_STRINGLIST *stop_services, const RC_DEPTREE *deptree, - const char *newlevel, bool parallel, bool going_down) -{ - pid_t pid; - RC_STRING *service, *svc1, *svc2; - RC_STRINGLIST *deporder, *tmplist, *kwords; - RC_SERVICE state; - RC_STRINGLIST *nostop; - bool crashed, nstop; - - if (!types_n) { - types_n = rc_stringlist_new(); - rc_stringlist_add(types_n, "needsme"); - } - - crashed = rc_conf_yesno("rc_crashed_stop"); - - nostop = rc_stringlist_split(rc_conf_value("rc_nostop"), " "); - TAILQ_FOREACH_REVERSE(service, stop_services, rc_stringlist, entries) - { - state = rc_service_state(service->value); - if (state & RC_SERVICE_STOPPED || state & RC_SERVICE_FAILED) - continue; - - /* Sometimes we don't ever want to stop a service. */ - if (rc_stringlist_find(nostop, service->value)) { - rc_service_mark(service->value, RC_SERVICE_FAILED); - continue; - } - kwords = rc_deptree_depend(deptree, service->value, "keyword"); - if (rc_stringlist_find(kwords, "-stop") || - rc_stringlist_find(kwords, "nostop") || - (going_down && - (rc_stringlist_find(kwords, "-shutdown") || - rc_stringlist_find(kwords, "noshutdown")))) - nstop = true; - else - nstop = false; - rc_stringlist_free(kwords); - if (nstop) { - rc_service_mark(service->value, RC_SERVICE_FAILED); - continue; - } - - /* If the service has crashed, skip futher checks and just stop - it */ - if (crashed && - rc_service_daemons_crashed(service->value)) - goto stop; - - /* If we're in the start list then don't bother stopping us */ - svc1 = rc_stringlist_find(start_services, service->value); - if (svc1) { - if (newlevel && strcmp(runlevel, newlevel) != 0) { - /* So we're in the start list. But we should - * be stopped if we have a runlevel - * configuration file for either the current - * or next so we use the correct one. */ - if (!runlevel_config(service->value,runlevel) && - !runlevel_config(service->value,newlevel)) - continue; - } - else - continue; - } - - /* We got this far. Last check is to see if any any service - * that going to be started depends on us */ - if (!svc1) { - tmplist = rc_stringlist_new(); - rc_stringlist_add(tmplist, service->value); - deporder = rc_deptree_depends(deptree, types_n, - tmplist, newlevel ? newlevel : runlevel, - RC_DEP_STRICT | RC_DEP_TRACE); - rc_stringlist_free(tmplist); - svc2 = NULL; - TAILQ_FOREACH(svc1, deporder, entries) { - svc2 = rc_stringlist_find(start_services, - svc1->value); - if (svc2) - break; - } - rc_stringlist_free(deporder); - - if (svc2) - continue; - } - -stop: - /* After all that we can finally stop the blighter! */ - pid = service_stop(service->value); - if (pid > 0) { - add_pid(pid); - if (!parallel) { - rc_waitpid(pid); - remove_pid(pid); - } - } - } - - rc_stringlist_free(nostop); -} - -static void -do_start_services(const RC_STRINGLIST *start_services, bool parallel) -{ - RC_STRING *service; - pid_t pid; - bool interactive = false; - RC_SERVICE state; - bool crashed = false; - - if (!rc_yesno(getenv("EINFO_QUIET"))) - interactive = exists(INTERACTIVE); - errno = 0; - crashed = rc_conf_yesno("rc_crashed_start"); - if (errno == ENOENT) - crashed = true; - - TAILQ_FOREACH(service, start_services, entries) { - state = rc_service_state(service->value); - if (state & RC_SERVICE_FAILED) - continue; - if (!(state & RC_SERVICE_STOPPED)) { - if (crashed && - rc_service_daemons_crashed(service->value)) - rc_service_mark(service->value, - RC_SERVICE_STOPPED); - else - continue; - } - if (!interactive) - interactive = want_interactive(); - - if (interactive) { - parallel = false; - interactive_retry: - printf("\n"); - einfo("About to start the service %s", - service->value); - eindent(); - einfo("1) Start the service\t\t2) Skip the service"); - einfo("3) Continue boot process\t\t4) Exit to shell"); - eoutdent(); - interactive_option: - switch (read_key(true)) { - case '1': break; - case '2': continue; - case '3': interactive = false; break; - case '4': open_shell(); goto interactive_retry; - default: goto interactive_option; - } - } - - pid = service_start(service->value); - /* Remember the pid if we're running in parallel */ - if (pid > 0) { - add_pid(pid); - if (!parallel) { - rc_waitpid(pid); - remove_pid(pid); - } - } - } - - /* Store our interactive status for boot */ - if (interactive && - (strcmp(runlevel, RC_LEVEL_SYSINIT) == 0 || - strcmp(runlevel, getenv("RC_BOOTLEVEL")) == 0)) - mark_interactive(); - else { - if (exists(INTERACTIVE)) - unlink(INTERACTIVE); - } - -} - -#ifdef RC_DEBUG -static void -handle_bad_signal(int sig) -{ - char pid[10]; - int status; - pid_t crashed_pid = getpid(); - - switch (fork()) { - case -1: - _exit(sig); - /* NOTREACHED */ - case 0: - sprintf(pid, "%i", crashed_pid); - printf("\nAuto launching gdb!\n\n"); - _exit(execlp("gdb", "gdb", "--quiet", "--pid", pid, - "-ex", "bt full", NULL)); - /* NOTREACHED */ - default: - wait(&status); - } - _exit(1); - /* NOTREACHED */ -} -#endif - -#include "_usage.h" -#define usagestring "" \ - "Usage: openrc [options] [<runlevel>]" -#define getoptstring "a:no:s:S" getoptstring_COMMON -static const struct option longopts[] = { - { "applet", 1, NULL, 'a' }, - { "no-stop", 0, NULL, 'n' }, - { "override", 1, NULL, 'o' }, - { "service", 1, NULL, 's' }, - { "sys", 0, NULL, 'S' }, - longopts_COMMON -}; -static const char * const longopts_help[] = { - "runs the applet specified by the next argument", - "do not stop any services", - "override the next runlevel to change into\n" - "when leaving single user or boot runlevels", - "runs the service specified with the rest\nof the arguments", - "output the RC system type, if any", - longopts_help_COMMON -}; -#include "_usage.c" - -int -main(int argc, char **argv) -{ - const char *bootlevel = NULL; - char *newlevel = NULL; - static RC_STRINGLIST *hotplugged_services; - static RC_STRINGLIST *stop_services; - static RC_STRINGLIST *start_services; - static RC_STRINGLIST *types_n; - static RC_STRINGLIST *types_nua; - static RC_DEPTREE *deptree; - RC_STRINGLIST *deporder = NULL; - RC_STRINGLIST *tmplist; - RC_STRING *service; - bool going_down = false; - int depoptions = RC_DEP_STRICT | RC_DEP_TRACE; - char krunlevel [PATH_MAX]; - char pidstr[10]; - int opt; - bool parallel; - int regen = 0; - bool nostop = false; -#ifdef __linux__ - char *proc; - char *p; - char *token; -#endif - -#ifdef RC_DEBUG - signal_setup(SIGBUS, handle_bad_signal); - signal_setup(SIGILL, handle_bad_signal); - signal_setup(SIGSEGV, handle_bad_signal); -#endif - - applet = basename_c(argv[0]); - LIST_INIT(&service_pids); - atexit(cleanup); - if (!applet) - eerrorx("arguments required"); - - /* Run our built in applets. If we ran one, we don't return. */ - run_applets(argc, argv); - - argc--; - argv++; - - /* Change dir to / to ensure all scripts don't use stuff in pwd */ - if (chdir("/") == -1) - eerror("chdir: %s", strerror(errno)); - - /* Ensure our environment is pure - * Also, add our configuration to it */ - env_filter(); - env_config(); - - /* complain about old configuration settings if they exist */ - if (exists(RC_CONF_OLD)) { - ewarn("%s still exists on your system and should be removed.", - RC_CONF_OLD); - ewarn("Please migrate to the appropriate settings in %s", RC_CONF); - } - - argc++; - argv--; - while ((opt = getopt_long(argc, argv, getoptstring, - longopts, (int *) 0)) != -1) - { - switch (opt) { - case 'a': - /* Do nothing, actual logic in run_applets, this - * is a placeholder */ - break; - case 'n': - nostop = true; - break; - case 'o': - if (*optarg == '\0') - optarg = NULL; - if (!rc_runlevel_exists(optarg)) { - eerror("runlevel `%s' does not exist", optarg); - exit(EXIT_FAILURE); - } - if (!set_krunlevel(optarg)) - exit(EXIT_FAILURE); - einfo("Overriding next runlevel to %s", optarg); - exit(EXIT_SUCCESS); - /* NOTREACHED */ - case 's': - newlevel = rc_service_resolve(optarg); - if (!newlevel) - eerrorx("%s: service `%s' does not exist", - applet, optarg); - argv += optind - 1; - *argv = newlevel; - execv(*argv, argv); - eerrorx("%s: %s", applet, strerror(errno)); - /* NOTREACHED */ - case 'S': - bootlevel = rc_sys(); - if (bootlevel) - printf("%s\n", bootlevel); - exit(EXIT_SUCCESS); - /* NOTREACHED */ - case_RC_COMMON_GETOPT - } - } - - newlevel = argv[optind++]; - /* To make life easier, we only have the shutdown runlevel as - * nothing really needs to know that we're rebooting. - * But for those that do, you can test against RC_REBOOT. */ - if (newlevel) { - if (strcmp(newlevel, "reboot") == 0) { - newlevel = UNCONST(RC_LEVEL_SHUTDOWN); - setenv("RC_REBOOT", "YES", 1); - } - } - - /* Enable logging */ - setenv("EINFO_LOG", "openrc", 1); - - /* Export our PID */ - snprintf(pidstr, sizeof(pidstr), "%d", getpid()); - setenv("RC_PID", pidstr, 1); - - /* Create a list of all services which should be started for the new or - * current runlevel including those in boot, sysinit and hotplugged - * runlevels. Clearly, some of these will already be started so we - * won't actually be starting them all. - */ - bootlevel = getenv("RC_BOOTLEVEL"); - runlevel = rc_runlevel_get(); - - rc_logger_open(newlevel ? newlevel : runlevel); - - /* Setup a signal handler */ - signal_setup(SIGINT, handle_signal); - signal_setup(SIGQUIT, handle_signal); - signal_setup(SIGTERM, handle_signal); - signal_setup(SIGUSR1, handle_signal); - signal_setup(SIGWINCH, handle_signal); - - /* Run any special sysinit foo */ - if (newlevel && strcmp(newlevel, RC_LEVEL_SYSINIT) == 0) { - do_sysinit(); - free(runlevel); - runlevel = rc_runlevel_get(); - } - - rc_plugin_load(); - - /* Now we start handling our children */ - signal_setup(SIGCHLD, handle_signal); - - if (newlevel && - (strcmp(newlevel, RC_LEVEL_SHUTDOWN) == 0 || - strcmp(newlevel, RC_LEVEL_SINGLE) == 0)) - { - going_down = true; - if (!exists(RC_KRUNLEVEL)) - set_krunlevel(runlevel); - rc_runlevel_set(newlevel); - setenv("RC_RUNLEVEL", newlevel, 1); - setenv("RC_GOINGDOWN", "YES", 1); - } else { - /* We should not use krunevel in sysinit or boot runlevels */ - if (!newlevel || - (strcmp(newlevel, RC_LEVEL_SYSINIT) != 0 && - strcmp(newlevel, getenv("RC_BOOTLEVEL")) != 0)) - { - if (get_krunlevel(krunlevel, sizeof(krunlevel))) { - newlevel = krunlevel; - set_krunlevel(NULL); - } - } - - if (newlevel) { - if (strcmp(runlevel, newlevel) != 0 && - !rc_runlevel_exists(newlevel)) - eerrorx("%s: not a valid runlevel", newlevel); - -#ifdef __linux__ - if (strcmp(newlevel, RC_LEVEL_SYSINIT) == 0) { - /* If we requested a runlevel, save it now */ - p = rc_proc_getent("rc_runlevel"); - if (p == NULL) - p = rc_proc_getent("softlevel"); - if (p != NULL) { - set_krunlevel(p); - free(p); - } - } -#endif - } - } - - if (going_down) { -#ifdef __FreeBSD__ - /* FIXME: we shouldn't have todo this */ - /* For some reason, wait_for_services waits for the logger - * proccess to finish as well, but only on FreeBSD. - * We cannot allow this so we stop logging now. */ - rc_logger_close(); -#endif - - rc_plugin_run(RC_HOOK_RUNLEVEL_STOP_IN, newlevel); - } else { - rc_plugin_run(RC_HOOK_RUNLEVEL_STOP_IN, runlevel); - } - hook_out = RC_HOOK_RUNLEVEL_STOP_OUT; - - /* Check if runlevel is valid if we're changing */ - if (newlevel && strcmp(runlevel, newlevel) != 0 && !going_down) { - if (!rc_runlevel_exists(newlevel)) - eerrorx("%s: is not a valid runlevel", newlevel); - } - - /* Load our deptree */ - if ((deptree = _rc_deptree_load(0, ®en)) == NULL) - eerrorx("failed to load deptree"); - if (exists(RC_DEPTREE_SKEWED)) - ewarn("WARNING: clock skew detected!"); - - /* Clean the failed services state dir */ - clean_failed(); - - if (mkdir(RC_STOPPING, 0755) != 0) { - if (errno == EACCES) - eerrorx("%s: superuser access required", applet); - eerrorx("%s: failed to create stopping dir `%s': %s", - applet, RC_STOPPING, strerror(errno)); - } - - /* Create a list of all services which we could stop (assuming - * they won't be active in the new or current runlevel) including - * all those services which have been started, are inactive or - * are currently starting. Clearly, some of these will be listed - * in the new or current runlevel so we won't actually be stopping - * them all. - */ - stop_services = rc_services_in_state(RC_SERVICE_STARTED); - tmplist = rc_services_in_state(RC_SERVICE_INACTIVE); - TAILQ_CONCAT(stop_services, tmplist, entries); - free(tmplist); - tmplist = rc_services_in_state(RC_SERVICE_STARTING); - TAILQ_CONCAT(stop_services, tmplist, entries); - free(tmplist); - if (stop_services) - rc_stringlist_sort(&stop_services); - - types_nua = rc_stringlist_new(); - rc_stringlist_add(types_nua, "ineed"); - rc_stringlist_add(types_nua, "iuse"); - rc_stringlist_add(types_nua, "iafter"); - - if (stop_services) { - tmplist = rc_deptree_depends(deptree, types_nua, stop_services, - runlevel, depoptions | RC_DEP_STOP); - rc_stringlist_free(stop_services); - stop_services = tmplist; - } - - /* Create a list of all services which should be started for the new or - * current runlevel including those in boot, sysinit and hotplugged - * runlevels. Clearly, some of these will already be started so we - * won't actually be starting them all. - */ - hotplugged_services = rc_services_in_state(RC_SERVICE_HOTPLUGGED); - start_services = rc_services_in_runlevel_stacked(newlevel ? - newlevel : runlevel); - if (strcmp(newlevel ? newlevel : runlevel, RC_LEVEL_SHUTDOWN) != 0 && - strcmp(newlevel ? newlevel : runlevel, RC_LEVEL_SYSINIT) != 0) - { - tmplist = rc_services_in_runlevel(RC_LEVEL_SYSINIT); - TAILQ_CONCAT(start_services, tmplist, entries); - free(tmplist); - /* If we are NOT headed for the single-user runlevel... */ - if (strcmp(newlevel ? newlevel : runlevel, - RC_LEVEL_SINGLE) != 0) - { - /* If we are NOT headed for the boot runlevel... */ - if (strcmp(newlevel ? newlevel : runlevel, - bootlevel) != 0) - { - tmplist = rc_services_in_runlevel(bootlevel); - TAILQ_CONCAT(start_services, tmplist, entries); - free(tmplist); - } - if (hotplugged_services) { - TAILQ_FOREACH(service, hotplugged_services, - entries) - rc_stringlist_addu(start_services, - service->value); - } - } - } - - parallel = rc_conf_yesno("rc_parallel"); - - /* Now stop the services that shouldn't be running */ - if (stop_services && !nostop) - do_stop_services(types_n, start_services, stop_services, deptree, newlevel, parallel, going_down); - - /* Wait for our services to finish */ - wait_for_services(); - - /* Notify the plugins we have finished */ - rc_plugin_run(RC_HOOK_RUNLEVEL_STOP_OUT, - going_down ? newlevel : runlevel); - hook_out = 0; - - rmdir(RC_STOPPING); - - /* Store the new runlevel */ - if (newlevel) { - rc_runlevel_set(newlevel); - free(runlevel); - runlevel = xstrdup(newlevel); - setenv("RC_RUNLEVEL", runlevel, 1); - } - -#ifdef __linux__ - /* We can't log beyond this point as the shutdown runlevel - * will mount / readonly. */ - if (strcmp(runlevel, RC_LEVEL_SHUTDOWN) == 0) - rc_logger_close(); -#endif - - mkdir(RC_STARTING, 0755); - rc_plugin_run(RC_HOOK_RUNLEVEL_START_IN, runlevel); - hook_out = RC_HOOK_RUNLEVEL_START_OUT; - - /* Re-add our hotplugged services if they stopped */ - if (hotplugged_services) - TAILQ_FOREACH(service, hotplugged_services, entries) - rc_service_mark(service->value, RC_SERVICE_HOTPLUGGED); - -#ifdef __linux__ - /* If the "noinit" parameter was passed on the kernel command line then - * mark the specified services as started so they will not be started - * by us. */ - proc = p = rc_proc_getent("noinit"); - if (proc) { - while ((token = strsep(&p, ","))) - rc_service_mark(token, RC_SERVICE_STARTED); - free(proc); - } -#endif - - /* If we have a list of services to start then... */ - if (start_services) { - /* Get a list of the chained runlevels which compose the target runlevel */ - RC_STRINGLIST *runlevel_chain = rc_runlevel_stacks(runlevel); - - /* Loop through them in reverse order. */ - RC_STRING *rlevel; - TAILQ_FOREACH_REVERSE(rlevel, runlevel_chain, rc_stringlist, entries) - { - /* Get a list of all the services in that runlevel */ - RC_STRINGLIST *run_services = rc_services_in_runlevel(rlevel->value); - - /* Start those services. */ - rc_stringlist_sort(&run_services); - deporder = rc_deptree_depends(deptree, types_nua, run_services, rlevel->value, depoptions | RC_DEP_START); - rc_stringlist_free(run_services); - run_services = deporder; - do_start_services(run_services, parallel); - - /* Wait for our services to finish */ - wait_for_services(); - - /* Free the list of services, we're done with it. */ - rc_stringlist_free(run_services); - } - rc_stringlist_free(runlevel_chain); - } - -#ifdef __linux__ - /* If the "noinit" parameter was passed on the kernel command line then - * mark the specified services as stopped so that our records reflect - * reality. */ - proc = p = rc_proc_getent("noinit"); - if (proc) { - while ((token = strsep(&p, ","))) - rc_service_mark(token, RC_SERVICE_STOPPED); - free(proc); - } - -#endif - - rc_plugin_run(RC_HOOK_RUNLEVEL_START_OUT, runlevel); - hook_out = 0; - - /* If we're in the boot runlevel and we regenerated our dependencies - * we need to delete them so that they are regenerated again in the - * default runlevel as they may depend on things that are now - * available */ - if (regen && strcmp(runlevel, bootlevel) == 0) - unlink(RC_DEPTREE_CACHE); - - return EXIT_SUCCESS; -} diff --git a/src/rc/runscript.c b/src/rc/runscript.c deleted file mode 100644 index 0ac19661..00000000 --- a/src/rc/runscript.c +++ /dev/null @@ -1,1396 +0,0 @@ -/* - * runscript.c - * Handle launching of init scripts. - */ - -/* - * Copyright (c) 2007-2009 Roy Marples <roy@marples.name> - * - * 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. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. - */ - -#include <sys/types.h> -#include <sys/ioctl.h> -#include <sys/file.h> -#include <sys/param.h> -#include <sys/stat.h> -#include <sys/wait.h> - -#include <ctype.h> -#include <errno.h> -#include <fcntl.h> -#include <fnmatch.h> -#include <getopt.h> -#include <libgen.h> -#include <limits.h> -#include <poll.h> -#include <signal.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <termios.h> -#include <time.h> -#include <unistd.h> - -#if defined(__linux__) || (defined(__FreeBSD_kernel__) && \ - defined(__GLIBC__)) -# include <pty.h> -#elif defined(__NetBSD__) || defined(__OpenBSD__) -# include <util.h> -#else -# include <libutil.h> -#endif - -#include "builtins.h" -#include "einfo.h" -#include "queue.h" -#include "rc.h" -#include "rc-misc.h" -#include "rc-plugin.h" - -#ifdef HAVE_SELINUX -#include "rc-selinux.h" -#endif - -#define PREFIX_LOCK RC_SVCDIR "/prefix.lock" - -#define WAIT_INTERVAL 20000000 /* usecs to poll the lock file */ -#define WAIT_TIMEOUT 60 /* seconds until we timeout */ -#define WARN_TIMEOUT 10 /* warn about this every N seconds */ - -static const char *applet; -static char *service, *runlevel, *ibsave, *prefix; -static RC_DEPTREE *deptree; -static RC_STRINGLIST *applet_list, *services, *tmplist; -static RC_STRINGLIST *restart_services, *need_services, *use_services; -static RC_HOOK hook_out; -static int exclusive_fd = -1, master_tty = -1; -static bool sighup, in_background, deps, dry_run; -static pid_t service_pid; -static int signal_pipe[2] = { -1, -1 }; - -static RC_STRINGLIST *types_b, *types_n, *types_nu, *types_nua, *types_m; -static RC_STRINGLIST *types_mua = NULL; - -static void -handle_signal(int sig) -{ - int serrno = errno; - char signame[10] = { '\0' }; - struct winsize ws; - - switch (sig) { - case SIGHUP: - sighup = true; - break; - - case SIGCHLD: - if (signal_pipe[1] > -1) { - if (write(signal_pipe[1], &sig, sizeof(sig)) == -1) - eerror("%s: send: %s", - service, strerror(errno)); - } else - rc_waitpid(-1); - break; - - case SIGWINCH: - if (master_tty >= 0) { - ioctl(fileno(stdout), TIOCGWINSZ, &ws); - ioctl(master_tty, TIOCSWINSZ, &ws); - } - break; - - case SIGINT: - if (!signame[0]) - snprintf(signame, sizeof(signame), "SIGINT"); - /* FALLTHROUGH */ - case SIGTERM: - if (!signame[0]) - snprintf(signame, sizeof(signame), "SIGTERM"); - /* FALLTHROUGH */ - case SIGQUIT: - if (!signame[0]) - snprintf(signame, sizeof(signame), "SIGQUIT"); - /* Send the signal to our children too */ - if (service_pid > 0) - kill(service_pid, sig); - eerrorx("%s: caught %s, aborting", applet, signame); - /* NOTREACHED */ - - default: - eerror("%s: caught unknown signal %d", applet, sig); - } - - /* Restore errno */ - errno = serrno; -} - -static void -unhotplug() -{ - char file[PATH_MAX]; - - snprintf(file, sizeof(file), RC_SVCDIR "/hotplugged/%s", applet); - if (exists(file) && unlink(file) != 0) - eerror("%s: unlink `%s': %s", applet, file, strerror(errno)); -} - -static void -start_services(RC_STRINGLIST *list) -{ - RC_STRING *svc; - RC_SERVICE state = rc_service_state (service); - - if (!list) - return; - - if (state & RC_SERVICE_INACTIVE || - state & RC_SERVICE_WASINACTIVE || - state & RC_SERVICE_STARTING || - state & RC_SERVICE_STARTED) - { - TAILQ_FOREACH(svc, list, entries) { - if (!(rc_service_state(svc->value) & - RC_SERVICE_STOPPED)) - continue; - if (state & RC_SERVICE_INACTIVE || - state & RC_SERVICE_WASINACTIVE) - { - rc_service_schedule_start(service, - svc->value); - ewarn("WARNING: %s will start when %s has started", - svc->value, applet); - } else - service_start(svc->value); - } - } -} - -static void -restore_state(void) -{ - RC_SERVICE state; - - if (rc_in_plugin || exclusive_fd == -1) - return; - state = rc_service_state(applet); - if (state & RC_SERVICE_STOPPING) { - if (state & RC_SERVICE_WASINACTIVE) - rc_service_mark(applet, RC_SERVICE_INACTIVE); - else - rc_service_mark(applet, RC_SERVICE_STARTED); - if (rc_runlevel_stopping()) - rc_service_mark(applet, RC_SERVICE_FAILED); - } else if (state & RC_SERVICE_STARTING) { - if (state & RC_SERVICE_WASINACTIVE) - rc_service_mark(applet, RC_SERVICE_INACTIVE); - else - rc_service_mark(applet, RC_SERVICE_STOPPED); - if (rc_runlevel_starting()) - rc_service_mark(applet, RC_SERVICE_FAILED); - } - exclusive_fd = svc_unlock(applet, exclusive_fd); -} - -static void -cleanup(void) -{ - restore_state(); - - if (!rc_in_plugin) { - if (hook_out) { - rc_plugin_run(hook_out, applet); - if (hook_out == RC_HOOK_SERVICE_START_DONE) - rc_plugin_run(RC_HOOK_SERVICE_START_OUT, - applet); - else if (hook_out == RC_HOOK_SERVICE_STOP_DONE) - rc_plugin_run(RC_HOOK_SERVICE_STOP_OUT, - applet); - } - - if (restart_services) - start_services(restart_services); - } - - rc_plugin_unload(); - -#ifdef DEBUG_MEMORY - rc_stringlist_free(types_b); - rc_stringlist_free(types_n); - rc_stringlist_free(types_nu); - rc_stringlist_free(types_nua); - rc_stringlist_free(types_m); - rc_stringlist_free(types_mua); - rc_deptree_free(deptree); - rc_stringlist_free(restart_services); - rc_stringlist_free(need_services); - rc_stringlist_free(use_services); - rc_stringlist_free(services); - rc_stringlist_free(applet_list); - rc_stringlist_free(tmplist); - free(ibsave); - free(service); - free(prefix); - free(runlevel); -#endif -} - -/* Buffer and lock all output messages so that we get readable content */ -/* FIXME: Use a dynamic lock file that contains the tty/pts as well. - * For example openrc-pts8.lock or openrc-tty1.lock. - * Using a static lock file makes no sense, esp. in multi-user environments. - * Why don't we use (f)printf, as it is thread-safe through POSIX already? - * Bug: 360013 - */ -static int -write_prefix(const char *buffer, size_t bytes, bool *prefixed) -{ - size_t i, j; - const char *ec = ecolor(ECOLOR_HILITE); - const char *ec_normal = ecolor(ECOLOR_NORMAL); - ssize_t ret = 0; - int fd = fileno(stdout), lock_fd = -1; - - /* - * Lock the prefix. - * open() may fail here when running as user, as RC_SVCDIR may not be writable. - */ - lock_fd = open(PREFIX_LOCK, O_WRONLY | O_CREAT, 0664); - - if (lock_fd != -1) { - while (flock(lock_fd, LOCK_EX) != 0) { - if (errno != EINTR) { - ewarnv("flock() failed: %s", strerror(errno)); - break; - } - } - } - else - ewarnv("Couldn't open the prefix lock, please make sure you have enough permissions"); - - for (i = 0; i < bytes; i++) { - /* We don't prefix eend calls (cursor up) */ - if (buffer[i] == '\033' && !*prefixed) { - for (j = i + 1; j < bytes; j++) { - if (buffer[j] == 'A') - *prefixed = true; - if (isalpha((unsigned int)buffer[j])) - break; - } - } - - if (!*prefixed) { - ret += write(fd, ec, strlen(ec)); - ret += write(fd, prefix, strlen(prefix)); - ret += write(fd, ec_normal, strlen(ec_normal)); - ret += write(fd, "|", 1); - *prefixed = true; - } - - if (buffer[i] == '\n') - *prefixed = false; - ret += write(fd, buffer + i, 1); - } - - /* Release the lock */ - close(lock_fd); - - return ret; -} - -static int -svc_exec(const char *arg1, const char *arg2) -{ - int ret, fdout = fileno(stdout); - struct termios tt; - struct winsize ws; - int i; - int flags = 0; - struct pollfd fd[2]; - int s; - char *buffer; - size_t bytes; - bool prefixed = false; - int slave_tty; - sigset_t sigchldmask; - sigset_t oldmask; - - /* Setup our signal pipe */ - if (pipe(signal_pipe) == -1) - eerrorx("%s: pipe: %s", service, applet); - for (i = 0; i < 2; i++) - if ((flags = fcntl(signal_pipe[i], F_GETFD, 0) == -1 || - fcntl(signal_pipe[i], F_SETFD, flags | FD_CLOEXEC) == -1)) - eerrorx("%s: fcntl: %s", service, strerror(errno)); - - /* Open a pty for our prefixed output - * We do this instead of mapping pipes to stdout, stderr so that - * programs can tell if they're attached to a tty or not. - * The only loss is that we can no longer tell the difference - * between the childs stdout or stderr */ - master_tty = slave_tty = -1; - if (prefix && isatty(fdout)) { - tcgetattr(fdout, &tt); - ioctl(fdout, TIOCGWINSZ, &ws); - - /* If the below call fails due to not enough ptys then we don't - * prefix the output, but we still work */ - openpty(&master_tty, &slave_tty, NULL, &tt, &ws); - if (master_tty >= 0 && - (flags = fcntl(master_tty, F_GETFD, 0)) == 0) - fcntl(master_tty, F_SETFD, flags | FD_CLOEXEC); - - if (slave_tty >=0 && - (flags = fcntl(slave_tty, F_GETFD, 0)) == 0) - fcntl(slave_tty, F_SETFD, flags | FD_CLOEXEC); - } - - service_pid = fork(); - if (service_pid == -1) - eerrorx("%s: fork: %s", service, strerror(errno)); - if (service_pid == 0) { - if (slave_tty >= 0) { - dup2(slave_tty, STDOUT_FILENO); - dup2(slave_tty, STDERR_FILENO); - } - - if (exists(RC_SVCDIR "/runscript.sh")) { - execl(RC_SVCDIR "/runscript.sh", - RC_SVCDIR "/runscript.sh", - service, arg1, arg2, (char *) NULL); - eerror("%s: exec `" RC_SVCDIR "/runscript.sh': %s", - service, strerror(errno)); - _exit(EXIT_FAILURE); - } else { - execl(RC_LIBEXECDIR "/sh/runscript.sh", - RC_LIBEXECDIR "/sh/runscript.sh", - service, arg1, arg2, (char *) NULL); - eerror("%s: exec `" RC_LIBEXECDIR "/sh/runscript.sh': %s", - service, strerror(errno)); - _exit(EXIT_FAILURE); - } - } - - buffer = xmalloc(sizeof(char) * BUFSIZ); - fd[0].fd = signal_pipe[0]; - fd[0].events = fd[1].events = POLLIN; - fd[0].revents = fd[1].revents = 0; - if (master_tty >= 0) { - fd[1].fd = master_tty; - fd[1].events = POLLIN; - fd[1].revents = 0; - } - - for (;;) { - if ((s = poll(fd, master_tty >= 0 ? 2 : 1, -1)) == -1) { - if (errno != EINTR) { - eerror("%s: poll: %s", - service, strerror(errno)); - break; - } - } - - if (s > 0) { - if (fd[1].revents & (POLLIN | POLLHUP)) { - bytes = read(master_tty, buffer, BUFSIZ); - write_prefix(buffer, bytes, &prefixed); - } - - /* Only SIGCHLD signals come down this pipe */ - if (fd[0].revents & (POLLIN | POLLHUP)) - break; - } - } - - free(buffer); - - sigemptyset (&sigchldmask); - sigaddset (&sigchldmask, SIGCHLD); - sigprocmask (SIG_BLOCK, &sigchldmask, &oldmask); - - close(signal_pipe[0]); - close(signal_pipe[1]); - signal_pipe[0] = signal_pipe[1] = -1; - - sigprocmask (SIG_SETMASK, &oldmask, NULL); - - if (master_tty >= 0) { - /* Why did we do this? */ - /* signal (SIGWINCH, SIG_IGN); */ - close(master_tty); - master_tty = -1; - } - - ret = rc_waitpid(service_pid); - ret = WEXITSTATUS(ret); - if (ret != 0 && errno == ECHILD) - /* killall5 -9 could cause this */ - ret = 0; - service_pid = 0; - - return ret; -} - -static bool -svc_wait(const char *svc) -{ - char file[PATH_MAX]; - int fd; - bool forever = false; - RC_STRINGLIST *keywords; - struct timespec interval, timeout, warn; - - /* Some services don't have a timeout, like fsck */ - keywords = rc_deptree_depend(deptree, svc, "keyword"); - if (rc_stringlist_find(keywords, "-timeout") || - rc_stringlist_find(keywords, "notimeout")) - forever = true; - rc_stringlist_free(keywords); - - snprintf(file, sizeof(file), RC_SVCDIR "/exclusive/%s", - basename_c(svc)); - - interval.tv_sec = 0; - interval.tv_nsec = WAIT_INTERVAL; - timeout.tv_sec = WAIT_TIMEOUT; - timeout.tv_nsec = 0; - warn.tv_sec = WARN_TIMEOUT; - warn.tv_nsec = 0; - for (;;) { - fd = open(file, O_RDONLY | O_NONBLOCK); - if (fd != -1) { - if (flock(fd, LOCK_SH | LOCK_NB) == 0) { - close(fd); - return true; - } - close(fd); - } - if (errno == ENOENT) - return true; - if (errno != EWOULDBLOCK) - eerrorx("%s: open `%s': %s", applet, file, - strerror(errno)); - if (nanosleep(&interval, NULL) == -1) { - if (errno != EINTR) - return false; - } - if (!forever) { - timespecsub(&timeout, &interval, &timeout); - if (timeout.tv_sec <= 0) - return false; - timespecsub(&warn, &interval, &warn); - if (warn.tv_sec <= 0) { - ewarn("%s: waiting for %s (%d seconds)", - applet, svc, (int)timeout.tv_sec); - warn.tv_sec = WARN_TIMEOUT; - warn.tv_nsec = 0; - } - } - } - return false; -} - -static void -get_started_services(void) -{ - RC_STRINGLIST *tmp = rc_services_in_state(RC_SERVICE_INACTIVE); - - rc_stringlist_free(restart_services); - restart_services = rc_services_in_state(RC_SERVICE_STARTED); - TAILQ_CONCAT(restart_services, tmp, entries); - free(tmp); -} - -static void -setup_types(void) -{ - types_b = rc_stringlist_new(); - rc_stringlist_add(types_b, "broken"); - - types_n = rc_stringlist_new(); - rc_stringlist_add(types_n, "ineed"); - - types_nu = rc_stringlist_new(); - rc_stringlist_add(types_nu, "ineed"); - rc_stringlist_add(types_nu, "iuse"); - - types_nua = rc_stringlist_new(); - rc_stringlist_add(types_nua, "ineed"); - rc_stringlist_add(types_nua, "iuse"); - rc_stringlist_add(types_nua, "iafter"); - - types_m = rc_stringlist_new(); - rc_stringlist_add(types_m, "needsme"); - - types_mua = rc_stringlist_new(); - rc_stringlist_add(types_mua, "needsme"); - rc_stringlist_add(types_mua, "usesme"); - rc_stringlist_add(types_mua, "beforeme"); -} - -static void -svc_start_check(void) -{ - RC_SERVICE state; - - state = rc_service_state(service); - - if (in_background) { - if (!(state & (RC_SERVICE_INACTIVE | RC_SERVICE_STOPPED))) - exit(EXIT_FAILURE); - if (rc_yesno(getenv("IN_HOTPLUG"))) - rc_service_mark(service, RC_SERVICE_HOTPLUGGED); - if (strcmp(runlevel, RC_LEVEL_SYSINIT) == 0) - ewarnx("WARNING: %s will be started in the" - " next runlevel", applet); - } - - if (exclusive_fd == -1) - exclusive_fd = svc_lock(applet); - if (exclusive_fd == -1) { - if (errno == EACCES) - eerrorx("%s: superuser access required", applet); - if (state & RC_SERVICE_STOPPING) - ewarnx("WARNING: %s is stopping", applet); - else - ewarnx("WARNING: %s is already starting", applet); - } - fcntl(exclusive_fd, F_SETFD, - fcntl(exclusive_fd, F_GETFD, 0) | FD_CLOEXEC); - - if (state & RC_SERVICE_STARTED) { - ewarn("WARNING: %s has already been started", applet); - exit(EXIT_SUCCESS); - } - else if (state & RC_SERVICE_INACTIVE && !in_background) - ewarnx("WARNING: %s has already started, but is inactive", - applet); - - rc_service_mark(service, RC_SERVICE_STARTING); - hook_out = RC_HOOK_SERVICE_START_OUT; - rc_plugin_run(RC_HOOK_SERVICE_START_IN, applet); -} - -static void -svc_start_deps(void) -{ - bool first; - RC_STRING *svc, *svc2; - RC_SERVICE state; - int depoptions = RC_DEP_TRACE, n; - size_t len; - char *p, *tmp; - pid_t pid; - - errno = 0; - if (rc_conf_yesno("rc_depend_strict") || errno == ENOENT) - depoptions |= RC_DEP_STRICT; - - if (!deptree && ((deptree = _rc_deptree_load(0, NULL)) == NULL)) - eerrorx("failed to load deptree"); - if (!types_b) - setup_types(); - - services = rc_deptree_depends(deptree, types_b, applet_list, - runlevel, 0); - if (TAILQ_FIRST(services)) { - eerrorn("ERROR: %s needs service(s) ", applet); - first = true; - TAILQ_FOREACH(svc, services, entries) { - if (first) - first = false; - else - fprintf(stderr, ", "); - fprintf(stderr, "%s", svc->value); - } - fprintf(stderr, "\n"); - exit(EXIT_FAILURE); - } - rc_stringlist_free(services); - services = NULL; - - need_services = rc_deptree_depends(deptree, types_n, - applet_list, runlevel, depoptions); - use_services = rc_deptree_depends(deptree, types_nu, - applet_list, runlevel, depoptions); - - if (!rc_runlevel_starting()) { - TAILQ_FOREACH(svc, use_services, entries) { - state = rc_service_state(svc->value); - /* Don't stop failed services again. - * If you remove this check, ensure that the - * exclusive file isn't created. */ - if (state & RC_SERVICE_FAILED && - rc_runlevel_starting()) - continue; - if (state & RC_SERVICE_STOPPED) { - if (dry_run) { - printf(" %s", svc->value); - continue; - } - pid = service_start(svc->value); - if (!rc_conf_yesno("rc_parallel")) - rc_waitpid(pid); - } - } - } - - if (dry_run) - return; - - /* Now wait for them to start */ - services = rc_deptree_depends(deptree, types_nua, applet_list, - runlevel, depoptions); - /* We use tmplist to hold our scheduled by list */ - tmplist = rc_stringlist_new(); - TAILQ_FOREACH(svc, services, entries) { - state = rc_service_state(svc->value); - if (state & RC_SERVICE_STARTED) - continue; - - /* Don't wait for services which went inactive but are - * now in starting state which we are after */ - if (state & RC_SERVICE_STARTING && - state & RC_SERVICE_WASINACTIVE) - { - if (!rc_stringlist_find(need_services, svc->value) && - !rc_stringlist_find(use_services, svc->value)) - continue; - } - - if (!svc_wait(svc->value)) - eerror("%s: timed out waiting for %s", - applet, svc->value); - state = rc_service_state(svc->value); - if (state & RC_SERVICE_STARTED) - continue; - if (rc_stringlist_find(need_services, svc->value)) { - if (state & RC_SERVICE_INACTIVE || - state & RC_SERVICE_WASINACTIVE) - { - rc_stringlist_add(tmplist, svc->value); - } else if (!TAILQ_FIRST(tmplist)) - eerrorx("ERROR: cannot start %s as" - " %s would not start", - applet, svc->value); - } - } - - if (TAILQ_FIRST(tmplist)) { - /* Set the state now, then unlink our exclusive so that - our scheduled list is preserved */ - rc_service_mark(service, RC_SERVICE_STOPPED); - - rc_stringlist_free(use_services); - use_services = NULL; - len = 0; - n = 0; - TAILQ_FOREACH(svc, tmplist, entries) { - rc_service_schedule_start(svc->value, service); - use_services = rc_deptree_depend(deptree, - "iprovide", svc->value); - TAILQ_FOREACH(svc2, use_services, entries) - rc_service_schedule_start(svc2->value, service); - rc_stringlist_free(use_services); - use_services = NULL; - len += strlen(svc->value) + 2; - n++; - } - - len += 5; - tmp = p = xmalloc(sizeof(char) * len); - TAILQ_FOREACH(svc, tmplist, entries) { - if (p != tmp) - p += snprintf(p, len, ", "); - p += snprintf(p, len - (p - tmp), - "%s", svc->value); - } - rc_stringlist_free(tmplist); - tmplist = NULL; - ewarnx("WARNING: %s will start when %s has started", applet, tmp); - free(tmp); - } - - rc_stringlist_free(tmplist); - tmplist = NULL; - rc_stringlist_free(services); - services = NULL; -} - -static void svc_start_real() -{ - bool started; - RC_STRING *svc, *svc2; - - if (ibsave) - setenv("IN_BACKGROUND", ibsave, 1); - hook_out = RC_HOOK_SERVICE_START_DONE; - rc_plugin_run(RC_HOOK_SERVICE_START_NOW, applet); - started = (svc_exec("start", NULL) == 0); - if (ibsave) - unsetenv("IN_BACKGROUND"); - - if (rc_service_state(service) & RC_SERVICE_INACTIVE) - ewarnx("WARNING: %s has started, but is inactive", applet); - else if (!started) - eerrorx("ERROR: %s failed to start", applet); - - rc_service_mark(service, RC_SERVICE_STARTED); - exclusive_fd = svc_unlock(applet, exclusive_fd); - hook_out = RC_HOOK_SERVICE_START_OUT; - rc_plugin_run(RC_HOOK_SERVICE_START_DONE, applet); - - /* Now start any scheduled services */ - services = rc_services_scheduled(service); - TAILQ_FOREACH(svc, services, entries) - if (rc_service_state(svc->value) & RC_SERVICE_STOPPED) - service_start(svc->value); - rc_stringlist_free(services); - services = NULL; - - /* Do the same for any services we provide */ - if (deptree) { - tmplist = rc_deptree_depend(deptree, "iprovide", applet); - TAILQ_FOREACH(svc, tmplist, entries) { - services = rc_services_scheduled(svc->value); - TAILQ_FOREACH(svc2, services, entries) - if (rc_service_state(svc2->value) & - RC_SERVICE_STOPPED) - service_start(svc2->value); - rc_stringlist_free(services); - services = NULL; - } - rc_stringlist_free(tmplist); - tmplist = NULL; - } - - hook_out = 0; - rc_plugin_run(RC_HOOK_SERVICE_START_OUT, applet); -} - -static void -svc_start(void) -{ - if (dry_run) - einfon("start:"); - else - svc_start_check(); - if (deps) - svc_start_deps(); - if (dry_run) - printf(" %s\n", applet); - else - svc_start_real(); -} - -static int -svc_stop_check(RC_SERVICE *state) -{ - *state = rc_service_state(service); - - if (rc_runlevel_stopping() && *state & RC_SERVICE_FAILED) - exit(EXIT_FAILURE); - - if (in_background && - !(*state & RC_SERVICE_STARTED) && - !(*state & RC_SERVICE_INACTIVE)) - exit(EXIT_FAILURE); - - if (exclusive_fd == -1) - exclusive_fd = svc_lock(applet); - if (exclusive_fd == -1) { - if (errno == EACCES) - eerrorx("%s: superuser access required", applet); - if (*state & RC_SERVICE_STOPPING) - ewarnx("WARNING: %s is already stopping", applet); - eerrorx("ERROR: %s stopped by something else", applet); - } - fcntl(exclusive_fd, F_SETFD, - fcntl(exclusive_fd, F_GETFD, 0) | FD_CLOEXEC); - - if (*state & RC_SERVICE_STOPPED) { - ewarn("WARNING: %s is already stopped", applet); - return 1; - } - - rc_service_mark(service, RC_SERVICE_STOPPING); - hook_out = RC_HOOK_SERVICE_STOP_OUT; - rc_plugin_run(RC_HOOK_SERVICE_STOP_IN, applet); - - if (!rc_runlevel_stopping()) { - if (rc_service_in_runlevel(service, RC_LEVEL_SYSINIT)) - ewarn("WARNING: you are stopping a sysinit service"); - else if (rc_service_in_runlevel(service, RC_LEVEL_BOOT)) - ewarn("WARNING: you are stopping a boot service"); - } - - return 0; -} - -static void -svc_stop_deps(RC_SERVICE state) -{ - int depoptions = RC_DEP_TRACE; - RC_STRING *svc; - pid_t pid; - - if (state & RC_SERVICE_WASINACTIVE) - return; - - errno = 0; - if (rc_conf_yesno("rc_depend_strict") || errno == ENOENT) - depoptions |= RC_DEP_STRICT; - - if (!deptree && ((deptree = _rc_deptree_load(0, NULL)) == NULL)) - eerrorx("failed to load deptree"); - - if (!types_m) - setup_types(); - - services = rc_deptree_depends(deptree, types_m, applet_list, - runlevel, depoptions); - tmplist = rc_stringlist_new(); - TAILQ_FOREACH_REVERSE(svc, services, rc_stringlist, entries) { - state = rc_service_state(svc->value); - /* Don't stop failed services again. - * If you remove this check, ensure that the - * exclusive file isn't created. */ - if (state & RC_SERVICE_FAILED && - rc_runlevel_stopping()) - continue; - if (state & RC_SERVICE_STARTED || - state & RC_SERVICE_INACTIVE) - { - if (dry_run) { - printf(" %s", svc->value); - continue; - } - svc_wait(svc->value); - state = rc_service_state(svc->value); - if (state & RC_SERVICE_STARTED || - state & RC_SERVICE_INACTIVE) - { - pid = service_stop(svc->value); - if (!rc_conf_yesno("rc_parallel")) - rc_waitpid(pid); - rc_stringlist_add(tmplist, svc->value); - } - } - } - rc_stringlist_free(services); - services = NULL; - if (dry_run) - return; - - TAILQ_FOREACH(svc, tmplist, entries) { - if (rc_service_state(svc->value) & RC_SERVICE_STOPPED) - continue; - svc_wait(svc->value); - if (rc_service_state(svc->value) & RC_SERVICE_STOPPED) - continue; - if (rc_runlevel_stopping()) { - /* If shutting down, we should stop even - * if a dependant failed */ - if (runlevel && - (strcmp(runlevel, - RC_LEVEL_SHUTDOWN) == 0 || - strcmp(runlevel, - RC_LEVEL_SINGLE) == 0)) - continue; - rc_service_mark(service, RC_SERVICE_FAILED); - } - eerrorx("ERROR: cannot stop %s as %s " - "is still up", applet, svc->value); - } - rc_stringlist_free(tmplist); - tmplist = NULL; - - /* We now wait for other services that may use us and are - * stopping. This is important when a runlevel stops */ - services = rc_deptree_depends(deptree, types_mua, applet_list, - runlevel, depoptions); - TAILQ_FOREACH(svc, services, entries) { - if (rc_service_state(svc->value) & RC_SERVICE_STOPPED) - continue; - svc_wait(svc->value); - } - rc_stringlist_free(services); - services = NULL; -} - -static void -svc_stop_real(void) -{ - bool stopped; - - /* If we're stopping localmount, set LC_ALL=C so that - * bash doesn't load anything blocking the unmounting of /usr */ - if (strcmp(applet, "localmount") == 0) - setenv("LC_ALL", "C", 1); - - if (ibsave) - setenv("IN_BACKGROUND", ibsave, 1); - hook_out = RC_HOOK_SERVICE_STOP_DONE; - rc_plugin_run(RC_HOOK_SERVICE_STOP_NOW, applet); - stopped = (svc_exec("stop", NULL) == 0); - if (ibsave) - unsetenv("IN_BACKGROUND"); - - if (!stopped) - eerrorx("ERROR: %s failed to stop", applet); - - if (in_background) - rc_service_mark(service, RC_SERVICE_INACTIVE); - else - rc_service_mark(service, RC_SERVICE_STOPPED); - - hook_out = RC_HOOK_SERVICE_STOP_OUT; - rc_plugin_run(RC_HOOK_SERVICE_STOP_DONE, applet); - hook_out = 0; - rc_plugin_run(RC_HOOK_SERVICE_STOP_OUT, applet); -} - -static int -svc_stop(void) -{ - RC_SERVICE state; - - state = 0; - if (dry_run) - einfon("stop:"); - else - if (svc_stop_check(&state) == 1) - return 1; /* Service has been stopped already */ - if (deps) - svc_stop_deps(state); - if (dry_run) - printf(" %s\n", applet); - else - svc_stop_real(); - - return 0; -} - -static void -svc_restart(void) -{ - /* This is hairly and a better way needs to be found I think! - * The issue is this - openvpn need net and dns. net can restart - * dns via resolvconf, so you could have openvpn trying to restart - * dnsmasq which in turn is waiting on net which in turn is waiting - * on dnsmasq. - * The work around is for resolvconf to restart its services with - * --nodeps which means just that. - * The downside is that there is a small window when our status is - * invalid. - * One workaround would be to introduce a new status, - * or status locking. */ - if (!deps) { - RC_SERVICE state = rc_service_state(service); - if (state & RC_SERVICE_STARTED || state & RC_SERVICE_INACTIVE) - svc_exec("stop", "start"); - else - svc_exec("start", NULL); - return; - } - - if (!(rc_service_state(service) & RC_SERVICE_STOPPED)) { - get_started_services(); - svc_stop(); - if (dry_run) - ewarn("Cannot calculate restart start dependencies" - " on a dry-run"); - } - - svc_start(); - start_services(restart_services); - rc_stringlist_free(restart_services); - restart_services = NULL; -} - -static bool -service_plugable(void) -{ - char *list, *p, *token; - bool allow = true, truefalse; - char *match = rc_conf_value("rc_hotplug"); - - if (!match) - match = rc_conf_value("rc_plug_services"); - if (!match) - return false; - - list = xstrdup(match); - p = list; - while ((token = strsep(&p, " "))) { - if (token[0] == '!') { - truefalse = false; - token++; - } else - truefalse = true; - - if (fnmatch(token, applet, 0) == 0) { - allow = truefalse; - break; - } - } -#ifdef DEBUG_MEMORY - free(list); -#endif - return allow; -} - -#include "_usage.h" -#define getoptstring "dDsSvl:Z" getoptstring_COMMON -#define extraopts "stop | start | restart | describe | zap" -static const struct option longopts[] = { - { "debug", 0, NULL, 'd'}, - { "dry-run", 0, NULL, 'Z'}, - { "ifstarted", 0, NULL, 's'}, - { "ifstopped", 0, NULL, 'S'}, - { "nodeps", 0, NULL, 'D'}, - { "lockfd", 1, NULL, 'l'}, - longopts_COMMON -}; -static const char *const longopts_help[] = { - "set xtrace when running the script", - "show what would be done", - "only run commands when started", - "only run commands when stopped", - "ignore dependencies", - "fd of the exclusive lock from rc", - longopts_help_COMMON -}; -#include "_usage.c" - -int -openrc_run(int argc, char **argv) -{ - bool doneone = false; - int retval, opt, depoptions = RC_DEP_TRACE; - RC_STRING *svc; - char path[PATH_MAX], lnk[PATH_MAX]; - char *dir, *save = NULL, *saveLnk = NULL; - char pidstr[10]; - size_t l = 0, ll; - const char *file; - struct stat stbuf; - - /* Show help if insufficient args */ - if (argc < 2 || !exists(argv[1])) { - fprintf(stderr, "openrc-run should not be run directly\n"); - exit(EXIT_FAILURE); - } - - if (stat(argv[1], &stbuf) != 0) { - fprintf(stderr, "openrc-run `%s': %s\n", - argv[1], strerror(errno)); - exit(EXIT_FAILURE); - } - - atexit(cleanup); - - /* We need to work out the real full path to our service. - * This works fine, provided that we ONLY allow multiplexed services - * to exist in the same directory as the master link. - * Also, the master link as to be a real file in the init dir. */ - if (!realpath(argv[1], path)) { - fprintf(stderr, "realpath: %s\n", strerror(errno)); - exit(EXIT_FAILURE); - } - memset(lnk, 0, sizeof(lnk)); - if (readlink(argv[1], lnk, sizeof(lnk)-1)) { - dir = dirname(path); - if (strchr(lnk, '/')) { - save = xstrdup(dir); - saveLnk = xstrdup(lnk); - dir = dirname(saveLnk); - if (strcmp(dir, save) == 0) - file = basename_c(argv[1]); - else - file = basename_c(lnk); - dir = save; - } else - file = basename_c(argv[1]); - ll = strlen(dir) + strlen(file) + 2; - service = xmalloc(ll); - snprintf(service, ll, "%s/%s", dir, file); - if (stat(service, &stbuf) != 0) { - free(service); - service = xstrdup(lnk); - } - free(save); - free(saveLnk); - } - if (!service) - service = xstrdup(path); - applet = basename_c(service); - - if (argc < 3) - usage(EXIT_FAILURE); - - /* Change dir to / to ensure all init scripts don't use stuff in pwd */ - if (chdir("/") == -1) - eerror("chdir: %s", strerror(errno)); - - if ((runlevel = xstrdup(getenv("RC_RUNLEVEL"))) == NULL) { - env_filter(); - env_config(); - runlevel = rc_runlevel_get(); - } - - setenv("EINFO_LOG", service, 1); - setenv("RC_SVCNAME", applet, 1); - - /* Set an env var so that we always know our pid regardless of any - subshells the init script may create so that our mark_service_* - functions can always instruct us of this change */ - snprintf(pidstr, sizeof(pidstr), "%d", (int) getpid()); - setenv("RC_RUNSCRIPT_PID", pidstr, 1); - - /* eprefix is kinda klunky, but it works for our purposes */ - if (rc_conf_yesno("rc_parallel")) { - /* Get the longest service name */ - services = rc_services_in_runlevel(NULL); - TAILQ_FOREACH(svc, services, entries) { - ll = strlen(svc->value); - if (ll > l) - l = ll; - } - rc_stringlist_free(services); - services = NULL; - ll = strlen(applet); - if (ll > l) - l = ll; - - /* Make our prefix string */ - prefix = xmalloc(sizeof(char) * l + 1); - ll = strlen(applet); - memcpy(prefix, applet, ll); - memset(prefix + ll, ' ', l - ll); - memset(prefix + l, 0, 1); - eprefix(prefix); - } - -#ifdef HAVE_SELINUX - /* Ok, we are ready to go, so setup selinux if applicable */ - selinux_setup(argv); -#endif - - deps = true; - - /* Punt the first arg as its our service name */ - argc--; - argv++; - - /* Right then, parse any options there may be */ - while ((opt = getopt_long(argc, argv, getoptstring, - longopts, (int *)0)) != -1) - switch (opt) { - case 'd': - setenv("RC_DEBUG", "YES", 1); - break; - case 'l': - exclusive_fd = atoi(optarg); - fcntl(exclusive_fd, F_SETFD, - fcntl(exclusive_fd, F_GETFD, 0) | FD_CLOEXEC); - break; - case 's': - if (!(rc_service_state(service) & RC_SERVICE_STARTED)) - exit(EXIT_FAILURE); - break; - case 'S': - if (!(rc_service_state(service) & RC_SERVICE_STOPPED)) - exit(EXIT_FAILURE); - break; - case 'D': - deps = false; - break; - case 'Z': - dry_run = true; - break; - case_RC_COMMON_GETOPT - } - - /* If we're changing runlevels and not called by rc then we cannot - work with any dependencies */ - if (deps && getenv("RC_PID") == NULL && - (rc_runlevel_starting() || rc_runlevel_stopping())) - deps = false; - - /* Save the IN_BACKGROUND env flag so it's ONLY passed to the service - that is being called and not any dependents */ - if (getenv("IN_BACKGROUND")) { - ibsave = xstrdup(getenv("IN_BACKGROUND")); - in_background = rc_yesno(ibsave); - unsetenv("IN_BACKGROUND"); - } - - if (rc_yesno(getenv("IN_HOTPLUG"))) { - if (!service_plugable()) - eerrorx("%s: not allowed to be hotplugged", applet); - in_background = true; - } - - /* Setup a signal handler */ - signal_setup(SIGHUP, handle_signal); - signal_setup(SIGINT, handle_signal); - signal_setup(SIGQUIT, handle_signal); - signal_setup(SIGTERM, handle_signal); - signal_setup(SIGCHLD, handle_signal); - - /* Load our plugins */ - rc_plugin_load(); - - applet_list = rc_stringlist_new(); - rc_stringlist_add(applet_list, applet); - - /* Now run each option */ - retval = EXIT_SUCCESS; - while (optind < argc) { - optarg = argv[optind++]; - - /* Abort on a sighup here */ - if (sighup) - exit (EXIT_FAILURE); - - /* Export the command we're running. - This is important as we stamp on the restart function now but - some start/stop routines still need to behave differently if - restarting. */ - unsetenv("RC_CMD"); - setenv("RC_CMD", optarg, 1); - - doneone = true; - - if (strcmp(optarg, "describe") == 0 || - strcmp(optarg, "help") == 0 || - strcmp(optarg, "depend") == 0) - { - save = prefix; - eprefix(NULL); - prefix = NULL; - svc_exec(optarg, NULL); - eprefix(save); - prefix = save; - } else if (strcmp(optarg, "ineed") == 0 || - strcmp(optarg, "iuse") == 0 || - strcmp(optarg, "needsme") == 0 || - strcmp(optarg, "usesme") == 0 || - strcmp(optarg, "iafter") == 0 || - strcmp(optarg, "ibefore") == 0 || - strcmp(optarg, "iprovide") == 0) - { - errno = 0; - if (rc_conf_yesno("rc_depend_strict") || - errno == ENOENT) - depoptions |= RC_DEP_STRICT; - - if (!deptree && - ((deptree = _rc_deptree_load(0, NULL)) == NULL)) - eerrorx("failed to load deptree"); - - tmplist = rc_stringlist_new(); - rc_stringlist_add(tmplist, optarg); - services = rc_deptree_depends(deptree, tmplist, - applet_list, - runlevel, depoptions); - rc_stringlist_free(tmplist); - TAILQ_FOREACH(svc, services, entries) - printf("%s ", svc->value); - printf ("\n"); - rc_stringlist_free(services); - services = NULL; - } else if (strcmp (optarg, "status") == 0) { - save = prefix; - eprefix(NULL); - prefix = NULL; - retval = svc_exec("status", NULL); - } else { - if (strcmp(optarg, "conditionalrestart") == 0 || - strcmp(optarg, "condrestart") == 0) - { - if (rc_service_state(service) & - RC_SERVICE_STARTED) - svc_restart(); - } else if (strcmp(optarg, "restart") == 0) { - svc_restart(); - } else if (strcmp(optarg, "start") == 0) { - svc_start(); - } else if (strcmp(optarg, "stop") == 0 || strcmp(optarg, "pause") == 0) { - if (strcmp(optarg, "pause") == 0) { - ewarn("WARNING: 'pause' is deprecated; please use '--nodeps stop'"); - deps = false; - } - if (deps && in_background) - get_started_services(); - if (svc_stop() == 1) - continue; /* Service has been stopped already */ - if (deps) { - if (!in_background && - !rc_runlevel_stopping() && - rc_service_state(service) & - RC_SERVICE_STOPPED) - unhotplug(); - - if (in_background && - rc_service_state(service) & - RC_SERVICE_INACTIVE) - { - TAILQ_FOREACH(svc, - restart_services, - entries) - if (rc_service_state(svc->value) & - RC_SERVICE_STOPPED) - rc_service_schedule_start(service, svc->value); - } - } - } else if (strcmp(optarg, "zap") == 0) { - einfo("Manually resetting %s to stopped state", - applet); - if (!rc_service_mark(applet, - RC_SERVICE_STOPPED)) - eerrorx("rc_service_mark: %s", - strerror(errno)); - unhotplug(); - } else - retval = svc_exec(optarg, NULL); - - /* We should ensure this list is empty after - * an action is done */ - rc_stringlist_free(restart_services); - restart_services = NULL; - } - - if (!doneone) - usage(EXIT_FAILURE); - } - - return retval; -} - -int -runscript(int argc, char **argv) -{ - ewarnv("runscript is deprecated; please use openrc-run instead."); - return (openrc_run(argc, argv)); -} diff --git a/src/rc/start-stop-daemon.c b/src/rc/start-stop-daemon.c deleted file mode 100644 index 6229bbfd..00000000 --- a/src/rc/start-stop-daemon.c +++ /dev/null @@ -1,1361 +0,0 @@ -/* - start-stop-daemon - Starts, stops, tests and signals daemons - - This is essentially a ground up re-write of Debians - start-stop-daemon for cleaner code and to integrate into our RC - system so we can monitor daemons a little. -*/ - -/* - * Copyright (c) 2007-2009 Roy Marples <roy@marples.name> - * - * 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. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. - */ - -/* nano seconds */ -#define POLL_INTERVAL 20000000 -#define WAIT_PIDFILE 500000000 -#define ONE_SECOND 1000000000 -#define ONE_MS 1000000 - -#include <sys/types.h> -#include <sys/ioctl.h> -#include <sys/resource.h> -#include <sys/stat.h> -#include <termios.h> -#include <sys/time.h> -#include <sys/wait.h> - -#ifdef __linux__ -#include <sys/syscall.h> /* For io priority */ -#endif - -#include <ctype.h> -#include <errno.h> -#include <fcntl.h> -#include <getopt.h> -#include <limits.h> -#include <grp.h> -#include <pwd.h> -#include <signal.h> -#include <stddef.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <time.h> -#include <unistd.h> - -#ifdef HAVE_PAM -#include <security/pam_appl.h> - -/* We are not supporting authentication conversations */ -static struct pam_conv conv = { NULL, NULL}; -#endif - -#include "builtins.h" -#include "einfo.h" -#include "queue.h" -#include "rc.h" -#include "rc-misc.h" - -typedef struct scheduleitem -{ - enum - { - SC_TIMEOUT, - SC_SIGNAL, - SC_GOTO, - SC_FOREVER - } type; - int value; - struct scheduleitem *gotoitem; - TAILQ_ENTRY(scheduleitem) entries; -} SCHEDULEITEM; -TAILQ_HEAD(, scheduleitem) schedule; -static char **nav; - -extern const char *applet; -static char *changeuser, *ch_root, *ch_dir; - -extern char **environ; - -#if !defined(SYS_ioprio_set) && defined(__NR_ioprio_set) -# define SYS_ioprio_set __NR_ioprio_set -#endif -#if !defined(__DragonFly__) -static inline int ioprio_set(int which, int who, int ioprio) -{ -#ifdef SYS_ioprio_set - return syscall(SYS_ioprio_set, which, who, ioprio); -#else - return 0; -#endif -} -#endif - -static 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); -} - -#ifdef DEBUG_MEMORY -static void -cleanup(void) -{ - free(changeuser); - free(nav); - free_schedulelist(); -} -#endif - -static int -parse_signal(const char *sig) -{ - typedef struct signalpair - { - const char *name; - int signal; - } SIGNALPAIR; - - static const SIGNALPAIR signallist[] = { - { "ABRT", SIGABRT }, - { "ALRM", SIGALRM }, - { "FPE", SIGFPE }, - { "HUP", SIGHUP }, - { "ILL", SIGILL }, - { "INT", SIGINT }, - { "KILL", SIGKILL }, - { "PIPE", SIGPIPE }, - { "QUIT", SIGQUIT }, - { "SEGV", SIGSEGV }, - { "TERM", SIGTERM }, - { "USR1", SIGUSR1 }, - { "USR2", SIGUSR2 }, - { "CHLD", SIGCHLD }, - { "CONT", SIGCONT }, - { "STOP", SIGSTOP }, - { "TSTP", SIGTSTP }, - { "TTIN", SIGTTIN }, - { "TTOU", SIGTTOU }, - { "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 *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(after_hyph)) != -1)) - { - item->type = SC_SIGNAL; - item->value = (int)sig; - } else - eerrorx("%s: invalid schedule item `%s'", applet, string); - - return item; -} - -static void -parse_schedule(const char *string, int timeout) -{ - char buffer[20]; - const char *slash; - int count = 0; - SCHEDULEITEM *repeatat = NULL; - size_t len; - SCHEDULEITEM *item; - - 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(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; -} - -static pid_t -get_pid(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; -} - -/* return number of processed killed, -1 on error */ -static int -do_stop(const char *exec, const char *const *argv, - pid_t pid, uid_t uid,int sig, bool test) -{ - RC_PIDLIST *pids; - RC_PID *pi; - RC_PID *np; - bool killed; - int nkilled = 0; - - if (pid) - pids = rc_find_pids(NULL, NULL, 0, pid); - else - pids = rc_find_pids(exec, argv, uid, pid); - - 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 { - ebeginv("Sending signal %d to PID %d", sig, pi->pid); - errno = 0; - killed = (kill(pi->pid, sig) == 0 || - errno == ESRCH ? true : false); - eendv(killed ? 0 : 1, - "%s: failed to send signal %d to PID %d: %s", - applet, sig, pi->pid, strerror(errno)); - if (!killed) { - nkilled = -1; - } else { - if (nkilled != -1) - nkilled++; - } - } - free(pi); - } - - free(pids); - return nkilled; -} - -static int -run_stop_schedule(const char *exec, const char *const *argv, - const char *pidfile, uid_t uid, - bool test, bool progress) -{ - SCHEDULEITEM *item = TAILQ_FIRST(&schedule); - int nkilled = 0; - int tkilled = 0; - int nrunning = 0; - long nloops, nsecs; - struct timespec ts; - pid_t pid = 0; - const char *const *p; - bool progressed = false; - - if (exec) - einfov("Will stop %s", exec); - if (pidfile) - einfov("Will stop PID in pidfile `%s'", pidfile); - if (uid) - einfov("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"); - } - } - - if (pidfile) { - pid = get_pid(pidfile); - if (pid == -1) - return 0; - } - - while (item) { - switch (item->type) { - case SC_GOTO: - item = item->gotoitem; - continue; - - case SC_SIGNAL: - nrunning = 0; - nkilled = do_stop(exec, argv, pid, uid, item->value, test); - 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_TIMEOUT: - if (item->value < 1) { - item = NULL; - break; - } - - ts.tv_sec = 0; - ts.tv_nsec = POLL_INTERVAL; - - for (nsecs = 0; nsecs < item->value; nsecs++) { - for (nloops = 0; - nloops < ONE_SECOND / POLL_INTERVAL; - nloops++) - { - if ((nrunning = do_stop(exec, argv, - pid, uid, 0, test)) == 0) - return 0; - - - if (nanosleep(&ts, NULL) == -1) { - if (progressed) { - printf("\n"); - progressed = false; - } - if (errno == EINTR) - eerror("%s: caught an" - " interrupt", applet); - else { - 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 (nrunning == 1) - eerror("%s: %d process refused to stop", applet, nrunning); - else - eerror("%s: %d process(es) refused to stop", applet, nrunning); - - return -nrunning; -} - -static void -handle_signal(int sig) -{ - int status; - int serrno = errno; - char signame[10] = { '\0' }; - - switch (sig) { - case SIGINT: - if (!signame[0]) - snprintf(signame, sizeof(signame), "SIGINT"); - /* FALLTHROUGH */ - case SIGTERM: - if (!signame[0]) - snprintf(signame, sizeof(signame), "SIGTERM"); - /* FALLTHROUGH */ - case SIGQUIT: - if (!signame[0]) - snprintf(signame, sizeof(signame), "SIGQUIT"); - eerrorx("%s: caught %s, aborting", applet, signame); - /* NOTREACHED */ - - case SIGCHLD: - for (;;) { - if (waitpid(-1, &status, WNOHANG) < 0) { - if (errno != ECHILD) - eerror("%s: waitpid: %s", - applet, strerror(errno)); - break; - } - } - break; - - default: - eerror("%s: caught unknown signal %d", applet, sig); - } - - /* Restore errno */ - errno = serrno; -} - -static char * -expand_home(const char *home, const char *path) -{ - char *opath, *ppath, *p, *nh; - size_t len; - struct passwd *pw; - - if (!path || *path != '~') - return xstrdup(path); - - opath = ppath = xstrdup(path); - if (ppath[1] != '/' && ppath[1] != '\0') { - p = strchr(ppath + 1, '/'); - if (p) - *p = '\0'; - pw = getpwnam(ppath + 1); - if (pw) { - home = pw->pw_dir; - ppath = p; - if (ppath) - *ppath = '/'; - } else - home = NULL; - } else - ppath++; - - if (!home) { - free(opath); - return xstrdup(path); - } - if (!ppath) { - free(opath); - return xstrdup(home); - } - - len = strlen(ppath) + strlen(home) + 1; - nh = xmalloc(len); - snprintf(nh, len, "%s%s", home, ppath); - free(opath); - return nh; -} - -#include "_usage.h" -#define getoptstring "I:KN:PR:Sa:bc:d:e:g:ik:mn:op:s:tu:r:w:x:1:2:" getoptstring_COMMON -static const struct option longopts[] = { - { "ionice", 1, NULL, 'I'}, - { "stop", 0, NULL, 'K'}, - { "nicelevel", 1, NULL, 'N'}, - { "retry", 1, NULL, 'R'}, - { "start", 0, NULL, 'S'}, - { "startas", 1, NULL, 'a'}, - { "background", 0, NULL, 'b'}, - { "chuid", 1, NULL, 'c'}, - { "chdir", 1, NULL, 'd'}, - { "env", 1, NULL, 'e'}, - { "umask", 1, NULL, 'k'}, - { "group", 1, NULL, 'g'}, - { "interpreted", 0, NULL, 'i'}, - { "make-pidfile", 0, NULL, 'm'}, - { "name", 1, NULL, 'n'}, - { "oknodo", 0, NULL, 'o'}, - { "pidfile", 1, NULL, 'p'}, - { "signal", 1, NULL, 's'}, - { "test", 0, NULL, 't'}, - { "user", 1, NULL, 'u'}, - { "chroot", 1, NULL, 'r'}, - { "wait", 1, NULL, 'w'}, - { "exec", 1, NULL, 'x'}, - { "stdout", 1, NULL, '1'}, - { "stderr", 1, NULL, '2'}, - { "progress", 0, NULL, 'P'}, - longopts_COMMON -}; -static const char * const longopts_help[] = { - "Set an ionice class:data when starting", - "Stop daemon", - "Set a nicelevel when starting", - "Retry schedule to use when stopping", - "Start daemon", - "deprecated, use --exec or --name", - "Force daemon to background", - "deprecated, use --user", - "Change the PWD", - "Set an environment string", - "Set the umask for the daemon", - "Change the process group", - "Match process name by interpreter", - "Create a pidfile", - "Match process name", - "deprecated", - "Match pid found in this file", - "Send a different signal", - "Test actions, don't do them", - "Change the process user", - "Chroot to this directory", - "Milliseconds to wait for daemon start", - "Binary to start/stop", - "Redirect stdout to file", - "Redirect stderr to file", - "Print dots each second while waiting", - longopts_help_COMMON -}; -#include "_usage.c" - -int -start_stop_daemon(int argc, char **argv) -{ - int devnull_fd = -1; -#ifdef TIOCNOTTY - int tty_fd = -1; -#endif - -#ifdef HAVE_PAM - pam_handle_t *pamh = NULL; - int pamr; - const char *const *pamenv = NULL; -#endif - - int opt; - bool start = false; - bool stop = false; - bool oknodo = false; - bool test = false; - char *exec = NULL; - char *startas = NULL; - char *name = NULL; - char *pidfile = NULL; - char *retry = NULL; - int sig = -1; - int nicelevel = 0, ionicec = -1, ioniced = 0; - bool background = false; - bool makepidfile = false; - bool interpreted = false; - bool progress = false; - uid_t uid = 0; - gid_t gid = 0; - char *home = NULL; - int tid = 0; - char *redirect_stderr = NULL; - char *redirect_stdout = NULL; - int stdout_fd; - int stderr_fd; - pid_t pid, spid; - int i; - char *svcname = getenv("RC_SVCNAME"); - RC_STRINGLIST *env_list; - RC_STRING *env; - char *tmp, *newpath, *np; - char *p; - char *token; - char exec_file[PATH_MAX]; - struct passwd *pw; - struct group *gr; - char line[130]; - FILE *fp; - size_t len; - mode_t numask = 022; - char **margv; - unsigned int start_wait = 0; - - TAILQ_INIT(&schedule); -#ifdef DEBUG_MEMORY - atexit(cleanup); -#endif - - signal_setup(SIGINT, handle_signal); - signal_setup(SIGQUIT, handle_signal); - signal_setup(SIGTERM, handle_signal); - - if ((tmp = getenv("SSD_NICELEVEL"))) - if (sscanf(tmp, "%d", &nicelevel) != 1) - eerror("%s: invalid nice level `%s' (SSD_NICELEVEL)", - applet, tmp); - - /* Get our user name and initial dir */ - p = getenv("USER"); - home = getenv("HOME"); - if (home == NULL || p == NULL) { - pw = getpwuid(getuid()); - if (pw != NULL) { - if (p == NULL) - setenv("USER", pw->pw_name, 1); - if (home == NULL) { - setenv("HOME", pw->pw_dir, 1); - home = pw->pw_dir; - } - } - } - - while ((opt = getopt_long(argc, argv, getoptstring, longopts, - (int *) 0)) != -1) - switch (opt) { - case 'I': /* --ionice */ - if (sscanf(optarg, "%d:%d", &ionicec, &ioniced) == 0) - eerrorx("%s: invalid ionice `%s'", - applet, optarg); - if (ionicec == 0) - ioniced = 0; - else if (ionicec == 3) - ioniced = 7; - ionicec <<= 13; /* class shift */ - break; - - case 'K': /* --stop */ - stop = true; - break; - - case 'N': /* --nice */ - if (sscanf(optarg, "%d", &nicelevel) != 1) - eerrorx("%s: invalid nice level `%s'", - applet, optarg); - break; - - case 'P': /* --progress */ - progress = true; - break; - - case 'R': /* --retry <schedule>|<timeout> */ - retry = optarg; - break; - - case 'S': /* --start */ - start = true; - break; - - case 'b': /* --background */ - background = true; - break; - - case 'c': /* --chuid <username>|<uid> */ - /* DEPRECATED */ - ewarn("WARNING: -c/--chuid is deprecated and will be removed in the future, please use -u/--user instead"); - case 'u': /* --user <username>|<uid> */ - { - p = optarg; - tmp = strsep(&p, ":"); - changeuser = xstrdup(tmp); - if (sscanf(tmp, "%d", &tid) != 1) - pw = getpwnam(tmp); - else - pw = getpwuid((uid_t)tid); - - if (pw == NULL) - eerrorx("%s: user `%s' not found", - applet, tmp); - uid = pw->pw_uid; - home = pw->pw_dir; - unsetenv("HOME"); - if (pw->pw_dir) - setenv("HOME", pw->pw_dir, 1); - unsetenv("USER"); - if (pw->pw_name) - setenv("USER", pw->pw_name, 1); - if (gid == 0) - gid = pw->pw_gid; - - if (p) { - tmp = strsep (&p, ":"); - if (sscanf(tmp, "%d", &tid) != 1) - gr = getgrnam(tmp); - else - gr = getgrgid((gid_t) tid); - - if (gr == NULL) - eerrorx("%s: group `%s'" - " not found", - applet, tmp); - gid = gr->gr_gid; - } - } - break; - - case 'd': /* --chdir /new/dir */ - ch_dir = optarg; - break; - - case 'e': /* --env */ - putenv(optarg); - break; - - case 'g': /* --group <group>|<gid> */ - if (sscanf(optarg, "%d", &tid) != 1) - gr = getgrnam(optarg); - else - gr = getgrgid((gid_t)tid); - if (gr == NULL) - eerrorx("%s: group `%s' not found", - applet, optarg); - gid = gr->gr_gid; - break; - - case 'i': /* --interpreted */ - interpreted = true; - break; - - case 'k': - if (parse_mode(&numask, optarg)) - eerrorx("%s: invalid mode `%s'", - applet, optarg); - break; - - case 'm': /* --make-pidfile */ - makepidfile = true; - break; - - case 'n': /* --name <process-name> */ - name = optarg; - break; - - case 'o': /* --oknodo */ - /* DEPRECATED */ - ewarn("WARNING: -o/--oknodo is deprecated and will be removed in the future"); - oknodo = true; - break; - - case 'p': /* --pidfile <pid-file> */ - pidfile = optarg; - break; - - case 's': /* --signal <signal> */ - sig = parse_signal(optarg); - break; - - case 't': /* --test */ - test = true; - break; - - case 'r': /* --chroot /new/root */ - ch_root = optarg; - break; - - case 'a': /* --startas <name> */ - /* DEPRECATED */ - ewarn("WARNING: -a/--startas is deprecated and will be removed in the future, please use -x/--exec or -n/--name instead"); - startas = optarg; - break; - case 'w': - if (sscanf(optarg, "%d", &start_wait) != 1) - eerrorx("%s: `%s' not a number", - applet, optarg); - break; - case 'x': /* --exec <executable> */ - exec = optarg; - break; - - case '1': /* --stdout /path/to/stdout.lgfile */ - redirect_stdout = optarg; - break; - - case '2': /* --stderr /path/to/stderr.logfile */ - redirect_stderr = optarg; - break; - - case_RC_COMMON_GETOPT - } - - endpwent(); - argc -= optind; - argv += optind; - - /* Allow start-stop-daemon --signal HUP --exec /usr/sbin/dnsmasq - * instead of forcing --stop --oknodo as well */ - if (!start && - !stop && - sig != SIGINT && - sig != SIGTERM && - sig != SIGQUIT && - sig != SIGKILL) - oknodo = true; - - if (!exec) - exec = startas; - else if (!name) - name = startas; - - if (!exec) { - exec = *argv; - if (!exec) - exec = name; - if (name && start) - *argv = name; - } else if (name) - *--argv = name; - else if (exec) - *--argv = exec; - - if (stop || sig != -1) { - if (sig == -1) - sig = SIGTERM; - if (!*argv && !pidfile && !name && !uid) - eerrorx("%s: --stop needs --exec, --pidfile," - " --name or --user", applet); - if (background) - eerrorx("%s: --background is only relevant with" - " --start", applet); - if (makepidfile) - eerrorx("%s: --make-pidfile is only relevant with" - " --start", applet); - if (redirect_stdout || redirect_stderr) - eerrorx("%s: --stdout and --stderr are only relevant" - " with --start", applet); - } else { - if (!exec) - eerrorx("%s: nothing to start", applet); - if (makepidfile && !pidfile) - eerrorx("%s: --make-pidfile is only relevant with" - " --pidfile", applet); - if ((redirect_stdout || redirect_stderr) && !background) - eerrorx("%s: --stdout and --stderr are only relevant" - " with --background", applet); - } - - /* Expand ~ */ - if (ch_dir && *ch_dir == '~') - ch_dir = expand_home(home, ch_dir); - if (ch_root && *ch_root == '~') - ch_root = expand_home(home, ch_root); - if (exec) { - if (*exec == '~') - exec = expand_home(home, exec); - - /* Validate that the binary exists if we are starting */ - if (*exec == '/' || *exec == '.') { - /* Full or relative path */ - if (ch_root) - snprintf(exec_file, sizeof(exec_file), - "%s/%s", ch_root, exec); - else - snprintf(exec_file, sizeof(exec_file), - "%s", exec); - } else { - /* Something in $PATH */ - p = tmp = xstrdup(getenv("PATH")); - *exec_file = '\0'; - while ((token = strsep(&p, ":"))) { - if (ch_root) - snprintf(exec_file, sizeof(exec_file), - "%s/%s/%s", - ch_root, token, exec); - else - snprintf(exec_file, sizeof(exec_file), - "%s/%s", token, exec); - if (exists(exec_file)) - break; - *exec_file = '\0'; - } - free(tmp); - } - } - if (start && !exists(exec_file)) { - eerror("%s: %s does not exist", applet, - *exec_file ? exec_file : exec); - exit(EXIT_FAILURE); - } - - /* If we don't have a pidfile we should check if it's interpreted - * or not. If it we, we need to pass the interpreter through - * to our daemon calls to find it correctly. */ - if (interpreted && !pidfile) { - fp = fopen(exec_file, "r"); - if (fp) { - p = fgets(line, sizeof(line), fp); - fclose(fp); - if (p != NULL && line[0] == '#' && line[1] == '!') { - p = line + 2; - /* Strip leading spaces */ - while (*p == ' ' || *p == '\t') - p++; - /* Remove the trailing newline */ - len = strlen(p) - 1; - if (p[len] == '\n') - p[len] = '\0'; - token = strsep(&p, " "); - strncpy(exec_file, token, sizeof(exec_file)); - opt = 0; - for (nav = argv; *nav; nav++) - opt++; - nav = xmalloc(sizeof(char *) * (opt + 3)); - nav[0] = exec_file; - len = 1; - if (p) - nav[len++] = p; - for (i = 0; i < opt; i++) - nav[i + len] = argv[i]; - nav[i + len] = '\0'; - } - } - } - margv = nav ? nav : argv; - - if (stop || sig != -1) { - if (sig == -1) - sig = SIGTERM; - if (!stop) - oknodo = true; - if (retry) - parse_schedule(retry, sig); - else if (test || oknodo) - parse_schedule("0", sig); - else - parse_schedule(NULL, sig); - i = run_stop_schedule(exec, (const char *const *)margv, - pidfile, uid, test, progress); - - if (i < 0) - /* We failed to stop something */ - exit(EXIT_FAILURE); - if (test || oknodo) - return i > 0 ? EXIT_SUCCESS : EXIT_FAILURE; - - /* Even if we have not actually killed anything, we should - * remove information about it as it may have unexpectedly - * crashed out. We should also return success as the end - * result would be the same. */ - if (pidfile && exists(pidfile)) - unlink(pidfile); - if (svcname) - rc_service_daemon_set(svcname, exec, - (const char *const *)argv, - pidfile, false); - exit(EXIT_SUCCESS); - } - - if (pidfile) - pid = get_pid(pidfile); - else - pid = 0; - - if (do_stop(exec, (const char * const *)margv, pid, uid, - 0, test) > 0) - eerrorx("%s: %s is already running", applet, exec); - - if (test) { - if (rc_yesno(getenv("EINFO_QUIET"))) - exit (EXIT_SUCCESS); - - einfon("Would start"); - while (argc-- >= 0) - printf(" %s", *argv++); - printf("\n"); - eindent(); - if (uid != 0) - einfo("as user id %d", uid); - if (gid != 0) - einfo("as group id %d", gid); - if (ch_root) - einfo("in root `%s'", ch_root); - if (ch_dir) - einfo("in dir `%s'", ch_dir); - if (nicelevel != 0) - einfo("with a priority of %d", nicelevel); - if (name) - einfo ("with a process name of %s", name); - eoutdent(); - exit(EXIT_SUCCESS); - } - - ebeginv("Detaching to start `%s'", exec); - eindentv(); - - /* Remove existing pidfile */ - if (pidfile) - unlink(pidfile); - - if (background) - signal_setup(SIGCHLD, handle_signal); - - if ((pid = fork()) == -1) - eerrorx("%s: fork: %s", applet, strerror(errno)); - - /* Child process - lets go! */ - if (pid == 0) { - pid_t mypid = getpid(); - umask(numask); - -#ifdef TIOCNOTTY - tty_fd = open("/dev/tty", O_RDWR); -#endif - - devnull_fd = open("/dev/null", O_RDWR); - - if (nicelevel) { - if (setpriority(PRIO_PROCESS, mypid, nicelevel) == -1) - eerrorx("%s: setpritory %d: %s", - applet, nicelevel, - strerror(errno)); - } - - if (ionicec != -1 && - ioprio_set(1, mypid, ionicec | ioniced) == -1) - eerrorx("%s: ioprio_set %d %d: %s", applet, - ionicec, ioniced, strerror(errno)); - - if (ch_root && chroot(ch_root) < 0) - eerrorx("%s: chroot `%s': %s", - applet, ch_root, strerror(errno)); - - if (ch_dir && chdir(ch_dir) < 0) - eerrorx("%s: chdir `%s': %s", - applet, ch_dir, strerror(errno)); - - if (makepidfile && pidfile) { - fp = fopen(pidfile, "w"); - if (! fp) - eerrorx("%s: fopen `%s': %s", applet, pidfile, - strerror(errno)); - fprintf(fp, "%d\n", mypid); - fclose(fp); - } - -#ifdef HAVE_PAM - if (changeuser != NULL) { - pamr = pam_start("start-stop-daemon", - changeuser, &conv, &pamh); - - if (pamr == PAM_SUCCESS) - pamr = pam_acct_mgmt(pamh, PAM_SILENT); - if (pamr == PAM_SUCCESS) - pamr = pam_open_session(pamh, PAM_SILENT); - if (pamr != PAM_SUCCESS) - eerrorx("%s: pam error: %s", - applet, pam_strerror(pamh, pamr)); - } -#endif - - if (gid && setgid(gid)) - eerrorx("%s: unable to set groupid to %d", - applet, gid); - if (changeuser && initgroups(changeuser, gid)) - eerrorx("%s: initgroups (%s, %d)", - applet, changeuser, gid); - if (uid && setuid(uid)) - eerrorx ("%s: unable to set userid to %d", - applet, uid); - - /* Close any fd's to the passwd database */ - endpwent(); - -#ifdef TIOCNOTTY - ioctl(tty_fd, TIOCNOTTY, 0); - close(tty_fd); -#endif - - /* Clean the environment of any RC_ variables */ - env_list = rc_stringlist_new(); - i = 0; - while (environ[i]) - rc_stringlist_add(env_list, environ[i++]); - -#ifdef HAVE_PAM - if (changeuser != NULL) { - pamenv = (const char *const *)pam_getenvlist(pamh); - if (pamenv) { - while (*pamenv) { - /* Don't add strings unless they set a var */ - if (strchr(*pamenv, '=')) - putenv(xstrdup(*pamenv)); - else - unsetenv(*pamenv); - pamenv++; - } - } - } -#endif - - TAILQ_FOREACH(env, env_list, entries) { - if ((strncmp(env->value, "RC_", 3) == 0 && - strncmp(env->value, "RC_SERVICE=", 10) != 0 && - strncmp(env->value, "RC_SVCNAME=", 10) != 0) || - strncmp(env->value, "SSD_NICELEVEL=", 14) == 0) - { - p = strchr(env->value, '='); - *p = '\0'; - unsetenv(env->value); - continue; - } - } - rc_stringlist_free(env_list); - - /* For the path, remove the rcscript bin dir from it */ - if ((token = getenv("PATH"))) { - len = strlen(token); - newpath = np = xmalloc(len + 1); - while (token && *token) { - p = strchr(token, ':'); - if (p) { - *p++ = '\0'; - while (*p == ':') - p++; - } - if (strcmp(token, RC_LIBEXECDIR "/bin") != 0 && - strcmp(token, RC_LIBEXECDIR "/sbin") != 0) - { - len = strlen(token); - if (np != newpath) - *np++ = ':'; - memcpy(np, token, len); - np += len; - } - token = p; - } - *np = '\0'; - unsetenv("PATH"); - setenv("PATH", newpath, 1); - } - - stdout_fd = devnull_fd; - stderr_fd = devnull_fd; - if (redirect_stdout) { - if ((stdout_fd = open(redirect_stdout, - O_WRONLY | O_CREAT | O_APPEND, - S_IRUSR | S_IWUSR)) == -1) - eerrorx("%s: unable to open the logfile" - " for stdout `%s': %s", - applet, redirect_stdout, strerror(errno)); - } - if (redirect_stderr) { - if ((stderr_fd = open(redirect_stderr, - O_WRONLY | O_CREAT | O_APPEND, - S_IRUSR | S_IWUSR)) == -1) - eerrorx("%s: unable to open the logfile" - " for stderr `%s': %s", - applet, redirect_stderr, strerror(errno)); - } - - /* We don't redirect stdin as some daemons may need it */ - if (background || redirect_stdout || rc_yesno(getenv("EINFO_QUIET"))) - dup2(stdout_fd, STDOUT_FILENO); - if (background || redirect_stderr || rc_yesno(getenv("EINFO_QUIET"))) - dup2(stderr_fd, STDERR_FILENO); - - for (i = getdtablesize() - 1; i >= 3; --i) - close(i); - - setsid(); - execvp(exec, argv); -#ifdef HAVE_PAM - if (changeuser != NULL && pamr == PAM_SUCCESS) - pam_close_session(pamh, PAM_SILENT); -#endif - eerrorx("%s: failed to exec `%s': %s", - applet, exec,strerror(errno)); - } - - /* Parent process */ - if (!background) { - /* As we're not backgrounding the process, wait for our pid - * to return */ - i = 0; - spid = pid; - - do { - pid = waitpid(spid, &i, 0); - if (pid < 1) { - eerror("waitpid %d: %s", - spid, strerror(errno)); - return -1; - } - } while (!WIFEXITED(i) && !WIFSIGNALED(i)); - if (!WIFEXITED(i) || WEXITSTATUS(i) != 0) { - eerror("%s: failed to start `%s'", applet, exec); - exit(EXIT_FAILURE); - } - pid = spid; - } - - /* Wait a little bit and check that process is still running - We do this as some badly written daemons fork and then barf */ - if (start_wait == 0 && - ((p = getenv("SSD_STARTWAIT")) || - (p = rc_conf_value("rc_start_wait")))) - { - if (sscanf(p, "%u", &start_wait) != 1) - start_wait = 0; - } - - if (start_wait > 0) { - struct timespec ts; - bool alive = false; - - ts.tv_sec = start_wait / 1000; - ts.tv_nsec = (start_wait % 1000) * ONE_MS; - if (nanosleep(&ts, NULL) == -1) { - if (errno == EINTR) - eerror("%s: caught an interrupt", applet); - else { - eerror("%s: nanosleep: %s", - applet, strerror(errno)); - return 0; - } - } - if (background) { - if (kill(pid, 0) == 0) - alive = true; - } else { - if (pidfile) { - pid = get_pid(pidfile); - if (pid == -1) { - eerrorx("%s: did not " - "create a valid" - " pid in `%s'", - applet, pidfile); - } - } else - pid = 0; - if (do_stop(exec, (const char *const *)margv, - pid, uid, 0, test) > 0) - alive = true; - } - - if (!alive) - eerrorx("%s: %s died", applet, exec); - } - - if (svcname) - rc_service_daemon_set(svcname, exec, - (const char *const *)margv, pidfile, true); - - exit(EXIT_SUCCESS); - /* NOTREACHED */ -} diff --git a/src/rc/start-stop-daemon.pam b/src/rc/start-stop-daemon.pam deleted file mode 100644 index a1bada22..00000000 --- a/src/rc/start-stop-daemon.pam +++ /dev/null @@ -1,6 +0,0 @@ -#%PAM-1.0 - -auth required pam_permit.so -account required pam_permit.so -password required pam_deny.so -session optional pam_limits.so diff --git a/src/rc/swclock.c b/src/rc/swclock.c deleted file mode 100644 index 0fb518b5..00000000 --- a/src/rc/swclock.c +++ /dev/null @@ -1,119 +0,0 @@ -/* - swclock.c - Sets the system time from the mtime of the given file. - This is useful for systems who do not have a working RTC and rely on ntp. - OpenRC relies on the correct system time for a lot of operations so this is needed - quite early. -*/ - -/* - * Copyright (c) 2009 Roy Marples <roy@marples.name> - * - * 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. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. - */ - -#include <sys/time.h> -#include <sys/types.h> -#include <sys/stat.h> - -#include <errno.h> -#include <fcntl.h> -#include <getopt.h> -#include <unistd.h> -#include <utime.h> - -#include "builtins.h" -#include "einfo.h" -#include "rc-misc.h" - -#define RC_SHUTDOWNTIME RC_SVCDIR "/shutdowntime" - -extern const char *applet; - -#include "_usage.h" -#define extraopts "file" -#define getoptstring "sw" getoptstring_COMMON -static const struct option longopts[] = { - { "save", 0, NULL, 's' }, - { "warn", 0, NULL, 'w' }, - longopts_COMMON -}; -static const char * const longopts_help[] = { - "saves the time", - "no error if no reference file", - longopts_help_COMMON -}; -#include "_usage.c" - -int -swclock(int argc, char **argv) -{ - int opt, sflag = 0, wflag = 0; - const char *file = RC_SHUTDOWNTIME; - struct stat sb; - struct timeval tv; - - while ((opt = getopt_long(argc, argv, getoptstring, - longopts, (int *) 0)) != -1) - { - switch (opt) { - case 's': - sflag = 1; - break; - case 'w': - wflag = 1; - break; - case_RC_COMMON_GETOPT - } - } - - if (optind < argc) - file = argv[optind++]; - - if (sflag) { - if (stat(file, &sb) == -1) { - opt = open(file, O_WRONLY | O_CREAT, 0644); - if (opt == -1) - eerrorx("swclock: open: %s", strerror(errno)); - close(opt); - } else - if (utime(file, NULL) == -1) - eerrorx("swclock: utime: %s", strerror(errno)); - return 0; - } - - if (stat(file, &sb) == -1) { - if (wflag != 0 && errno == ENOENT) - ewarn("swclock: `%s': %s", file, strerror(errno)); - else - eerrorx("swclock: `%s': %s", file, strerror(errno)); - return 0; - } - - tv.tv_sec = sb.st_mtime; - tv.tv_usec = 0; - - if (settimeofday(&tv, NULL) == -1) - eerrorx("swclock: settimeofday: %s", strerror(errno)); - - return 0; -} |
