From 391d12db48754861b5cecac92ee3321597ee02c1 Mon Sep 17 00:00:00 2001 From: William Hubbs Date: Wed, 6 Apr 2022 10:51:55 -0500 Subject: migrate fully to meson build system - drop old build system - move shared include and source files to common directory - drop "rc-" prefix from shared include and source files - move executable-specific code to individual directories under src - adjust top-level .gitignore file for new build system This closes #489. --- .gitignore | 6 +- Makefile | 55 - Makefile.inc | 3 - NEWS.md | 4 + README.md | 58 +- bash-completion/Makefile | 11 - conf.d/Makefile | 20 - etc/.gitignore | 2 - etc/Makefile | 23 - init.d/.gitignore | 51 - init.d/Makefile | 37 - local.d/Makefile | 6 - man/Makefile | 45 - meson.build | 3 +- mk/cc.mk | 44 - mk/debug.mk | 23 - mk/depend.mk | 21 - mk/dist.mk | 50 - mk/gitignore.mk | 22 - mk/gitver.mk | 8 - mk/lib.mk | 77 -- mk/net.mk | 1 - mk/os-BSD.mk | 16 - mk/os-DragonFly.mk | 13 - mk/os-FreeBSD.mk | 15 - mk/os-GNU-kFreeBSD.mk | 18 - mk/os-GNU.mk | 15 - mk/os-Linux.mk | 40 - mk/os-NetBSD.mk | 14 - mk/os-prefix.mk | 8 - mk/os.mk | 19 - mk/pam.mk | 16 - mk/prog.mk | 48 - mk/scripts.mk | 64 -- mk/subdir.mk | 38 - mk/sys.mk | 71 -- mk/termcap.mk | 17 - pkgconfig/.gitignore | 2 - pkgconfig/Makefile | 12 - runlevels/Makefile | 102 -- scripts/.gitignore | 5 - scripts/Makefile | 30 - scripts/halt.in | 29 - scripts/meson.build | 45 - scripts/meson_script_links.sh | 19 - scripts/on_ac_power | 47 - scripts/poweroff.in | 28 - scripts/rc-sstat.in | 149 --- scripts/reboot.in | 30 - scripts/shutdown.in | 46 - sh/.gitignore | 7 - sh/Makefile | 33 - src/Makefile | 7 - src/checkpath/checkpath.c | 452 +++++++++ src/checkpath/meson.build | 9 + src/common/meson.build | 6 - src/common/version.h.in | 18 - src/common/version.in | 1 - src/einfo/einfo.c | 228 +++++ src/einfo/meson.build | 32 + src/fstabinfo/fstabinfo.c | 339 +++++++ src/fstabinfo/meson.build | 7 + src/halt/halt.in | 29 + src/halt/meson.build | 7 + src/includes/helpers.h | 174 ---- src/includes/queue.h | 846 ---------------- src/includes/rc-misc.h | 74 -- src/includes/rc-wtmp.h | 26 - src/is_newer_than/is_newer_than.c | 34 + src/is_newer_than/meson.build | 6 + src/is_older_than/is_older_than.c | 35 + src/is_older_than/meson.build | 6 + src/kill_all/kill_all.c | 260 +++++ src/kill_all/meson.build | 9 + src/libeinfo/.gitignore | 1 - src/libeinfo/Makefile | 12 - src/librc/.gitignore | 2 - src/librc/Makefile | 48 - src/librc/librc.h | 2 +- src/mark_service/mark_service.c | 91 ++ src/mark_service/meson.build | 20 + src/meson.build | 36 +- src/mountinfo/meson.build | 7 + src/mountinfo/mountinfo.c | 488 +++++++++ src/on_ac_power/meson.build | 3 + src/on_ac_power/on_ac_power | 47 + src/openrc-init/meson.build | 10 + src/openrc-init/openrc-init.c | 333 +++++++ src/openrc-run/meson.build | 17 + src/openrc-run/openrc-run.c | 1436 +++++++++++++++++++++++++++ src/openrc-shutdown/broadcast.c | 211 ++++ src/openrc-shutdown/broadcast.h | 16 + src/openrc-shutdown/meson.build | 10 + src/openrc-shutdown/openrc-shutdown.c | 358 +++++++ src/openrc-shutdown/rc-sysvinit.c | 102 ++ src/openrc-shutdown/rc-sysvinit.h | 72 ++ src/openrc/meson.build | 17 + src/openrc/rc-logger.c | 314 ++++++ src/openrc/rc-logger.h | 23 + src/openrc/rc.c | 1118 +++++++++++++++++++++ src/poweroff/meson.build | 7 + src/poweroff/poweroff.in | 28 + src/rc-abort/meson.build | 6 + src/rc-abort/rc-abort.c | 43 + src/rc-depend/meson.build | 7 + src/rc-depend/rc-depend.c | 177 ++++ src/rc-service/meson.build | 7 + src/rc-service/rc-service.c | 171 ++++ src/rc-sstat/meson.build | 5 + src/rc-sstat/rc-sstat | 149 +++ src/rc-status/meson.build | 8 + src/rc-status/rc-status.c | 465 +++++++++ src/rc-update/meson.build | 7 + src/rc-update/rc-update.c | 353 +++++++ src/rc/.gitignore | 66 -- src/rc/Makefile | 186 ---- src/rc/_usage.c | 95 -- src/rc/_usage.h | 56 -- src/rc/broadcast.c | 211 ---- src/rc/broadcast.h | 16 - src/rc/checkpath.c | 452 --------- src/rc/do_e.c | 228 ----- src/rc/do_mark_service.c | 91 -- src/rc/do_service.c | 76 -- src/rc/do_value.c | 65 -- src/rc/fstabinfo.c | 339 ------- src/rc/is_newer_than.c | 34 - src/rc/is_older_than.c | 35 - src/rc/kill_all.c | 260 ----- src/rc/meson.build | 320 ------ src/rc/mountinfo.c | 488 --------- src/rc/openrc-init.c | 333 ------- src/rc/openrc-run.c | 1436 --------------------------- src/rc/openrc-shutdown.c | 358 ------- src/rc/rc-abort.c | 43 - src/rc/rc-depend.c | 177 ---- src/rc/rc-logger.c | 314 ------ src/rc/rc-logger.h | 23 - src/rc/rc-misc.c | 492 --------- src/rc/rc-pipes.c | 56 -- src/rc/rc-pipes.h | 18 - src/rc/rc-plugin.c | 245 ----- src/rc/rc-plugin.h | 41 - src/rc/rc-schedules.c | 433 -------- src/rc/rc-schedules.h | 26 - src/rc/rc-selinux.c | 417 -------- src/rc/rc-selinux.h | 36 - src/rc/rc-service.c | 171 ---- src/rc/rc-status.c | 465 --------- src/rc/rc-sysvinit.c | 102 -- src/rc/rc-sysvinit.h | 72 -- src/rc/rc-update.c | 353 ------- src/rc/rc-wtmp.c | 51 - src/rc/rc.c | 1118 --------------------- src/rc/seedrng.c | 522 ---------- src/rc/shell_var.c | 41 - src/rc/start-stop-daemon.c | 1205 ---------------------- src/rc/start-stop-daemon.pam | 6 - src/rc/supervise-daemon.c | 1250 ----------------------- src/rc/supervise-daemon.pam | 6 - src/rc/swclock.c | 106 -- src/reboot/meson.build | 7 + src/reboot/reboot.in | 30 + src/seedrng/meson.build | 9 + src/seedrng/seedrng.c | 522 ++++++++++ src/service/meson.build | 20 + src/service/service.c | 76 ++ src/shared/_usage.c | 95 ++ src/shared/_usage.h | 56 ++ src/shared/helpers.h | 174 ++++ src/shared/meson.build | 35 + src/shared/misc.c | 492 +++++++++ src/shared/misc.h | 74 ++ src/shared/plugin.c | 245 +++++ src/shared/plugin.h | 41 + src/shared/queue.h | 846 ++++++++++++++++ src/shared/schedules.c | 433 ++++++++ src/shared/schedules.h | 26 + src/shared/selinux.c | 417 ++++++++ src/shared/selinux.h | 36 + src/shared/version.h.in | 18 + src/shared/version.in | 1 + src/shared/wtmp.c | 51 + src/shared/wtmp.h | 26 + src/shell_var/meson.build | 4 + src/shell_var/shell_var.c | 41 + src/shutdown/meson.build | 7 + src/shutdown/shutdown.in | 46 + src/start-stop-daemon/meson.build | 15 + src/start-stop-daemon/rc-pipes.c | 56 ++ src/start-stop-daemon/rc-pipes.h | 18 + src/start-stop-daemon/start-stop-daemon.c | 1205 ++++++++++++++++++++++ src/start-stop-daemon/start-stop-daemon.pam | 6 + src/supervise-daemon/meson.build | 14 + src/supervise-daemon/supervise-daemon.c | 1250 +++++++++++++++++++++++ src/supervise-daemon/supervise-daemon.pam | 6 + src/swclock/meson.build | 7 + src/swclock/swclock.c | 106 ++ src/value/meson.build | 15 + src/value/value.c | 65 ++ support/Makefile | 20 - support/deptree2dot/Makefile | 9 - support/init.d.examples/.gitignore | 11 - support/init.d.examples/Makefile | 11 - support/openvpn/Makefile | 9 - support/sysvinit/Makefile | 8 - sysctl.d/Makefile | 6 - test/Makefile | 14 - tools/meson_final.sh | 2 +- zsh-completion/Makefile | 10 - 210 files changed, 14213 insertions(+), 15810 deletions(-) delete mode 100644 Makefile delete mode 100644 Makefile.inc delete mode 100644 bash-completion/Makefile delete mode 100644 conf.d/Makefile delete mode 100644 etc/.gitignore delete mode 100644 etc/Makefile delete mode 100644 init.d/.gitignore delete mode 100644 init.d/Makefile delete mode 100644 local.d/Makefile delete mode 100644 man/Makefile delete mode 100644 mk/cc.mk delete mode 100644 mk/debug.mk delete mode 100644 mk/depend.mk delete mode 100644 mk/dist.mk delete mode 100644 mk/gitignore.mk delete mode 100644 mk/gitver.mk delete mode 100644 mk/lib.mk delete mode 100644 mk/net.mk delete mode 100644 mk/os-BSD.mk delete mode 100644 mk/os-DragonFly.mk delete mode 100644 mk/os-FreeBSD.mk delete mode 100644 mk/os-GNU-kFreeBSD.mk delete mode 100644 mk/os-GNU.mk delete mode 100644 mk/os-Linux.mk delete mode 100644 mk/os-NetBSD.mk delete mode 100644 mk/os-prefix.mk delete mode 100644 mk/os.mk delete mode 100644 mk/pam.mk delete mode 100644 mk/prog.mk delete mode 100644 mk/scripts.mk delete mode 100644 mk/subdir.mk delete mode 100644 mk/sys.mk delete mode 100644 mk/termcap.mk delete mode 100644 pkgconfig/.gitignore delete mode 100644 pkgconfig/Makefile delete mode 100644 runlevels/Makefile delete mode 100644 scripts/.gitignore delete mode 100644 scripts/Makefile delete mode 100644 scripts/halt.in delete mode 100644 scripts/meson.build delete mode 100755 scripts/meson_script_links.sh delete mode 100755 scripts/on_ac_power delete mode 100644 scripts/poweroff.in delete mode 100644 scripts/rc-sstat.in delete mode 100644 scripts/reboot.in delete mode 100644 scripts/shutdown.in delete mode 100644 sh/.gitignore delete mode 100644 sh/Makefile delete mode 100644 src/Makefile create mode 100644 src/checkpath/checkpath.c create mode 100644 src/checkpath/meson.build delete mode 100644 src/common/meson.build delete mode 100644 src/common/version.h.in delete mode 100644 src/common/version.in create mode 100644 src/einfo/einfo.c create mode 100644 src/einfo/meson.build create mode 100644 src/fstabinfo/fstabinfo.c create mode 100644 src/fstabinfo/meson.build create mode 100644 src/halt/halt.in create mode 100644 src/halt/meson.build delete mode 100644 src/includes/helpers.h delete mode 100644 src/includes/queue.h delete mode 100644 src/includes/rc-misc.h delete mode 100644 src/includes/rc-wtmp.h create mode 100644 src/is_newer_than/is_newer_than.c create mode 100644 src/is_newer_than/meson.build create mode 100644 src/is_older_than/is_older_than.c create mode 100644 src/is_older_than/meson.build create mode 100644 src/kill_all/kill_all.c create mode 100644 src/kill_all/meson.build delete mode 100644 src/libeinfo/.gitignore delete mode 100644 src/libeinfo/Makefile delete mode 100644 src/librc/.gitignore delete mode 100644 src/librc/Makefile create mode 100644 src/mark_service/mark_service.c create mode 100644 src/mark_service/meson.build create mode 100644 src/mountinfo/meson.build create mode 100644 src/mountinfo/mountinfo.c create mode 100644 src/on_ac_power/meson.build create mode 100644 src/on_ac_power/on_ac_power create mode 100644 src/openrc-init/meson.build create mode 100644 src/openrc-init/openrc-init.c create mode 100644 src/openrc-run/meson.build create mode 100644 src/openrc-run/openrc-run.c create mode 100644 src/openrc-shutdown/broadcast.c create mode 100644 src/openrc-shutdown/broadcast.h create mode 100644 src/openrc-shutdown/meson.build create mode 100644 src/openrc-shutdown/openrc-shutdown.c create mode 100644 src/openrc-shutdown/rc-sysvinit.c create mode 100644 src/openrc-shutdown/rc-sysvinit.h create mode 100644 src/openrc/meson.build create mode 100644 src/openrc/rc-logger.c create mode 100644 src/openrc/rc-logger.h create mode 100644 src/openrc/rc.c create mode 100644 src/poweroff/meson.build create mode 100644 src/poweroff/poweroff.in create mode 100644 src/rc-abort/meson.build create mode 100644 src/rc-abort/rc-abort.c create mode 100644 src/rc-depend/meson.build create mode 100644 src/rc-depend/rc-depend.c create mode 100644 src/rc-service/meson.build create mode 100644 src/rc-service/rc-service.c create mode 100644 src/rc-sstat/meson.build create mode 100644 src/rc-sstat/rc-sstat create mode 100644 src/rc-status/meson.build create mode 100644 src/rc-status/rc-status.c create mode 100644 src/rc-update/meson.build create mode 100644 src/rc-update/rc-update.c delete mode 100644 src/rc/.gitignore delete mode 100644 src/rc/Makefile delete mode 100644 src/rc/_usage.c delete mode 100644 src/rc/_usage.h delete mode 100644 src/rc/broadcast.c delete mode 100644 src/rc/broadcast.h delete mode 100644 src/rc/checkpath.c delete mode 100644 src/rc/do_e.c delete mode 100644 src/rc/do_mark_service.c delete mode 100644 src/rc/do_service.c delete mode 100644 src/rc/do_value.c delete mode 100644 src/rc/fstabinfo.c delete mode 100644 src/rc/is_newer_than.c delete mode 100644 src/rc/is_older_than.c delete mode 100644 src/rc/kill_all.c delete mode 100644 src/rc/meson.build delete mode 100644 src/rc/mountinfo.c delete mode 100644 src/rc/openrc-init.c delete mode 100644 src/rc/openrc-run.c delete mode 100644 src/rc/openrc-shutdown.c delete mode 100644 src/rc/rc-abort.c delete mode 100644 src/rc/rc-depend.c delete mode 100644 src/rc/rc-logger.c delete mode 100644 src/rc/rc-logger.h delete mode 100644 src/rc/rc-misc.c delete mode 100644 src/rc/rc-pipes.c delete mode 100644 src/rc/rc-pipes.h delete mode 100644 src/rc/rc-plugin.c delete mode 100644 src/rc/rc-plugin.h delete mode 100644 src/rc/rc-schedules.c delete mode 100644 src/rc/rc-schedules.h delete mode 100644 src/rc/rc-selinux.c delete mode 100644 src/rc/rc-selinux.h delete mode 100644 src/rc/rc-service.c delete mode 100644 src/rc/rc-status.c delete mode 100644 src/rc/rc-sysvinit.c delete mode 100644 src/rc/rc-sysvinit.h delete mode 100644 src/rc/rc-update.c delete mode 100644 src/rc/rc-wtmp.c delete mode 100644 src/rc/rc.c delete mode 100644 src/rc/seedrng.c delete mode 100644 src/rc/shell_var.c delete mode 100644 src/rc/start-stop-daemon.c delete mode 100644 src/rc/start-stop-daemon.pam delete mode 100644 src/rc/supervise-daemon.c delete mode 100644 src/rc/supervise-daemon.pam delete mode 100644 src/rc/swclock.c create mode 100644 src/reboot/meson.build create mode 100644 src/reboot/reboot.in create mode 100644 src/seedrng/meson.build create mode 100644 src/seedrng/seedrng.c create mode 100644 src/service/meson.build create mode 100644 src/service/service.c create mode 100644 src/shared/_usage.c create mode 100644 src/shared/_usage.h create mode 100644 src/shared/helpers.h create mode 100644 src/shared/meson.build create mode 100644 src/shared/misc.c create mode 100644 src/shared/misc.h create mode 100644 src/shared/plugin.c create mode 100644 src/shared/plugin.h create mode 100644 src/shared/queue.h create mode 100644 src/shared/schedules.c create mode 100644 src/shared/schedules.h create mode 100644 src/shared/selinux.c create mode 100644 src/shared/selinux.h create mode 100644 src/shared/version.h.in create mode 100644 src/shared/version.in create mode 100644 src/shared/wtmp.c create mode 100644 src/shared/wtmp.h create mode 100644 src/shell_var/meson.build create mode 100644 src/shell_var/shell_var.c create mode 100644 src/shutdown/meson.build create mode 100644 src/shutdown/shutdown.in create mode 100644 src/start-stop-daemon/meson.build create mode 100644 src/start-stop-daemon/rc-pipes.c create mode 100644 src/start-stop-daemon/rc-pipes.h create mode 100644 src/start-stop-daemon/start-stop-daemon.c create mode 100644 src/start-stop-daemon/start-stop-daemon.pam create mode 100644 src/supervise-daemon/meson.build create mode 100644 src/supervise-daemon/supervise-daemon.c create mode 100644 src/supervise-daemon/supervise-daemon.pam create mode 100644 src/swclock/meson.build create mode 100644 src/swclock/swclock.c create mode 100644 src/value/meson.build create mode 100644 src/value/value.c delete mode 100644 support/Makefile delete mode 100644 support/deptree2dot/Makefile delete mode 100644 support/init.d.examples/.gitignore delete mode 100644 support/init.d.examples/Makefile delete mode 100644 support/openvpn/Makefile delete mode 100644 support/sysvinit/Makefile delete mode 100644 sysctl.d/Makefile delete mode 100644 test/Makefile delete mode 100644 zsh-completion/Makefile diff --git a/.gitignore b/.gitignore index 23f644a7..d3b65db4 100644 --- a/.gitignore +++ b/.gitignore @@ -9,8 +9,4 @@ core .gdb_history .gdbinit -.depend -*.a -*.o -*.So -lib*.so +build diff --git a/Makefile b/Makefile deleted file mode 100644 index b9ff839c..00000000 --- a/Makefile +++ /dev/null @@ -1,55 +0,0 @@ -# Copyright (c) 2007-2015 The OpenRC Authors. -# See the Authors file at the top-level directory of this distribution and -# https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS -# -# This file is part of OpenRC. It is subject to the license terms in -# the LICENSE file found in the top-level directory of this -# distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE -# This file may not be copied, modified, propagated, or distributed -# except according to the terms contained in the LICENSE file. - -TOP:= ${dir ${realpath ${firstword ${MAKEFILE_LIST}}}} -MK= ${TOP}/mk - -include ${TOP}/Makefile.inc - -SUBDIR= conf.d etc init.d local.d man scripts sh src support sysctl.d - -# Build bash completion or not -MKBASHCOMP?= no -ifeq (${MKBASHCOMP},yes) -SUBDIR+= bash-completion -endif - -# Build pkgconfig or not -MKPKGCONFIG?= yes -ifeq (${MKPKGCONFIG},yes) -SUBDIR+= pkgconfig -endif - -# Build zsh completion or not -MKZSHCOMP?= no -ifeq (${MKZSHCOMP},yes) -SUBDIR+= zsh-completion -endif - -# We need to ensure that runlevels is done last other than test -SUBDIR+= runlevels -SUBDIR+= test - -INSTALLAFTER= _installafter - -include ${MK}/sys.mk -include ${MK}/os.mk -include ${MK}/subdir.mk -include ${MK}/dist.mk -include ${MK}/gitver.mk - -_installafter: -ifeq (${MKPREFIX},yes) - ${INSTALL} -d ${DESTDIR}/${LIBEXECDIR}/init.d -else ifneq (${OS},Linux) - ${INSTALL} -d ${DESTDIR}/${LIBEXECDIR}/init.d -endif - ${INSTALL} -d ${DESTDIR}/${LIBEXECDIR}/tmp - ${ECHO} "${VERSION}${GITVER}" > ${DESTDIR}/${LIBEXECDIR}/version diff --git a/Makefile.inc b/Makefile.inc deleted file mode 100644 index 931b42ac..00000000 --- a/Makefile.inc +++ /dev/null @@ -1,3 +0,0 @@ -NAME= openrc -VERSION= 0.44 -PKG= ${NAME}-${VERSION} diff --git a/NEWS.md b/NEWS.md index 8c591ca0..0e341de8 100644 --- a/NEWS.md +++ b/NEWS.md @@ -4,6 +4,10 @@ OpenRC NEWS This file will contain a list of notable changes for each release. Note the information in this file is in reverse order. +## OpenRC 0.45 + +The old make-based build system is removed in this release. + ## OpenRC 0.44 This version is the first to use a meson-based build system. diff --git a/README.md b/README.md index f9825678..8164e04e 100644 --- a/README.md +++ b/README.md @@ -9,72 +9,16 @@ system-provided init program, normally `/sbin/init`. OpenRC uses the [meson](http://mesonbuild.com) build system, so use the usual methods for this build system to build and install. -The old build system is still available for the 0.44.x branch, but it is -considered deprecated and will be removed. The previous documentation is -below. - -## Installation (historical) - -OpenRC requires GNU make. - -Once you have GNU Make installed, the default OpenRC installation can be -executed using this command: - -`make install` - -## Configuration (historical) - -You may wish to configure the installation by passing one or more of the -below arguments to the make command - -``` -PROGLDFLAGS=-static -LIBNAME=lib64 -DESTDIR=/tmp/openrc-image -MKBASHCOMP=no -MKNET=no -MKPAM=pam -MKCAP=yes -MKPREFIX=yes -MKPKGCONFIG=no -MKSELINUX=yes -MKSTATICLIBS=no -MKSYSVINIT=yes -MKTERMCAP=ncurses -MKTERMCAP=termcap -MKZSHCOMP=no -PKG_PREFIX=/usr/pkg -LOCAL_PREFIX=/usr/local -PREFIX=/usr/local -BRANDING=\"Gentoo/$(uname -s)\" -SH=/bin/sh -``` - ## Notes We don't support building a static OpenRC with PAM. -You may need to use `PROGLDFLAGS=-Wl,-Bstatic` on glibc instead of just `-static` -(This is now handled by the meson build system). - -If you are building OpenRC for a Gentoo Prefix installation, add `MKPREFIX=yes` -(this is not supported in the meson build currently, but patches are welcome). - `PKG_PREFIX` should be set to where packages install to by default. `LOCAL_PREFIX` should be set to where user maintained packages are. Only set `LOCAL_PREFIX` if different from `PKG_PREFIX`. -`PREFIX` should be set when OpenRC is not installed to /. - -If any of the following files exist then we do not overwrite them - -``` -/etc/devd.conf -/etc/rc -/etc/rc.shutdown -/etc/conf.d/* -``` +`ROOTPREFIX` should be set when the root path is different from '/'. `rc` and `rc.shutdown` are the hooks from the BSD init into OpenRC. diff --git a/bash-completion/Makefile b/bash-completion/Makefile deleted file mode 100644 index 3d9d886a..00000000 --- a/bash-completion/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -DIR= ${BASHCOMPDIR} -CONF= openrc \ - openrc-service-script \ - rc-service \ - rc-status \ - rc-update \ - -MK= ../mk -include ${MK}/os.mk - -include ${MK}/scripts.mk diff --git a/conf.d/Makefile b/conf.d/Makefile deleted file mode 100644 index 6d2e7de1..00000000 --- a/conf.d/Makefile +++ /dev/null @@ -1,20 +0,0 @@ -include ../mk/net.mk - -DIR= ${CONFDIR} -CONF= bootmisc fsck hostname localmount netmount swap ${CONF-${OS}} - -ifeq (${MKNET},yes) -CONF+= network staticroute -endif - -MK= ../mk -include ${MK}/os.mk - -CONF-FreeBSD= ipfw modules moused powerd rarpd savecore syscons urandom - -CONF-Linux= agetty consolefont devfs dmesg hwclock keymaps killprocs modules \ - mtab net-online - -CONF-NetBSD= moused rarpd savecore urandom - -include ${MK}/scripts.mk diff --git a/etc/.gitignore b/etc/.gitignore deleted file mode 100644 index fe38bc1f..00000000 --- a/etc/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -rc -rc.shutdown diff --git a/etc/Makefile b/etc/Makefile deleted file mode 100644 index 1695dee5..00000000 --- a/etc/Makefile +++ /dev/null @@ -1,23 +0,0 @@ -DIR= ${SYSCONFDIR} -SRCS= rc.in rc.shutdown.in -BIN= ${BIN-${OS}} -CONF= rc.conf ${BIN-${OS}} - -MK= ../mk -include ${MK}/os.mk - -SED_EXTRA-FreeBSD= -e 's:@TERM@:cons25:g' -BIN-FreeBSD= rc rc.shutdown rc.devd -CONF-FreeBSD= devd.conf - -SED_EXTRA-Linux= -e 's:@TERM@:wsvt25:g' -BIN-Linux= -CONF-Linux= - -SED_EXTRA-NetBSD= -e 's:@TERM@:wsvt25:g' -BIN-NetBSD= rc rc.shutdown -CONF-NetBSD= - -SED_EXTRA= ${SED_EXTRA-${OS}} - -include ${MK}/scripts.mk diff --git a/init.d/.gitignore b/init.d/.gitignore deleted file mode 100644 index 8e03e3b9..00000000 --- a/init.d/.gitignore +++ /dev/null @@ -1,51 +0,0 @@ -agetty -binfmt -cgroups -modules-load -bootmisc -fsck -hostname -local -localmount -loopback -moused -netmount -network -root -savecache -swap -sysctl -urandom -devfs -dmesg -hwclock -consolefont -keymaps -killprocs -modules -mount-ro -mtab -net-online -numlock -osclock -procfs -s6-svscan -staticroute -sysfs -devdb -hostid -newsyslog -pf -rarpd -rc-enabled -rpcbind -runsvdir -savecore -save-keymaps -save-termencoding -swap-blk -swclock -syslogd -termencoding -ttys -wscons diff --git a/init.d/Makefile b/init.d/Makefile deleted file mode 100644 index ee227f43..00000000 --- a/init.d/Makefile +++ /dev/null @@ -1,37 +0,0 @@ -include ../mk/net.mk - -DIR= ${INITDIR} -SRCS= bootmisc.in fsck.in hostname.in local.in localmount.in loopback.in \ - netmount.in osclock.in root.in savecache.in swap.in swclock.in \ - sysctl.in runsvdir.in s6-svscan.in ${SRCS-${OS}} -BIN= ${OBJS} - -# Are we installing our network scripts? -ifeq (${MKNET},yes) -SRCS+= network.in staticroute.in -endif - -MK= ../mk -include ${MK}/os.mk - -# Generic BSD scripts -SRCS-FreeBSD= hostid.in modules.in moused.in newsyslog.in pf.in rarpd.in \ - rc-enabled.in rpcbind.in savecore.in syslogd.in urandom.in -# These are FreeBSD specific -SRCS-FreeBSD+= adjkerntz.in devd.in dumpon.in encswap.in ipfw.in \ - mixer.in nscd.in powerd.in syscons.in - -SRCS-Linux= agetty.in binfmt.in devfs.in cgroups.in dmesg.in hwclock.in \ - consolefont.in keymaps.in killprocs.in modules.in \ - mount-ro.in mtab.in numlock.in procfs.in net-online.in save-keymaps.in \ - save-termencoding.in seedrng.in sysfs.in termencoding.in - -# Generic BSD scripts -SRCS-NetBSD= hostid.in moused.in newsyslog.in pf.in rarpd.in rc-enabled.in \ - rpcbind.in savecore.in syslogd.in urandom.in -# These are NetBSD specific -SRCS-NetBSD+= devdb.in swap-blk.in ttys.in wscons.in - -include ${MK}/scripts.mk - -_installafter_: realinstall diff --git a/local.d/Makefile b/local.d/Makefile deleted file mode 100644 index 7a7d31dd..00000000 --- a/local.d/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -DIR= ${LOCALDIR} -CONF= README - -MK= ../mk -include ${MK}/os.mk -include ${MK}/scripts.mk diff --git a/man/Makefile b/man/Makefile deleted file mode 100644 index 006c8d16..00000000 --- a/man/Makefile +++ /dev/null @@ -1,45 +0,0 @@ -MK= ../mk -include ${MK}/sys.mk -include ${MK}/os.mk - -MAN3= einfo.3 \ - rc_config.3 rc_deptree.3 rc_find_pids.3 rc_plugin_hook.3 \ - rc_runlevel.3 rc_service.3 rc_stringlist.3 -MAN8= rc-service.8 rc-status.8 rc-update.8 openrc.8 openrc-run.8 \ - start-stop-daemon.8 supervise-daemon.8 - -ifeq (${OS},Linux) -MAN8 += rc-sstat.8 openrc-init.8 openrc-shutdown.8 -endif - -# Handy macro to create symlinks -# This does rely on correctly formatting our manpages! -MAKE_LINKS= suffix=$${man\#*.}; \ - prefix=$${man%%.*}; \ - for link in `sed -e 's/ ,//g' \ - -n -e '/^\.Sh NAME$$/,/\.Sh/ s/\.Nm //p' $${man}`; do \ - if test "$${link}" != "$${prefix}" ; then \ - ln -sf $${man} \ - ${DESTDIR}/${MANDIR}/man$${suffix}/$${link}.$${suffix} ; \ - fi; \ - done; - -include ${MK}/gitignore.mk - -all: - -install: - ${INSTALL} -d ${DESTDIR}/${MANDIR}/man3 - for man in ${MAN3}; do \ - ${INSTALL} -m ${MANMODE} "$$man" ${DESTDIR}/${MANDIR}/man3 || exit $$?; \ - ${MAKE_LINKS} \ - done - ${INSTALL} -d ${DESTDIR}/${MANDIR}/man8 - for man in ${MAN8}; do \ - ${INSTALL} -m ${MANMODE} "$$man" ${DESTDIR}/${MANDIR}/man8 || exit $$?; \ - ${MAKE_LINKS} \ - done - -check test:: - -clean: diff --git a/meson.build b/meson.build index e104b7d7..d37910c3 100644 --- a/meson.build +++ b/meson.build @@ -182,7 +182,7 @@ cc_warning_flags = cc.get_supported_arguments(cc_warning_flags_test) cc_flags = [cc_debug_flags, cc_os_flags, cc_warning_flags] add_project_arguments(cc_flags, language : 'c') -incdir = include_directories('src/includes') +incdir = include_directories('src/shared') einfo_incdir = include_directories('src/libeinfo') rc_incdir = include_directories('src/librc') @@ -201,7 +201,6 @@ subdir('init.d') subdir('local.d') subdir('man') subdir('pkgconfig') -subdir('scripts') subdir('sh') subdir('src') subdir('support') diff --git a/mk/cc.mk b/mk/cc.mk deleted file mode 100644 index 5efd5ad8..00000000 --- a/mk/cc.mk +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright (c) 2008-2015 The OpenRC Authors. -# See the Authors file at the top-level directory of this distribution and -# https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS -# -# This file is part of OpenRC. It is subject to the license terms in -# the LICENSE file found in the top-level directory of this -# distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE -# This file may not be copied, modified, propagated, or distributed -# except according to the terms contained in the LICENSE file. - -# Setup some good default CFLAGS -CFLAGS?= -O2 -g - -# Default to using the C99 standard -CSTD?= c99 -ifneq (${CSTD},) -CFLAGS+= -std=${CSTD} -endif - -# Try and use some good cc flags if we're building from git -# We don't use -pedantic as it will warn about our perfectly valid -# use of %m in our logger. -_CCFLAGS= -Wall -Wextra -Wimplicit -Wshadow -Wformat=2 \ - -Wmissing-prototypes -Wmissing-declarations \ - -Wmissing-noreturn -Wmissing-format-attribute \ - -Wnested-externs \ - -Winline -Wwrite-strings -Wcast-align -Wcast-qual \ - -Wpointer-arith \ - -Wdeclaration-after-statement -Wsequence-point \ - -Werror=implicit-function-declaration - -# We should be using -Wredundant-decls, but our library hidden proto stuff -# gives loads of warnings. I don't fully understand it (the hidden proto, -# not the warning) so we just silence the warning. - -_CC_FLAGS_SH= for f in ${_CCFLAGS}; do \ - if echo "int main(void) { return 0;} " | \ - ${CC} $$f -S -xc -o /dev/null - ; \ - then printf "%s" "$$f "; fi \ - done; -_CC_FLAGS:= $(shell ${_CC_FLAGS_SH}) -CFLAGS+= ${_CC_FLAGS} - -include ${MK}/debug.mk diff --git a/mk/debug.mk b/mk/debug.mk deleted file mode 100644 index c7cbe9e8..00000000 --- a/mk/debug.mk +++ /dev/null @@ -1,23 +0,0 @@ -# rules to enable debugging support -# Copyright (c) 2008-2015 The OpenRC Authors. -# See the Authors file at the top-level directory of this distribution and -# https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS -# -# This file is part of OpenRC. It is subject to the license terms in -# the LICENSE file found in the top-level directory of this -# distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE -# This file may not be copied, modified, propagated, or distributed -# except according to the terms contained in the LICENSE file. - -_RC_DEBUG_SH= case "${DEBUG}" in "") echo "";; *) echo "-DRC_DEBUG";; esac -_RC_DEBUG:= $(shell ${_RC_DEBUG_SH}) -CPPFLAGS+= ${_RC_DEBUG} - -# Should we enable this with a different flag? -_LD_DEBUG_SH= case "${DEBUG}" in "") echo "";; *) echo "-Wl,--rpath=../librc -Wl,--rpath=../libeinfo";; esac -_LD_DEBUG:= $(shell ${_LD_DEBUG_SH}) -LDFLAGS+= ${_LD_DEBUG} - -_GGDB_SH= case "${DEBUG}" in "") echo "";; *) echo "-ggdb";; esac -_GGDB:= $(shell ${_GGDB_SH}) -CFLAGS+= ${_GGDB} diff --git a/mk/depend.mk b/mk/depend.mk deleted file mode 100644 index 187592dd..00000000 --- a/mk/depend.mk +++ /dev/null @@ -1,21 +0,0 @@ -# Generate .depend -# Copyright (c) 2008-2015 The OpenRC Authors. -# See the Authors file at the top-level directory of this distribution and -# https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS -# -# This file is part of OpenRC. It is subject to the license terms in -# the LICENSE file found in the top-level directory of this -# distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE -# This file may not be copied, modified, propagated, or distributed -# except according to the terms contained in the LICENSE file. - -CLEANFILES+= .depend -IGNOREFILES+= .depend - -.depend: ${SRCS} - rm -f .depend - ${CC} ${LOCAL_CPPFLAGS} ${CPPFLAGS} -MM ${SRCS} > .depend - -depend: .depend extra_depend - --include .depend diff --git a/mk/dist.mk b/mk/dist.mk deleted file mode 100644 index c1e94a59..00000000 --- a/mk/dist.mk +++ /dev/null @@ -1,50 +0,0 @@ -# rules to make a distribution tarball from a git repo -# Copyright (c) 2008-2015 The OpenRC Authors. -# See the Authors file at the top-level directory of this distribution and -# https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS -# -# This file is part of OpenRC. It is subject to the license terms in -# the LICENSE file found in the top-level directory of this -# distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE -# This file may not be copied, modified, propagated, or distributed -# except according to the terms contained in the LICENSE file. - -GITREF?= ${VERSION} -DISTPREFIX?= ${NAME}-${VERSION} -DISTFILE?= ${DISTPREFIX}.tar.gz - -CLEANFILES+= ${NAME}-*.tar.gz - -CHANGELOG_LIMIT?= --after="1 year ago" - -_SNAP_SH= date -u +%Y%m%d%H%M -_SNAP:= $(shell ${_SNAP_SH}) -SNAP= ${_SNAP} -SNAPDIR= ${DISTPREFIX}-${SNAP} -SNAPFILE= ${SNAPDIR}.tar.gz - -changelog: - git log ${CHANGELOG_LIMIT} --format=full > ChangeLog - -dist: - git archive --prefix=${DISTPREFIX}/ ${GITREF} --output=${DISTFILE} - -distcheck: dist - rm -rf ${DISTPREFIX} - tar xf ${DISTFILE} - MAKEFLAGS= $(MAKE) -C ${DISTPREFIX} - MAKEFLAGS= $(MAKE) -C ${DISTPREFIX} check - rm -rf ${DISTPREFIX} - -snapshot: - rm -rf /tmp/${SNAPDIR} - mkdir /tmp/${SNAPDIR} - cp -RPp * /tmp/${SNAPDIR} - (cd /tmp/${SNAPDIR}; make clean) - rm -rf /tmp/${SNAPDIR}/.git 2>/dev/null || true - tar -cvzpf ${SNAPFILE} -C /tmp ${SNAPDIR} - rm -rf /tmp/${SNAPDIR} - ls -l ${SNAPFILE} - -snap: snapshot - diff --git a/mk/gitignore.mk b/mk/gitignore.mk deleted file mode 100644 index a0b24cb7..00000000 --- a/mk/gitignore.mk +++ /dev/null @@ -1,22 +0,0 @@ -# rules to make .gitignore files -# Copyright (c) 2008-2015 The OpenRC Authors. -# See the Authors file at the top-level directory of this distribution and -# https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS -# -# This file is part of OpenRC. It is subject to the license terms in -# the LICENSE file found in the top-level directory of this -# distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE -# This file may not be copied, modified, propagated, or distributed -# except according to the terms contained in the LICENSE file. - -IGNOREFILES+= ${CLEANFILES} - -.PHONY: .gitignore - -.gitignore: - @if test -n "${IGNOREFILES}"; then \ - echo "Ignoring ${IGNOREFILES}"; \ - echo ${IGNOREFILES} | tr ' ' '\n' > .gitignore; \ - fi - -ignore: .gitignore diff --git a/mk/gitver.mk b/mk/gitver.mk deleted file mode 100644 index 62cae5a4..00000000 --- a/mk/gitver.mk +++ /dev/null @@ -1,8 +0,0 @@ -_GITVER_SH= if git rev-parse --short HEAD >/dev/null 2>&1; then \ - printf "."; \ - git rev-parse --short HEAD; \ - else \ - echo ""; \ - fi -_GITVER:= $(shell ${_GITVER_SH}) -GITVER= ${_GITVER} diff --git a/mk/lib.mk b/mk/lib.mk deleted file mode 100644 index eb6fcf4b..00000000 --- a/mk/lib.mk +++ /dev/null @@ -1,77 +0,0 @@ -# rules to build a library -# based on FreeBSD's bsd.lib.mk - -# Copyright (c) 2008-2015 The OpenRC Authors. -# See the Authors file at the top-level directory of this distribution and -# https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS -# -# This file is part of OpenRC. It is subject to the license terms in -# the LICENSE file found in the top-level directory of this -# distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE -# This file may not be copied, modified, propagated, or distributed -# except according to the terms contained in the LICENSE file. - -SHLIB_NAME= lib${LIB}.so.${SHLIB_MAJOR} -SHLIB_LINK= lib${LIB}.so -SONAME?= ${SHLIB_NAME} - -SOBJS+= ${SRCS:.c=.So} - -MKSTATICLIBS?= yes -ifeq (${MKSTATICLIBS},yes) -OBJS+= ${SRCS:.c=.o} -_LIBS+= lib${LIB}.a -endif - -_LIBS+= ${SHLIB_NAME} - -CLEANFILES+= ${OBJS} ${SOBJS} ${_LIBS} ${SHLIB_LINK} - -%.o: %.c - ${CC} ${LOCAL_CFLAGS} ${LOCAL_CPPFLAGS} ${CFLAGS} ${CPPFLAGS} -c $< -o $@ - -%.So: %.c - ${CC} ${PICFLAG} -DPIC ${LOCAL_CFLAGS} ${LOCAL_CPPFLAGS} ${CPPFLAGS} ${CFLAGS} -c $< -o $@ - -all: depend ${_LIBS} - -lib${LIB}.a: ${OBJS} ${STATICOBJS} - @${ECHO} building static library $@ - ${AR} rc $@ $^ - ${RANLIB} $@ - -${SHLIB_NAME}: ${VERSION_MAP} -LDFLAGS+= -Wl,--version-script=${VERSION_MAP} - -${SHLIB_NAME}: ${SOBJS} - @${ECHO} building shared library $@ - @rm -f $@ ${SHLIB_LINK} - @ln -fs $@ ${SHLIB_LINK} - ${CC} ${LOCAL_CFLAGS} ${CFLAGS} ${LOCAL_LDFLAGS} ${LDFLAGS} -shared -Wl,-x \ - -o $@ -Wl,-soname,${SONAME} \ - ${SOBJS} ${LDADD} - -install: all - ${INSTALL} -d ${DESTDIR}${LIBDIR} -ifeq (${MKSTATICLIBS},yes) - ${INSTALL} -m ${LIBMODE} lib${LIB}.a ${DESTDIR}${LIBDIR} -endif - ${INSTALL} -m ${LIBMODE} ${SHLIB_NAME} ${DESTDIR}${LIBDIR} - ln -fs ${SHLIB_NAME} ${DESTDIR}${LIBDIR}/${SHLIB_LINK} - ${INSTALL} -d ${DESTDIR}${INCDIR} - for x in ${INCS}; do ${INSTALL} -m ${INCMODE} $$x ${DESTDIR}${INCDIR}; done - -check test:: - -clean: - rm -f ${OBJS} ${SOBJS} ${_LIBS} ${SHLIB_LINK} ${CLEANFILES} - -extra_depend: - @TMP=depend.$$$$; \ - ${SED} -e 's/^\([^\.]*\).o[ ]*:/\1.o \1.So:/' .depend > $${TMP}; \ - mv $${TMP} .depend - -include ${MK}/sys.mk -include ${MK}/os.mk -include ${MK}/depend.mk -include ${MK}/gitignore.mk diff --git a/mk/net.mk b/mk/net.mk deleted file mode 100644 index abc198c6..00000000 --- a/mk/net.mk +++ /dev/null @@ -1 +0,0 @@ -MKNET?= yes diff --git a/mk/os-BSD.mk b/mk/os-BSD.mk deleted file mode 100644 index 43db4d59..00000000 --- a/mk/os-BSD.mk +++ /dev/null @@ -1,16 +0,0 @@ -# Copyright (c) 2008-2015 The OpenRC Authors. -# See the Authors file at the top-level directory of this distribution and -# https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS -# -# This file is part of OpenRC. It is subject to the license terms in -# the LICENSE file found in the top-level directory of this -# distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE -# This file may not be copied, modified, propagated, or distributed -# except according to the terms contained in the LICENSE file. - -# Generic definitions - -PKG_PREFIX?= /usr/local -SFX= .BSD.in - -LIBKVM?= -lkvm diff --git a/mk/os-DragonFly.mk b/mk/os-DragonFly.mk deleted file mode 100644 index 6bd9c29c..00000000 --- a/mk/os-DragonFly.mk +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2013-2015 The OpenRC Authors. -# See the Authors file at the top-level directory of this distribution and -# https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS -# -# This file is part of OpenRC. It is subject to the license terms in -# the LICENSE file found in the top-level directory of this -# distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE -# This file may not be copied, modified, propagated, or distributed -# except according to the terms contained in the LICENSE file. - -# Generic definitions - -include ${MK}/os-BSD.mk diff --git a/mk/os-FreeBSD.mk b/mk/os-FreeBSD.mk deleted file mode 100644 index 1025921f..00000000 --- a/mk/os-FreeBSD.mk +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright (c) 2008-2015 The OpenRC Authors. -# See the Authors file at the top-level directory of this distribution and -# https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS -# -# This file is part of OpenRC. It is subject to the license terms in -# the LICENSE file found in the top-level directory of this -# distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE -# This file may not be copied, modified, propagated, or distributed -# except according to the terms contained in the LICENSE file. - -# Generic definitions - -include ${MK}/os-BSD.mk - -CPPFLAGS+= -D_BSD_SOURCE diff --git a/mk/os-GNU-kFreeBSD.mk b/mk/os-GNU-kFreeBSD.mk deleted file mode 100644 index 8d25a19d..00000000 --- a/mk/os-GNU-kFreeBSD.mk +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright (c) 2013-2015 The OpenRC Authors. -# See the Authors file at the top-level directory of this distribution and -# https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS -# -# This file is part of OpenRC. It is subject to the license terms in -# the LICENSE file found in the top-level directory of this -# distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE -# This file may not be copied, modified, propagated, or distributed -# except according to the terms contained in the LICENSE file. - -# Generic definitions - -SFX= .GNU-kFreeBSD.in -PKG_PREFIX?= /usr - -CPPFLAGS+= -D_BSD_SOURCE -LIBDL= -Wl,-Bdynamic -ldl -LIBKVM?= diff --git a/mk/os-GNU.mk b/mk/os-GNU.mk deleted file mode 100644 index ea22b2fc..00000000 --- a/mk/os-GNU.mk +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright (c) 2007-2015 The OpenRC Authors. -# See the Authors file at the top-level directory of this distribution and -# https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS -# -# This file is part of OpenRC. It is subject to the license terms in -# the LICENSE file found in the top-level directory of this -# distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE -# This file may not be copied, modified, propagated, or distributed -# except according to the terms contained in the LICENSE file. - -SFX= .GNU.in -PKG_PREFIX?= /usr - -CPPFLAGS+= -D_DEFAULT_SOURCE -DMAXPATHLEN=4096 -DPATH_MAX=4096 -LIBDL= -Wl,-Bdynamic -ldl diff --git a/mk/os-Linux.mk b/mk/os-Linux.mk deleted file mode 100644 index 00521c80..00000000 --- a/mk/os-Linux.mk +++ /dev/null @@ -1,40 +0,0 @@ -# Copyright (c) 2008-2015 The OpenRC Authors. -# See the Authors file at the top-level directory of this distribution and -# https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS -# -# This file is part of OpenRC. It is subject to the license terms in -# the LICENSE file found in the top-level directory of this -# distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE -# This file may not be copied, modified, propagated, or distributed -# except according to the terms contained in the LICENSE file. - -SFX= .Linux.in -PKG_PREFIX?= /usr - -CPPFLAGS+= -D_DEFAULT_SOURCE -D_POSIX_C_SOURCE=200809L -D_BSD_SOURCE -D_XOPEN_SOURCE -LIBDL= -Wl,-Bdynamic -ldl - -ifeq (${MKSELINUX},yes) -CPPFLAGS+= -DHAVE_SELINUX -LIBSELINUX?= -lselinux -LDADD += $(LIBSELINUX) - -ifneq (${MKPAM},pam) -# if using selinux but not pam then we need crypt -LIBCRYPT?= -lcrypt -LDADD += $(LIBCRYPT) -endif - -endif - -ifeq (${MKCAP},yes) -CPPFLAGS+= -DHAVE_CAP -LIBCAP?= -lcap -LDADD += $(LIBCAP) -endif - -ifeq (${MKAUDIT},yes) -LIBAUDIT?= -laudit -CPPFLAGS+= -DHAVE_AUDIT -LDADD+= ${LIBAUDIT} -endif diff --git a/mk/os-NetBSD.mk b/mk/os-NetBSD.mk deleted file mode 100644 index 240949aa..00000000 --- a/mk/os-NetBSD.mk +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright (c) 2008-2015 The OpenRC Authors. -# See the Authors file at the top-level directory of this distribution and -# https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS -# -# This file is part of OpenRC. It is subject to the license terms in -# the LICENSE file found in the top-level directory of this -# distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE -# This file may not be copied, modified, propagated, or distributed -# except according to the terms contained in the LICENSE file. - -# Generic definitions - -PKG_PREFIX?= /usr/pkg -include ${MK}/os-BSD.mk diff --git a/mk/os-prefix.mk b/mk/os-prefix.mk deleted file mode 100644 index af08c99e..00000000 --- a/mk/os-prefix.mk +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright (c) 2012 William Hubbs -# Released under the 2-clause BSD license. - -ifeq (${MKPREFIX},yes) -CPPFLAGS+= -DPREFIX -PKG_PREFIX?= $(PREFIX)/usr -SED_EXTRA= -e '/_PATH=.*usr.bin/d' -endif diff --git a/mk/os.mk b/mk/os.mk deleted file mode 100644 index 1ce94152..00000000 --- a/mk/os.mk +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright (c) 2008-2015 The OpenRC Authors. -# See the Authors file at the top-level directory of this distribution and -# https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS -# -# This file is part of OpenRC. It is subject to the license terms in -# the LICENSE file found in the top-level directory of this -# distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE -# This file may not be copied, modified, propagated, or distributed -# except according to the terms contained in the LICENSE file. - -# Generic definitions - -_OS_SH= uname -s | tr '/' '-' -_OS:= $(shell ${_OS_SH}) -OS?= ${_OS} -include ${MK}/os-prefix.mk -include ${MK}/os-${OS}.mk - -RC_LIB= /$(LIBNAME)/rc diff --git a/mk/pam.mk b/mk/pam.mk deleted file mode 100644 index 199896cc..00000000 --- a/mk/pam.mk +++ /dev/null @@ -1,16 +0,0 @@ -ifeq (${MKPAM},pam) -LIBPAM?= -lpam -CPPFLAGS+= -DHAVE_PAM -LDADD+= ${LIBPAM} - -ifeq (${MKSELINUX},yes) -# with selinux, pam_misc is needed too -LIBPAM_MISC?= -lpam_misc -LDADD+= ${LIBPAM_MISC} -endif - -PAMDIR?= /etc/pam.d -PAMMODE?= 0644 -else ifneq (${MKPAM},) -$(error if MKPAM is defined, it must be "pam") -endif diff --git a/mk/prog.mk b/mk/prog.mk deleted file mode 100644 index 463a9508..00000000 --- a/mk/prog.mk +++ /dev/null @@ -1,48 +0,0 @@ -# rules to build a program -# based on FreeBSD's bsd.prog.mk - -# Copyright (c) 2008-2015 The OpenRC Authors. -# See the Authors file at the top-level directory of this distribution and -# https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS -# -# This file is part of OpenRC. It is subject to the license terms in -# the LICENSE file found in the top-level directory of this -# distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE -# This file may not be copied, modified, propagated, or distributed -# except according to the terms contained in the LICENSE file. - -OBJS+= ${SRCS:.c=.o} - -# Some systems don't include /lib in their standard link path -# so we should embed it if different -# This is currently hardcoded for NetBSD which has two dynamic linkers -# and we need to use the one in /libexec instead of /usr/libexec -_DYNLINK_SH= if test "${PREFIX}" = "" && test -e /libexec/ld.elf_so; then \ - echo "-Wl,-dynamic-linker=/libexec/ld.elf_so"; \ - else \ - echo ""; \ - fi -_DYNLINK:= $(shell ${_DYNLINK_SH}) -LDFLAGS+= ${_DYNLINK} -LDFLAGS+= -Wl,-rpath=${PREFIX}/${LIBNAME} -LDFLAGS+= ${PROGLDFLAGS} - -CLEANFILES+= ${OBJS} ${PROG} - -all: depend ${PROG} - -%.o: %.c - ${CC} ${LOCAL_CFLAGS} ${LOCAL_CPPFLAGS} ${CFLAGS} ${CPPFLAGS} -c $< -o $@ - -${PROG}: ${SCRIPTS} ${OBJS} - ${CC} ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ ${OBJS} ${LDADD} - -clean: - rm -f ${CLEANFILES} - -extra_depend: - -include ${MK}/sys.mk -include ${MK}/os.mk -include ${MK}/depend.mk -include ${MK}/gitignore.mk diff --git a/mk/scripts.mk b/mk/scripts.mk deleted file mode 100644 index 14220916..00000000 --- a/mk/scripts.mk +++ /dev/null @@ -1,64 +0,0 @@ -# Install rules for our scripts -# Copyright (c) 2007-2015 The OpenRC Authors. -# See the Authors file at the top-level directory of this distribution and -# https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS -# -# This file is part of OpenRC. It is subject to the license terms in -# the LICENSE file found in the top-level directory of this -# distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE -# This file may not be copied, modified, propagated, or distributed -# except according to the terms contained in the LICENSE file. - -include ${MK}/sys.mk -include ${MK}/os.mk - -OBJS+= ${SRCS:.in=} - -_PKG_SED_SH= if test "${PREFIX}" = "${PKG_PREFIX}"; then echo "-e 's:@PKG_PREFIX@::g'"; else echo "-e 's:@PKG_PREFIX@:${PKG_PREFIX}:g'"; fi -_PKG_SED:= $(shell ${_PKG_SED_SH}) -_LCL_SED_SH= if test "${PREFIX}" = "${LOCAL_PREFIX}"; then echo "-e 's:@LOCAL_PREFIX@::g'"; else echo "-e 's:@LOCAL_PREFIX@:${LOCAL_PREFIX}:g'"; fi -_LCL_SED:= $(shell ${_LCL_SED_SH}) - -SED_REPLACE= -e 's:@SHELL@:${SH}:' -e 's:@LIB@:${LIBNAME}:g' -e 's:@SYSCONFDIR@:${SYSCONFDIR}:g' -e 's:@LIBEXECDIR@:${LIBEXECDIR}:g' -e 's:@PREFIX@:${PREFIX}:g' -e 's:@BINDIR@:${BINDIR}:g' -e 's:@SBINDIR@:${SBINDIR}:g' ${_PKG_SED} ${_LCL_SED} - -# Tweak our shell scripts -%.sh: %.sh.in - ${SED} ${SED_REPLACE} ${SED_EXTRA} $< > $@ - -%: %.in - ${SED} ${SED_REPLACE} ${SED_EXTRA} $< > $@ - -all: ${OBJS} ${TARGETS} - -realinstall: ${BIN} ${CONF} ${INC} - @if test -n "${DIR}"; then \ - ${ECHO} ${INSTALL} -d ${DESTDIR}/${DIR}; \ - ${INSTALL} -d ${DESTDIR}/${DIR} || exit $$?; \ - fi - @if test -n "${BIN}"; then \ - ${ECHO} ${INSTALL} -m ${BINMODE} ${BIN} ${DESTDIR}/${DIR}; \ - ${INSTALL} -m ${BINMODE} ${BIN} ${DESTDIR}/${DIR} || exit $$?; \ - fi - @if test -n "${INC}"; then \ - ${ECHO} ${INSTALL} -m ${INCMODE} ${INC} ${DESTDIR}/${DIR}; \ - ${INSTALL} -m ${INCMODE} ${INC} ${DESTDIR}/${DIR} || exit $$?; \ - fi - @for x in ${CONF}; do \ - if ! test -e ${DESTDIR}/${PREFIX}${DIR}/$$x; then \ - ${ECHO} ${INSTALL} -m ${CONFMODE} $$x ${DESTDIR}/${DIR}; \ - ${INSTALL} -m ${CONFMODE} $$x ${DESTDIR}/${DIR} || exit $$?; \ - fi; \ - done - -install: all realinstall ${INSTALLAFTER} - -check test:: - -# A lot of scripts don't have anything to clean -# Also, some rm implentation require a file argument regardless of error -# so we ensure that it has a bogus argument -CLEANFILES+= ${OBJS} -clean: - @if test -n "${CLEANFILES}"; then echo "rm -f ${CLEANFILES}"; rm -f ${CLEANFILES}; fi - -include ${MK}/gitignore.mk diff --git a/mk/subdir.mk b/mk/subdir.mk deleted file mode 100644 index 6d8ecd98..00000000 --- a/mk/subdir.mk +++ /dev/null @@ -1,38 +0,0 @@ -# Recursive rules -# Adapted from FreeBSDs bsd.subdir.mk -# Copyright (c) 2007-2015 The OpenRC Authors. -# See the Authors file at the top-level directory of this distribution and -# https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS -# -# This file is part of OpenRC. It is subject to the license terms in -# the LICENSE file found in the top-level directory of this -# distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE -# This file may not be copied, modified, propagated, or distributed -# except according to the terms contained in the LICENSE file. - -_+_ ?= + -ECHODIR ?= echo -_SUBDIR = @${_+_}for x in ${SUBDIR}; do \ - if test -d $$x; then \ - ${ECHODIR} "===> ${DIRPRFX}$$x (${@:realinstall=install})"; \ - cd $$x; \ - ${MAKE} ${@:realinstall=install} \ - DIRPRFX=${DIRPRFX}$$x/ || exit $$?; \ - cd ..; \ - fi; \ -done - -all: - ${_SUBDIR} -clean: - @if test -n "${CLEANFILES}"; then echo "rm -f ${CLEANFILES}"; rm -f ${CLEANFILES}; fi - ${_SUBDIR} -realinstall: - ${_SUBDIR} -install: realinstall ${INSTALLAFTER} -check test:: - ${_SUBDIR} -depend: - ${_SUBDIR} -ignore: - ${_SUBDIR} diff --git a/mk/sys.mk b/mk/sys.mk deleted file mode 100644 index 777b60d9..00000000 --- a/mk/sys.mk +++ /dev/null @@ -1,71 +0,0 @@ -# Generic system definitions -# Copyright (c) 2008-2015 The OpenRC Authors. -# See the Authors file at the top-level directory of this distribution and -# https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS -# -# This file is part of OpenRC. It is subject to the license terms in -# the LICENSE file found in the top-level directory of this -# distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE -# This file may not be copied, modified, propagated, or distributed -# except according to the terms contained in the LICENSE file. - -AR?= ar -CP?= cp -PKG_CONFIG?= pkg-config -ECHO?= echo -INSTALL?= install -RANLIB?= ranlib -SED?= sed -SH= /bin/sh - -PREFIX?= -ifeq (${PREFIX},) -UPREFIX= /usr -else -UPREFIX= ${PREFIX} -ifeq (${MKPREFIX},yes) -UPREFIX= ${PREFIX}/usr -endif -endif -LOCAL_PREFIX= $(UPREFIX)/local - -PICFLAG?= -fPIC - -SYSCONFDIR?= ${PREFIX}/etc -INITDIR?= ${SYSCONFDIR}/init.d -CONFDIR?= ${SYSCONFDIR}/conf.d -CONFMODE?= 0644 -LOCALDIR?= ${SYSCONFDIR}/local.d -SYSCTLDIR?= ${SYSCONFDIR}/sysctl.d - -BINDIR?= ${PREFIX}/bin -BINMODE?= 0755 - -SBINDIR?= ${PREFIX}/sbin -SBINMODE?= 0755 - -INCDIR?= ${UPREFIX}/include -INCMODE?= 0644 - -_LIBNAME_SH= case `readlink /lib` in /lib64|lib64) echo "lib64";; *) echo "lib";; esac -_LIBNAME:= $(shell ${_LIBNAME_SH}) -LIBNAME?= ${_LIBNAME} -LIBDIR?= ${UPREFIX}/${LIBNAME} -LIBMODE?= 0644 - -LIBEXECDIR?= ${PREFIX}/libexec/rc -PKGCONFIGDIR?= ${UPREFIX}/${LIBNAME}/pkgconfig - -MANPREFIX?= ${UPREFIX}/share -MANDIR?= ${MANPREFIX}/man -MANMODE?= 0644 - -BASHCOMPDIR?= ${UPREFIX}/share/bash-completion/completions - -DATADIR?= ${UPREFIX}/share/openrc -DATAMODE?= 0644 - -DOCDIR?= ${UPREFIX}/share/doc -DOCMODE?= 0644 - -ZSHCOMPDIR?= ${UPREFIX}/share/zsh/site-functions diff --git a/mk/termcap.mk b/mk/termcap.mk deleted file mode 100644 index 4890e35a..00000000 --- a/mk/termcap.mk +++ /dev/null @@ -1,17 +0,0 @@ -ifeq (${MKTERMCAP},ncurses) -TERMCAP_CFLAGS:= $(shell ${PKG_CONFIG} ncurses --cflags 2> /dev/null) -LTERMCAP:= $(shell ${PKG_CONFIG} ncurses --libs 2> /dev/null) -ifeq ($(LTERMCAP),) -LIBTERMCAP?= -lncurses -else -LIBTERMCAP?= $(LTERMCAP) -endif -CPPFLAGS+= -DHAVE_TERMCAP ${TERMCAP_CFLAGS} -LDADD+= ${LIBTERMCAP} -else ifeq (${MKTERMCAP},termcap) -LIBTERMCAP?= -ltermcap -CPPFLAGS+= -DHAVE_TERMCAP -LDADD+= ${LIBTERMCAP} -else ifneq (${MKTERMCAP},) -$(error If MKTERMCAP is defined, it must be ncurses or termcap) -endif diff --git a/pkgconfig/.gitignore b/pkgconfig/.gitignore deleted file mode 100644 index e50bf9c5..00000000 --- a/pkgconfig/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -einfo.pc -openrc.pc diff --git a/pkgconfig/Makefile b/pkgconfig/Makefile deleted file mode 100644 index 80514d6a..00000000 --- a/pkgconfig/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -DIR= ${PKGCONFIGDIR} -SRCS= einfo.pc.in openrc.pc.in -INC= einfo.pc openrc.pc - -.DEFAULT: - ${SED} -n -e 's/^VERSION=[[:space:]]*\([^[:space:]]*\).*/#define VERSION "\1${GITVER}\"/p' ../../Makefile > version.h - -SED_EXTRA= -e 's:@VERSION@:${VERSION}:g' - -MK= ../mk -include ../Makefile.inc -include ${MK}/scripts.mk diff --git a/runlevels/Makefile b/runlevels/Makefile deleted file mode 100644 index 8d8b8e9e..00000000 --- a/runlevels/Makefile +++ /dev/null @@ -1,102 +0,0 @@ -include ../mk/net.mk - -BOOT= bootmisc fsck hostname localmount loopback \ - root swap sysctl ${BOOT-${OS}} -DEFAULT= local netmount -NONETWORK= local -SHUTDOWN= savecache ${SHUTDOWN-${OS}} -SYSINIT= ${SYSINIT-${OS}} - -LEVELDIR= ${DESTDIR}/${SYSCONFDIR}/runlevels -SYSINITDIR= ${LEVELDIR}/sysinit -BOOTDIR= ${LEVELDIR}/boot -DEFAULTDIR= ${LEVELDIR}/default -NONETWORKDIR= ${LEVELDIR}/nonetwork -SHUTDOWNDIR= ${LEVELDIR}/shutdown - -ifeq (${MKNET},yes) -BOOT+= network staticroute -endif - -INITFILES= ../init.d - -MK= ../mk -include ${MK}/sys.mk -include ${MK}/os.mk -include ${MK}/gitignore.mk - -BOOT-${OS}= -SHUTDOWN-${OS}= -SYSINIT-${OS}= - -BOOT-BSD= hostid newsyslog savecore syslogd swap-blk urandom - -# Generic BSD stuff -BOOT-FreeBSD+= hostid modules newsyslog savecore syslogd urandom -# FreeBSD specific stuff -BOOT-FreeBSD+= adjkerntz dumpon syscons - -BOOT-Linux+= binfmt hwclock keymaps modules mtab procfs save-keymaps \ - save-termencoding seedrng termencoding -SHUTDOWN-Linux= killprocs mount-ro -SYSINIT-Linux= devfs cgroups dmesg sysfs - -# Generic BSD stuff -BOOT-NetBSD+= hostid newsyslog savecore syslogd urandom -# NetBSD specific stuff -BOOT-NetBSD+= devdb swap-blk ttys wscons - -all: - -install: - if ! test -d "${SYSINITDIR}"; then \ - ${INSTALL} -d ${SYSINITDIR} || exit $$?; \ - for x in ${SYSINIT}; do \ - if test "${MKPREFIX}" = yes; then \ - grep -q "keyword .*-prefix" ${INITFILES}/"$$x" && continue; \ - fi; \ - ln -snf ${INITDIR}/"$$x" ${SYSINITDIR}/"$$x" || exit $$?; done \ - fi - if ! test -d "${BOOTDIR}"; then \ - ${INSTALL} -d ${BOOTDIR} || exit $$?; \ - for x in ${BOOT}; do \ - if test "${MKPREFIX}" = yes; then \ - grep -q "keyword .*-prefix" ${INITFILES}/"$$x" && continue; \ - fi; \ - ln -snf ${INITDIR}/"$$x" ${BOOTDIR}/"$$x" || exit $$?; \ - done \ - fi - if ! test -d "${DEFAULTDIR}"; then \ - ${INSTALL} -d ${DEFAULTDIR} || exit $$?; \ - for x in ${DEFAULT}; do \ - if test "${MKPREFIX}" = yes; then \ - grep -q "keyword .*-prefix" ${INITFILES}/"$$x" && continue; \ - fi; \ - ln -snf ${INITDIR}/"$$x" ${DEFAULTDIR}/"$$x" || exit $$?; done \ - fi - if ! test -d "${NONETWORKDIR}"; then \ - ${INSTALL} -d ${NONETWORKDIR} || exit $$?; \ - for x in ${NONETWORK}; do \ - if test "${MKPREFIX}" = yes; then \ - grep -q "keyword .*-prefix" ${INITFILES}/"$$x" && continue; \ - fi; \ - ln -snf ${INITDIR}/"$$x" ${NONETWORKDIR}/"$$x" || exit $$?; done \ - fi - if ! test -d "${SHUTDOWNDIR}"; then \ - ${INSTALL} -d ${SHUTDOWNDIR} || exit $$?; \ - for x in ${SHUTDOWN}; do \ - if test "${MKPREFIX}" = yes; then \ - grep -q "keyword .*-prefix" ${INITFILES}/"$$x" && continue; \ - fi; \ - ln -snf ${INITDIR}/"$$x" ${SHUTDOWNDIR}/"$$x" || exit $$?; done \ - fi - if test "${MKSYSVINIT}" = yes && test "${OS}" = Linux; then \ - for x in tty1 tty2 tty3 tty4 tty5 tty6; do \ - ln -snf ${INITDIR}/agetty ${DESTDIR}/${INITDIR}/"agetty.$$x" || exit $$?; \ - ln -snf ${INITDIR}/agetty.$$x ${DEFAULTDIR}/"agetty.$$x" || exit $$?; \ - done; \ - fi - -check test:: - -clean: diff --git a/scripts/.gitignore b/scripts/.gitignore deleted file mode 100644 index e26c51ae..00000000 --- a/scripts/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -halt -poweroff -rc-sstat -reboot -shutdown diff --git a/scripts/Makefile b/scripts/Makefile deleted file mode 100644 index d2215b34..00000000 --- a/scripts/Makefile +++ /dev/null @@ -1,30 +0,0 @@ -MK= ../mk -include ${MK}/os.mk - -DIR= ${LIBEXECDIR}/bin -BIN= on_ac_power -INSTALLAFTER = _installafter - -ifeq (${OS},Linux) -SRCS+= rc-sstat.in -BIN+= rc-sstat -ifeq (${MKSYSVINIT},yes) -SRCS+= halt.in poweroff.in reboot.in shutdown.in -BIN+= halt poweroff reboot shutdown - endif -endif - -_installafter: -ifeq (${OS},Linux) - ${INSTALL} -d ${DESTDIR}${SBINDIR} - ln -sf ${DIR}/rc-sstat ${DESTDIR}/${SBINDIR}/rc-sstat -ifeq (${MKSYSVINIT},yes) - ln -sf ${DIR}/halt ${DESTDIR}/${SBINDIR}/halt - ln -sf ${DIR}/poweroff ${DESTDIR}/${SBINDIR}/poweroff - ln -sf ${DIR}/reboot ${DESTDIR}/${SBINDIR}/reboot - ln -sf ${DIR}/shutdown ${DESTDIR}/${SBINDIR}/shutdown - ln -sf openrc-init ${DESTDIR}/${SBINDIR}/init -endif -endif - -include ${MK}/scripts.mk diff --git a/scripts/halt.in b/scripts/halt.in deleted file mode 100644 index 209dc130..00000000 --- a/scripts/halt.in +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/sh - -option_arg= -poweroff_arg= -while getopts :nwdfiph opt; do - case "$opt" in - n) ;; - w) poweroff_arg=--write-only ;; - d) option_arg=--no-write ;; - f) ;; - i) ;; - p) poweroff_arg=--poweroff ;; - [?]) printf "%s\n" "${0##*/}: invalid command line option" >&2 - exit 1 - ;; - esac -done -shift $((OPTIND-1)) - -if [ -z "${poweroff_arg}" ]; then - poweroff_arg=--poweroff -fi - -script_args="$@" -if [ -z "${script_args}" ]; then - script_args=now -fi - -exec @SBINDIR@/openrc-shutdown ${option_arg} ${poweroff_arg} "${script_args}" diff --git a/scripts/meson.build b/scripts/meson.build deleted file mode 100644 index 07d38acc..00000000 --- a/scripts/meson.build +++ /dev/null @@ -1,45 +0,0 @@ -script_conf_data = configuration_data() -script_conf_data.set('SBINDIR', sbindir) - -script_dir = rc_libexecdir / 'bin' - -scripts_internal = [ - 'on_ac_power', - ] - -scripts_Linux = [ - 'rc-sstat.in', - ] - -scripts_sysvinit = [ - 'halt.in', - 'poweroff.in', - 'shutdown.in', - 'reboot.in', - ] - -install_data(scripts_internal, - install_dir : script_dir, - install_mode: 'rwxr-xr-x') - -if os == 'Linux' - foreach file : scripts_Linux - configure_file(input : file, - output : '@BASENAME@', - configuration : script_conf_data, - install_dir: script_dir, - install_mode: 'rwxr-xr-x') - endforeach - if get_option('sysvinit') - foreach file : scripts_sysvinit - configure_file(input : file, - output : '@BASENAME@', - configuration : script_conf_data, - install_dir: script_dir, - install_mode: 'rwxr-xr-x') - endforeach - endif -endif - -meson.add_install_script('meson_script_links.sh', rc_libexecdir, - sbindir) diff --git a/scripts/meson_script_links.sh b/scripts/meson_script_links.sh deleted file mode 100755 index f13e767d..00000000 --- a/scripts/meson_script_links.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/sh - -set -e -set -u - -rc_libexecdir="$1" -sbindir="$2" -binaries=" halt poweroff rc-sstat reboot shutdown" -for f in $binaries; do - if [ -x "${DESTDIR}${rc_libexecdir}/bin/${f}" ]; then - ln -snf "${rc_libexecdir}/bin/${f}" \ - "${DESTDIR}${sbindir}/${f}" - fi -done -# sysvinit is active when halt exits -if [ -x "${DESTDIR}${rc_libexecdir}/bin/halt" ]; then - ln -snf "${sbindir}/openrc-init" \ - "${DESTDIR}${sbindir}/init" -fi diff --git a/scripts/on_ac_power b/scripts/on_ac_power deleted file mode 100755 index a4c1cf9c..00000000 --- a/scripts/on_ac_power +++ /dev/null @@ -1,47 +0,0 @@ -#!/bin/sh -# Detect AC power or not in a portable way -# Exit 0 if on AC power, 1 if not and 255 if we don't know how to work it out - -# Copyright (c) 2007-2015 The OpenRC Authors. -# See the Authors file at the top-level directory of this distribution and -# https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS -# -# This file is part of OpenRC. It is subject to the license terms in -# the LICENSE file found in the top-level directory of this -# distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE -# This file may not be copied, modified, propagated, or distributed -# except according to the terms contained in the LICENSE file. - -if [ -f /proc/acpi/ac_adapter/*/state ]; then - cat /proc/acpi/ac_adapter/*/state | while read line; do - case "$line" in - "state:"*"off-line") exit 128;; - esac - done -elif [ -d /sys/class/power_supply ]; then - for dir in /sys/class/power_supply/*/; do - [ "$(cat "${dir}/type")" != "Mains" ] && continue - [ "$(cat "${dir}/online")" = 0 ] && exit 128 - done -elif [ -f /proc/pmu/info ]; then - cat /proc/pmu/info | while read line; do - case "$line" in - "AC Power"*": 0") exit 128;; - esac - done -elif command -v envstat >/dev/null 2>&1; then - # NetBSD has envstat - envstat -d acpiacad0 2>/dev/null | while read line; do - case "$line" in - "connected:"*"OFF") exit 128;; - esac - done -elif sysctl -q hw.acpi.acline >/dev/null 2>/dev/null; then - case $(sysctl -n hw.acpi.acline) in - 0) exit 1;; - *) exit 0;; - esac -else - exit 255 -fi -[ $? != 128 ] diff --git a/scripts/poweroff.in b/scripts/poweroff.in deleted file mode 100644 index 4e944bdc..00000000 --- a/scripts/poweroff.in +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/sh - -option_arg= -poweroff_arg= -while getopts :nwdfiph opt; do - case "$opt" in - n) ;; - w) poweroff_arg=--write-only ;; - d) option_arg=--no-write ;; - f) ;; - i) ;; - [?]) printf "%s\n" "${0##*/}: invalid command line option" >&2 - exit 1 - ;; - esac -done -shift $((OPTIND-1)) - -if [ -z "${poweroff_arg}" ]; then - poweroff_arg=--poweroff -fi - -script_args="$@" -if [ -z "${script_args}" ]; then - script_args=now -fi - -exec @SBINDIR@/openrc-shutdown ${option_arg} ${poweroff_arg} "${script_args}" diff --git a/scripts/rc-sstat.in b/scripts/rc-sstat.in deleted file mode 100644 index 4811293d..00000000 --- a/scripts/rc-sstat.in +++ /dev/null @@ -1,149 +0,0 @@ -#!/bin/sh -# Copyright (c) 2015 The OpenRC Authors. -# See the Authors file at the top-level directory of this distribution and -# https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS -# -# This file is part of OpenRC. It is subject to the license terms in -# the LICENSE file found in the top-level directory of this -# distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE -# This file may not be copied, modified, propagated, or distributed -# except according to the terms contained in the LICENSE file. - -# Define variables -scandir="/run/openrc/s6-scan" -statfile=/dev/shm/s6-svstat.${USER} - -color_red='\E[01;31m' -color_green='\E[32m' -color_yellow='\E[01;33m' - -# Time Modules -uptimeModules() { - # Given a single integer argument representing seconds of uptime... - # convert uptime to a friendly human readable string: '2d 16h 58m 46s' - # define a variable to keep track of the longest length uptime string - uSec=${1:-0} - - uDay=$(( $uSec / 86400 )) - uSec=$(( $uSec % 86400 )) - uHour=$(( $uSec / 3600 )) - uSec=$(( $uSec % 3600 )) - uMin=$(( $uSec / 60 )) - uSec=$(( $uSec % 60 )) - - [ $uDay -ne 0 ] && pDay="${uDay}d " || pDay="" - [ $uHour -ne 0 ] && pHour="${uHour}h " || pHour="" - [ $uMin -ne 0 ] && pMin="${uMin}m " || pMin="" - [ $uSec -ne 0 ] && pSec="${uSec}s " || pSec="" - - parsedUptime="$( echo ${pDay}${pHour}${pMin}${pSec} | sed 's#[ \t]*$##' )" - uCharCount=${#parsedUptime} -} - -# Make sure we are running as root -if [ $(id -u) != 0 ]; then - printf "This command must be run as root\n" - exit 1 -fi - -# Make sure scandir exists -if [ ! -d $scandir ]; then - printf "%s\n" "$scandir does not exist" - exit 1 -fi - -# Make sure s6-svscan is running -if ! pgrep s6-svscan >/dev/null ; then - printf "s6-svscan is not running\n" - exit 1 -fi - -# If TERM is undefined (launching sstat through an ssh command) then make it vt100 -if [ -z $TERM -o $TERM = "dumb" ]; then - export TERM=vt100 -fi - -# Gather list of candidate services s6-supervise may be supervising -# filter for folders and symlinks at /run/openrc/s6-scan/* ommiting output starting with '.' -services="$(find $scandir -maxdepth 1 -mindepth 1 \( -type d -or -type l \) | awk -F'/' '{ if ( $NF !~ "^\\." ) print $NF}')" -if [ -z "$services" ]; then - printf "s6 found no services configured for supervision\n" - exit 1 -fi - -# Gather status for each service from s6-svstat -# write to tmp file in memory for non I/O bound repeatative access -rm -f $statfile 2>/dev/null -for service in $services ; do - echo "$service $(s6-svstat ${scandir}/${service})" >> $statfile -done - -# Define longest string from parsed uptime (default to 7 to match string length of 'Up Time') -timeStringLength=7 -for uptime in $(awk '$2 == "up" {print $5}' $statfile | sort -run) -do - uptimeModules $uptime - [ ${uCharCount} -gt $timeStringLength ] && timeStringLength=$uCharCount -done - - -# Print the status header like so... -# Service Name State PID Up Time Start Time -#---------------------------- ----- ----- -------------- ------------------- -printf "\n" -printf "%28s %5s %5s %${timeStringLength}s %19s\n" "Service Name" "State" "PID" "Up Time" "Start Time" -for dashes in 28 5 5 $timeStringLength 19 ; do - printf "%0.s-" $(seq 1 $dashes) ; echo -n ' ' -done && printf "\n" - - -# sshd up (pid 26300) 80373 seconds -cat $statfile | \ -while read line -do - set $line - - service=$1 - state=$2 - pid=${4/)/} - time=$5 - - # call function to convert time in seconds and define additional variables - uptimeModules $time - - if [ "$state" = up ]; then - if [ $time -lt 30 ]; then - # uptime < 30 seconds, color the whole line yellow - echo -en "$color_yellow" - # 1st 4 columns are printed with printf for space padding - printf "%28s %5s %5s %${timeStringLength}s" $service $state $pid "$parsedUptime" - # 4th column is output from date -d - echo -e " $(date -d "${time} seconds ago" "+%F %T")" - # reset terminal colors - tput sgr0 - else - printf "%28s" $service - # uptime > 30 seconds, color just the "state" value green - echo -en "$color_green" - printf " %5s" $state - # reset terminal colors - tput sgr0 - printf " %5s" $pid - printf " %${timeStringLength}s" "$parsedUptime" - echo -e " $(date -d "${time} seconds ago" "+%F %T")" - fi - else - printf "%28s" $service - echo -en "$color_red" - printf " %5s" $state - tput sgr0 - echo "" - fi -done - -# Cleanup -rm -f $statfile 2>/dev/null - -printf "\n\n" - -rc-status diff --git a/scripts/reboot.in b/scripts/reboot.in deleted file mode 100644 index bf50967a..00000000 --- a/scripts/reboot.in +++ /dev/null @@ -1,30 +0,0 @@ -#!/bin/sh - -option_arg= -poweroff_arg= -while getopts :nwdfhik opt; do - case "$opt" in - n) ;; - w) poweroff_arg=--write-only ;; - d) option_arg=--no-write ;; - f) ;; - h) ;; - i) ;; - k) poweroff_arg=--kexec ;; - [?]) printf "%s\n" "${0##*/}: invalid command line option" >&2 - exit 1 - ;; - esac -done -shift $((OPTIND-1)) - -if [ -z "${poweroff_arg}" ]; then - poweroff_arg=--reboot -fi - -script_args="$@" -if [ -z "${script_args}" ]; then - script_args=now -fi - -exec @SBINDIR@/openrc-shutdown ${option_arg} ${poweroff_arg} "${script_args}" diff --git a/scripts/shutdown.in b/scripts/shutdown.in deleted file mode 100644 index 23e5e31d..00000000 --- a/scripts/shutdown.in +++ /dev/null @@ -1,46 +0,0 @@ -#!/bin/sh - -do_halt=false -while getopts :akrhPHfFnct: opt; do - case "$opt" in - a) ;; - k) ;; - r) - shutdown_arg=--reboot - ;; - h) - do_halt=true - shutdown_arg=--poweroff - ;; - P) - if ! ${do_halt}; then - printf "%s\n" "The -P flag requires the -h flag" >&2 - exit 1 - fi - shutdown_arg=--poweroff - ;; - H) - if ! ${do_halt}; then - printf "%s\n" "The -H flag requires the -h flag" >&2 - exit 1 - fi - shutdown_arg=--halt - ;; - f) ;; - F) ;; - n) ;; - c) ;; - t) ;; - [?]) printf "%s\n" "${0##*/}: invalid command line option" >&2 - exit 1 - ;; - esac -done -shift $((OPTIND-1)) - -if [ -z "${shutdown_arg}" ]; then - shutdown_arg=--single -fi - -printf "%s %s\n" "@SBINDIR@/openrc-shutdown ${shutdown_arg}" "$@" -exec @SBINDIR@/openrc-shutdown ${shutdown_arg} "$@" diff --git a/sh/.gitignore b/sh/.gitignore deleted file mode 100644 index 69e3bd03..00000000 --- a/sh/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -functions.sh -gendepends.sh -openrc-run.sh -cgroup-release-agent.sh -init.sh -init-early.sh -binfmt.sh diff --git a/sh/Makefile b/sh/Makefile deleted file mode 100644 index 7f5dfb21..00000000 --- a/sh/Makefile +++ /dev/null @@ -1,33 +0,0 @@ -DIR= ${LIBEXECDIR}/sh -SRCS= init.sh.in functions.sh.in gendepends.sh.in \ - openrc-run.sh.in ${SRCS-${OS}} -INC= rc-mount.sh functions.sh rc-functions.sh runit.sh s6.sh \ - start-stop-daemon.sh supervise-daemon.sh ${INC-${OS}} -BIN= gendepends.sh init.sh openrc-run.sh ${BIN-${OS}} - -INSTALLAFTER= _installafter - -MK= ../mk -include ${MK}/os.mk - -SRCS-FreeBSD= -BIN-FreeBSD= - -SRCS-Linux= binfmt.sh.in cgroup-release-agent.sh.in init-early.sh.in -BIN-Linux= binfmt.sh cgroup-release-agent.sh init-early.sh -INC-Linux= rc-cgroup.sh - -SRCS-NetBSD= -BIN-NetBSD= - -include ${MK}/scripts.mk - -%.sh: %.sh${SFX} - ${SED} ${SED_REPLACE} ${SED_EXTRA} $< > $@ - -_installafter: - ${INSTALL} -d ${DESTDIR}/${INITDIR} - @# Put functions.sh into init for backwards compat - ln -snf ${LIBEXECDIR}/sh/functions.sh ${DESTDIR}/${INITDIR} || exit $$? - -check test:: diff --git a/src/Makefile b/src/Makefile deleted file mode 100644 index 0148c4d1..00000000 --- a/src/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -# Copyright (c) 2007-2008 Roy Marples -# Released under the 2-clause BSD license. - -SUBDIR= libeinfo librc rc - -MK= ../mk -include ${MK}/subdir.mk diff --git a/src/checkpath/checkpath.c b/src/checkpath/checkpath.c new file mode 100644 index 00000000..34f5c452 --- /dev/null +++ b/src/checkpath/checkpath.c @@ -0,0 +1,452 @@ +/* + * 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-2015 The OpenRC Authors. + * See the Authors file at the top-level directory of this distribution and + * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS + * + * This file is part of OpenRC. It is subject to the license terms in + * the LICENSE file found in the top-level directory of this + * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE + * This file may not be copied, modified, propagated, or distributed + * except according to the terms contained in the LICENSE file. + */ + +#define _GNU_SOURCE +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "einfo.h" +#include "rc.h" +#include "misc.h" +#include "selinux.h" +#include "_usage.h" + +typedef enum { + inode_unknown = 0, + inode_file = 1, + inode_dir = 2, + inode_fifo = 3, +} inode_t; + +const char *applet = NULL; +const char *extraopts ="path1 [path2] [...]"; +const char getoptstring[] = "dDfFpm:o:sW" getoptstring_COMMON; +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'}, + { "symlinks", 0, NULL, 's'}, + { "writable", 0, NULL, 'W'}, + longopts_COMMON +}; +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)", + "follow symbolic links (irrelivent on linux)", + "Check whether the path is writable or not", + longopts_help_COMMON +}; +const char *usagestring = NULL; + +static int get_dirfd(char *path, bool symlinks) +{ + char *ch; + char *item; + char *linkpath = NULL; + char *path_dupe; + char *str; + int components = 0; + int dirfd; + int flags = 0; + int new_dirfd; + struct stat st; + ssize_t linksize; + + if (!path || *path != '/') + eerrorx("%s: empty or relative path", applet); + dirfd = openat(dirfd, "/", O_RDONLY); + if (dirfd == -1) + eerrorx("%s: unable to open the root directory: %s", + applet, strerror(errno)); + ch = path; + while (*ch) { + if (*ch == '/') + components++; + ch++; + } + path_dupe = xstrdup(path); + item = strtok(path_dupe, "/"); +#ifdef O_PATH + flags |= O_PATH; +#endif + if (!symlinks) + flags |= O_NOFOLLOW; + flags |= O_RDONLY; + while (dirfd > 0 && item && components > 1) { + str = xstrdup(linkpath ? linkpath : item); + new_dirfd = openat(dirfd, str, flags); + if (new_dirfd == -1) + eerrorx("%s: %s: could not open %s: %s", applet, path, str, + strerror(errno)); + if (fstat(new_dirfd, &st) == -1) + eerrorx("%s: %s: unable to stat %s: %s", applet, path, item, + strerror(errno)); + if (S_ISLNK(st.st_mode) ) { + if (st.st_uid != 0) + eerrorx("%s: %s: symbolic link %s not owned by root", + applet, path, str); + linksize = st.st_size+1; + if (linkpath) + free(linkpath); + linkpath = xmalloc(linksize); + memset(linkpath, 0, linksize); + if (readlinkat(new_dirfd, "", linkpath, linksize) != st.st_size) + eerrorx("%s: symbolic link destination changed", applet); + /* + * now follow the symlink. + */ + close(new_dirfd); + } else { + /* now walk down the directory path */ + close(dirfd); + dirfd = new_dirfd; + free(linkpath); + linkpath = NULL; + item = strtok(NULL, "/"); + components--; + } + } + free(path_dupe); + free(linkpath); + return dirfd; +} + +static char *clean_path(char *path) +{ + char *ch; + char *ch2; + char *str; + str = xmalloc(strlen(path) + 1); + ch = path; + ch2 = str; + while (true) { + *ch2 = *ch; + ch++; + ch2++; + if (!*(ch-1)) + break; + while (*(ch - 1) == '/' && *ch == '/') + ch++; + } + /* get rid of trailing / characters */ + while ((ch = strrchr(str, '/'))) { + if (ch == str) + break; + if (!*(ch+1)) + *ch = 0; + else + break; + } + return str; +} + +static int do_check(char *path, uid_t uid, gid_t gid, mode_t mode, + inode_t type, bool trunc, bool chowner, bool symlinks, bool selinux_on) +{ + struct stat st; + char *name = NULL; + int dirfd; + int fd; + int flags; + int r; + int readfd; + int readflags; + int u; + + memset(&st, 0, sizeof(st)); + flags = O_CREAT|O_NDELAY|O_WRONLY|O_NOCTTY; + readflags = O_NDELAY|O_NOCTTY|O_RDONLY; +#ifdef O_CLOEXEC + flags |= O_CLOEXEC; + readflags |= O_CLOEXEC; +#endif +#ifdef O_NOFOLLOW + flags |= O_NOFOLLOW; + readflags |= O_NOFOLLOW; +#endif + if (trunc) + flags |= O_TRUNC; + xasprintf(&name, "%s", basename_c(path)); + dirfd = get_dirfd(path, symlinks); + readfd = openat(dirfd, name, readflags); + if (readfd == -1 || (type == inode_file && trunc)) { + if (type == inode_file) { + einfo("%s: creating file", path); + if (!mode) /* 664 */ + mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH; + u = umask(0); + fd = openat(dirfd, name, flags, mode); + umask(u); + if (fd == -1) { + eerror("%s: open: %s", applet, strerror(errno)); + return -1; + } + if (readfd != -1 && trunc) + close(readfd); + readfd = 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 = mkdirat(dirfd, name, mode); + umask(u); + if (r == -1 && errno != EEXIST) { + eerror("%s: mkdirat: %s", applet, + strerror (errno)); + return -1; + } + readfd = openat(dirfd, name, readflags); + if (readfd == -1) { + eerror("%s: unable to open directory: %s", applet, + strerror(errno)); + return -1; + } + } 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; + } + readfd = openat(dirfd, name, readflags); + if (readfd == -1) { + eerror("%s: unable to open fifo: %s", applet, + strerror(errno)); + return -1; + } + } + } + if (fstat(readfd, &st) != -1) { + if (type != inode_dir && S_ISDIR(st.st_mode)) { + eerror("%s: is a directory", path); + close(readfd); + return 1; + } + if (type != inode_file && S_ISREG(st.st_mode)) { + eerror("%s: is a file", path); + close(readfd); + return 1; + } + if (type != inode_fifo && S_ISFIFO(st.st_mode)) { + eerror("%s: is a fifo", path); + close(readfd); + return -1; + } + + if (mode && (st.st_mode & 0777) != mode) { + if ((type != inode_dir) && (st.st_nlink > 1)) { + eerror("%s: chmod: Too many hard links to %s", applet, path); + close(readfd); + return -1; + } + if (S_ISLNK(st.st_mode)) { + eerror("%s: chmod: %s %s", applet, path, " is a symbolic link"); + close(readfd); + return -1; + } + einfo("%s: correcting mode", path); + if (fchmod(readfd, mode)) { + eerror("%s: chmod: %s", applet, strerror(errno)); + close(readfd); + return -1; + } + } + + if (chowner && (st.st_uid != uid || st.st_gid != gid)) { + if ((type != inode_dir) && (st.st_nlink > 1)) { + eerror("%s: chown: %s %s", applet, "Too many hard links to", path); + close(readfd); + return -1; + } + if (S_ISLNK(st.st_mode)) { + eerror("%s: chown: %s %s", applet, path, " is a symbolic link"); + close(readfd); + return -1; + } + einfo("%s: correcting owner", path); + if (fchown(readfd, uid, gid)) { + eerror("%s: chown: %s", applet, strerror(errno)); + close(readfd); + return -1; + } + } + if (selinux_on) + selinux_util_label(path); + } else { + eerror("fstat: %s: %s", path, strerror(errno)); + close(readfd); + return -1; + } + close(readfd); + + 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; +} + +int main(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 symlinks = false; + bool writable = false; + bool selinux_on = false; + char *path = NULL; + + applet = basename_c(argv[0]); + while ((opt = getopt_long(argc, argv, getoptstring, + longopts, (int *) 0)) != -1) + { + switch (opt) { + case 'D': + trunc = true; + /* falls through */ + case 'd': + type = inode_dir; + break; + case 'F': + trunc = true; + /* falls through */ + 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 's': +#ifndef O_PATH + symlinks = true; +#endif + 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; + + if (selinux_util_open() == 1) + selinux_on = true; + + while (optind < argc) { + path = clean_path(argv[optind]); + if (writable) + exit(!is_writable(path)); + if (do_check(path, uid, gid, mode, type, trunc, chowner, + symlinks, selinux_on)) + retval = EXIT_FAILURE; + optind++; + free(path); + } + + if (selinux_on) + selinux_util_close(); + + return retval; +} diff --git a/src/checkpath/meson.build b/src/checkpath/meson.build new file mode 100644 index 00000000..300dcf2f --- /dev/null +++ b/src/checkpath/meson.build @@ -0,0 +1,9 @@ +executable('checkpath', + ['checkpath.c', misc_c, usage_c, selinux_c, + version_h], + c_args : [cc_audit_flags, cc_branding_flags, cc_pam_flags, cc_selinux_flags], + include_directories: [incdir, einfo_incdir, rc_incdir], + link_with: [libeinfo, librc], + dependencies: [audit_dep, pam_dep, pam_misc_dep, selinux_dep, crypt_dep], + install: true, + install_dir: rc_bindir) diff --git a/src/common/meson.build b/src/common/meson.build deleted file mode 100644 index 2e421658..00000000 --- a/src/common/meson.build +++ /dev/null @@ -1,6 +0,0 @@ -version_h = vcs_tag( - input : 'version.h.in', - output : 'version.h') -version_f = vcs_tag( - input : 'version.in', - output : 'version') diff --git a/src/common/version.h.in b/src/common/version.h.in deleted file mode 100644 index 4f228f55..00000000 --- a/src/common/version.h.in +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright (c) 2007-2015 The OpenRC Authors. - * See the Authors file at the top-level directory of this distribution and - * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS - * - * This file is part of OpenRC. It is subject to the license terms in - * the LICENSE file found in the top-level directory of this - * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE - * This file may not be copied, modified, propagated, or distributed - * except according to the terms contained in the LICENSE file. - */ - -#ifndef _VERSION_H_ -#define _VERSION_H_ - -#define VERSION "@VCS_TAG@" - -#endif diff --git a/src/common/version.in b/src/common/version.in deleted file mode 100644 index 42179405..00000000 --- a/src/common/version.in +++ /dev/null @@ -1 +0,0 @@ -@VCS_TAG@ diff --git a/src/einfo/einfo.c b/src/einfo/einfo.c new file mode 100644 index 00000000..1778efe4 --- /dev/null +++ b/src/einfo/einfo.c @@ -0,0 +1,228 @@ +/* + * Copyright (c) 2007-2015 The OpenRC Authors. + * See the Authors file at the top-level directory of this distribution and + * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS + * + * This file is part of OpenRC. It is subject to the license terms in + * the LICENSE file found in the top-level directory of this + * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE + * This file may not be copied, modified, propagated, or distributed + * except according to the terms contained in the LICENSE file. + */ + +#define SYSLOG_NAMES + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "einfo.h" +#include "helpers.h" + +/* usecs to wait while we poll the file existance */ +#define WAIT_INTERVAL 20000000 + +const char *applet = NULL; + +static int syslog_decode(char *name, const CODE *codetab) +{ + const 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; +} + +int main(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; + + applet = basename_c(argv[0]); + 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; +} diff --git a/src/einfo/meson.build b/src/einfo/meson.build new file mode 100644 index 00000000..df11d5fd --- /dev/null +++ b/src/einfo/meson.build @@ -0,0 +1,32 @@ +einfo_execs = [ + 'einfon', + 'einfo', + 'ewarnn', + 'ewarn', + 'eerrorn', + 'eerror', + 'ebegin', + 'eend', + 'ewend', + 'eindent', + 'eoutdent', + 'esyslog', + 'eval_ecolors', + 'ewaitfile', + 'veinfo', + 'vewarn', + 'vebegin', + 'veend', + 'vewend', + 'veindent', + 'veoutdent', + ] + +foreach exec: einfo_execs + executable(exec, + ['einfo.c', version_h], + include_directories: [incdir, einfo_incdir, rc_incdir], + link_with: [libeinfo, librc], + install: true, + install_dir: rc_bindir) +endforeach diff --git a/src/fstabinfo/fstabinfo.c b/src/fstabinfo/fstabinfo.c new file mode 100644 index 00000000..ef7d0609 --- /dev/null +++ b/src/fstabinfo/fstabinfo.c @@ -0,0 +1,339 @@ +/* + * fstabinfo.c + * Gets information about /etc/fstab. + */ + +/* + * Copyright (c) 2007-2015 The OpenRC Authors. + * See the Authors file at the top-level directory of this distribution and + * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS + * + * This file is part of OpenRC. It is subject to the license terms in + * the LICENSE file found in the top-level directory of this + * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE + * This file may not be copied, modified, propagated, or distributed + * except according to the terms contained in the LICENSE file. + */ + +#include + +#include +#include +#include +#include +#include +#include + +/* 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 +# 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 +# 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 "einfo.h" +#include "queue.h" +#include "rc.h" +#include "misc.h" +#include "_usage.h" + +const char *applet = NULL; +const char *extraopts = NULL; +const char getoptstring[] = "MRbmop:t:" getoptstring_COMMON; +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 +}; +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 +}; +const char *usagestring = NULL; + +#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: execvp: %s", applet, strerror(errno)); + _exit(EXIT_FAILURE); + /* NOTREACHED */ + default: + waitpid(pid, &status, 0); + if (WIFEXITED(status)) + return WEXITSTATUS(status); + else + return -1; + /* NOTREACHED */ + } +} + +#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 main(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 + + /* fail if there is no /etc/fstab */ + if (!exists("/etc/fstab")) + eerrorx("/etc/fstab does not exist"); + /* Ensure that we are only quiet when explicitly told to be */ + unsetenv("EINFO_QUIET"); + + applet = basename_c(argv[0]); + 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/fstabinfo/meson.build b/src/fstabinfo/meson.build new file mode 100644 index 00000000..d7b9153a --- /dev/null +++ b/src/fstabinfo/meson.build @@ -0,0 +1,7 @@ +executable('fstabinfo', + ['fstabinfo.c', misc_c, usage_c, version_h], + c_args : cc_branding_flags, + include_directories: [incdir, einfo_incdir, rc_incdir], + link_with: [libeinfo, librc], + install: true, + install_dir: rc_bindir) diff --git a/src/halt/halt.in b/src/halt/halt.in new file mode 100644 index 00000000..209dc130 --- /dev/null +++ b/src/halt/halt.in @@ -0,0 +1,29 @@ +#!/bin/sh + +option_arg= +poweroff_arg= +while getopts :nwdfiph opt; do + case "$opt" in + n) ;; + w) poweroff_arg=--write-only ;; + d) option_arg=--no-write ;; + f) ;; + i) ;; + p) poweroff_arg=--poweroff ;; + [?]) printf "%s\n" "${0##*/}: invalid command line option" >&2 + exit 1 + ;; + esac +done +shift $((OPTIND-1)) + +if [ -z "${poweroff_arg}" ]; then + poweroff_arg=--poweroff +fi + +script_args="$@" +if [ -z "${script_args}" ]; then + script_args=now +fi + +exec @SBINDIR@/openrc-shutdown ${option_arg} ${poweroff_arg} "${script_args}" diff --git a/src/halt/meson.build b/src/halt/meson.build new file mode 100644 index 00000000..dde1bca7 --- /dev/null +++ b/src/halt/meson.build @@ -0,0 +1,7 @@ +if os == 'Linux' and get_option('sysvinit') + configure_file(input : 'halt.in', + output : '@BASENAME@', + configuration : script_conf_data, + install_dir: sbindir, + install_mode: 'rwxr-xr-x') +endif diff --git a/src/includes/helpers.h b/src/includes/helpers.h deleted file mode 100644 index 44d76552..00000000 --- a/src/includes/helpers.h +++ /dev/null @@ -1,174 +0,0 @@ -/* - * helpers.h - * This is private to us and not for user consumption - */ - -/* - * Copyright (c) 2007-2015 The OpenRC Authors. - * See the Authors file at the top-level directory of this distribution and - * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS - * - * This file is part of OpenRC. It is subject to the license terms in - * the LICENSE file found in the top-level directory of this - * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE - * This file may not be copied, modified, propagated, or distributed - * except according to the terms contained in the LICENSE file. - */ - -#ifndef __HELPERS_H__ -#define __HELPERS_H__ - -#define ERRX fprintf (stderr, "out of memory\n"); exit (1) - -#define UNCONST(a) ((void *)(unsigned long)(const void *)(a)) - -#ifdef lint -# define _unused -#endif -#if __GNUC__ > 2 || defined(__INTEL_COMPILER) -# define _dead __attribute__((__noreturn__)) -# define _unused __attribute__((__unused__)) -# define _xasprintf(a, b) __attribute__((__format__(__printf__, a, b))) -#else -# define _dead -# define _unused -# define _xasprintf(a, b) -#endif - -#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) - -#ifdef __GLIBC__ -# if !defined (__UCLIBC__) && !defined (__dietlibc__) -# define strlcpy(dst, src, size) snprintf(dst, size, "%s", src) -# endif -#endif - -#ifndef timespecsub -#define timespecsub(tsp, usp, vsp) \ - do { \ - (vsp)->tv_sec = (tsp)->tv_sec - (usp)->tv_sec; \ - (vsp)->tv_nsec = (tsp)->tv_nsec - (usp)->tv_nsec; \ - if ((vsp)->tv_nsec < 0) { \ - (vsp)->tv_sec--; \ - (vsp)->tv_nsec += 1000000000L; \ - } \ - } while (/* CONSTCOND */ 0) -#endif - -#include -#include -#include - -_unused static void *xmalloc (size_t size) -{ - void *value = malloc(size); - - if (value) - return (value); - - ERRX; - /* NOTREACHED */ -} - -_unused static void *xrealloc(void *ptr, size_t size) -{ - void *value = realloc(ptr, size); - - if (value) - return (value); - - ERRX; - /* NOTREACHED */ -} - -_unused static char *xstrdup(const char *str) -{ - char *value; - - if (!str) - return (NULL); - - value = strdup(str); - - if (value) - return (value); - - ERRX; - /* NOTREACHED */ -} - -#undef ERRX - -/* - * basename_c never modifies the argument. As such, if there is a trailing - * slash then an empty string is returned. - */ -_unused static const char *basename_c(const char *path) -{ - const char *slash = strrchr(path, '/'); - - if (slash) - return (++slash); - return (path); -} - -_unused static bool exists(const char *pathname) -{ - struct stat buf; - - return (stat(pathname, &buf) == 0); -} - -_unused static bool existss(const char *pathname) -{ - struct stat buf; - - return (stat(pathname, &buf) == 0 && buf.st_size != 0); -} - -/* - * This is an OpenRC specific version of the asprintf() function. - * We do this to avoid defining the _GNU_SOURCE feature test macro on - * glibc systems and to insure that we have a consistent function across - * platforms. This also allows us to call our xmalloc and xrealloc - * functions to handle memory allocation. - * this function was originally written by Mike Frysinger. - */ -_unused _xasprintf(2,3) static int xasprintf(char **strp, const char *fmt, ...) -{ - va_list ap; - int len; - int memlen; - char *ret; - - /* - * Start with a buffer size that should cover the vast majority of uses - * (path construction). - */ - memlen = 4096; - ret = xmalloc(memlen); - - va_start(ap, fmt); - len = vsnprintf(ret, memlen, fmt, ap); - va_end(ap); - if (len >= memlen) { - /* - * Output was truncated, so increase buffer to exactly what we need. - */ - memlen = len + 1; - ret = xrealloc(ret, memlen); - va_start(ap, fmt); - len = vsnprintf(ret, len + 1, fmt, ap); - va_end(ap); - } - if (len < 0 || len >= memlen) { - /* Give up! */ - fprintf(stderr, "xasprintf: unable to format a buffer\n"); - free(ret); - exit(1); - } - *strp = ret; - return len; -} - -#endif diff --git a/src/includes/queue.h b/src/includes/queue.h deleted file mode 100644 index 67f801d7..00000000 --- a/src/includes/queue.h +++ /dev/null @@ -1,846 +0,0 @@ -/* $NetBSD: queue.h,v 1.67 2014/05/17 21:22:56 rmind Exp $ */ - -/* - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)queue.h 8.5 (Berkeley) 8/20/94 - */ - -#ifndef _SYS_QUEUE_H_ -#define _SYS_QUEUE_H_ - -/* - * This file defines five types of data structures: singly-linked lists, - * lists, simple queues, tail queues, and circular queues. - * - * A singly-linked list is headed by a single forward pointer. The - * elements are singly linked for minimum space and pointer manipulation - * overhead at the expense of O(n) removal for arbitrary elements. New - * elements can be added to the list after an existing element or at the - * head of the list. Elements being removed from the head of the list - * should use the explicit macro for this purpose for optimum - * efficiency. A singly-linked list may only be traversed in the forward - * direction. Singly-linked lists are ideal for applications with large - * datasets and few or no removals or for implementing a LIFO queue. - * - * A list is headed by a single forward pointer (or an array of forward - * pointers for a hash table header). The elements are doubly linked - * so that an arbitrary element can be removed without a need to - * traverse the list. New elements can be added to the list before - * or after an existing element or at the head of the list. A list - * may only be traversed in the forward direction. - * - * A simple queue is headed by a pair of pointers, one the head of the - * list and the other to the tail of the list. The elements are singly - * linked to save space, so elements can only be removed from the - * head of the list. New elements can be added to the list after - * an existing element, at the head of the list, or at the end of the - * list. A simple queue may only be traversed in the forward direction. - * - * A tail queue is headed by a pair of pointers, one to the head of the - * list and the other to the tail of the list. The elements are doubly - * linked so that an arbitrary element can be removed without a need to - * traverse the list. New elements can be added to the list before or - * after an existing element, at the head of the list, or at the end of - * the list. A tail queue may be traversed in either direction. - * - * A circle queue is headed by a pair of pointers, one to the head of the - * list and the other to the tail of the list. The elements are doubly - * linked so that an arbitrary element can be removed without a need to - * traverse the list. New elements can be added to the list before or after - * an existing element, at the head of the list, or at the end of the list. - * A circle queue may be traversed in either direction, but has a more - * complex end of list detection. - * - * For details on the use of these macros, see the queue(3) manual page. - */ - -/* - * Include the definition of NULL only on NetBSD because sys/null.h - * is not available elsewhere. This conditional makes the header - * portable and it can simply be dropped verbatim into any system. - * The caveat is that on other systems some other header - * must provide NULL before the macros can be used. - */ -#ifdef __NetBSD__ -#include -#endif - -#if defined(QUEUEDEBUG) -# if defined(_KERNEL) -# define QUEUEDEBUG_ABORT(...) panic(__VA_ARGS__) -# else -# include -# define QUEUEDEBUG_ABORT(...) err(1, __VA_ARGS__) -# endif -#endif - -/* - * Singly-linked List definitions. - */ -#define SLIST_HEAD(name, type) \ -struct name { \ - struct type *slh_first; /* first element */ \ -} - -#define SLIST_HEAD_INITIALIZER(head) \ - { NULL } - -#define SLIST_ENTRY(type) \ -struct { \ - struct type *sle_next; /* next element */ \ -} - -/* - * Singly-linked List access methods. - */ -#define SLIST_FIRST(head) ((head)->slh_first) -#define SLIST_END(head) NULL -#define SLIST_EMPTY(head) ((head)->slh_first == NULL) -#define SLIST_NEXT(elm, field) ((elm)->field.sle_next) - -#define SLIST_FOREACH(var, head, field) \ - for((var) = (head)->slh_first; \ - (var) != SLIST_END(head); \ - (var) = (var)->field.sle_next) - -#define SLIST_FOREACH_SAFE(var, head, field, tvar) \ - for ((var) = SLIST_FIRST((head)); \ - (var) != SLIST_END(head) && \ - ((tvar) = SLIST_NEXT((var), field), 1); \ - (var) = (tvar)) - -/* - * Singly-linked List functions. - */ -#define SLIST_INIT(head) do { \ - (head)->slh_first = SLIST_END(head); \ -} while (/*CONSTCOND*/0) - -#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ - (elm)->field.sle_next = (slistelm)->field.sle_next; \ - (slistelm)->field.sle_next = (elm); \ -} while (/*CONSTCOND*/0) - -#define SLIST_INSERT_HEAD(head, elm, field) do { \ - (elm)->field.sle_next = (head)->slh_first; \ - (head)->slh_first = (elm); \ -} while (/*CONSTCOND*/0) - -#define SLIST_REMOVE_AFTER(slistelm, field) do { \ - (slistelm)->field.sle_next = \ - SLIST_NEXT(SLIST_NEXT((slistelm), field), field); \ -} while (/*CONSTCOND*/0) - -#define SLIST_REMOVE_HEAD(head, field) do { \ - (head)->slh_first = (head)->slh_first->field.sle_next; \ -} while (/*CONSTCOND*/0) - -#define SLIST_REMOVE(head, elm, type, field) do { \ - if ((head)->slh_first == (elm)) { \ - SLIST_REMOVE_HEAD((head), field); \ - } \ - else { \ - struct type *curelm = (head)->slh_first; \ - while(curelm->field.sle_next != (elm)) \ - curelm = curelm->field.sle_next; \ - curelm->field.sle_next = \ - curelm->field.sle_next->field.sle_next; \ - } \ -} while (/*CONSTCOND*/0) - - -/* - * List definitions. - */ -#define LIST_HEAD(name, type) \ -struct name { \ - struct type *lh_first; /* first element */ \ -} - -#define LIST_HEAD_INITIALIZER(head) \ - { NULL } - -#define LIST_ENTRY(type) \ -struct { \ - struct type *le_next; /* next element */ \ - struct type **le_prev; /* address of previous next element */ \ -} - -/* - * List access methods. - */ -#define LIST_FIRST(head) ((head)->lh_first) -#define LIST_END(head) NULL -#define LIST_EMPTY(head) ((head)->lh_first == LIST_END(head)) -#define LIST_NEXT(elm, field) ((elm)->field.le_next) - -#define LIST_FOREACH(var, head, field) \ - for ((var) = ((head)->lh_first); \ - (var) != LIST_END(head); \ - (var) = ((var)->field.le_next)) - -#define LIST_FOREACH_SAFE(var, head, field, tvar) \ - for ((var) = LIST_FIRST((head)); \ - (var) != LIST_END(head) && \ - ((tvar) = LIST_NEXT((var), field), 1); \ - (var) = (tvar)) - -#define LIST_MOVE(head1, head2) do { \ - LIST_INIT((head2)); \ - if (!LIST_EMPTY((head1))) { \ - (head2)->lh_first = (head1)->lh_first; \ - LIST_INIT((head1)); \ - } \ -} while (/*CONSTCOND*/0) - -/* - * List functions. - */ -#if defined(QUEUEDEBUG) -#define QUEUEDEBUG_LIST_INSERT_HEAD(head, elm, field) \ - if ((head)->lh_first && \ - (head)->lh_first->field.le_prev != &(head)->lh_first) \ - QUEUEDEBUG_ABORT("LIST_INSERT_HEAD %p %s:%d", (head), \ - __FILE__, __LINE__); -#define QUEUEDEBUG_LIST_OP(elm, field) \ - if ((elm)->field.le_next && \ - (elm)->field.le_next->field.le_prev != \ - &(elm)->field.le_next) \ - QUEUEDEBUG_ABORT("LIST_* forw %p %s:%d", (elm), \ - __FILE__, __LINE__); \ - if (*(elm)->field.le_prev != (elm)) \ - QUEUEDEBUG_ABORT("LIST_* back %p %s:%d", (elm), \ - __FILE__, __LINE__); -#define QUEUEDEBUG_LIST_POSTREMOVE(elm, field) \ - (elm)->field.le_next = (void *)1L; \ - (elm)->field.le_prev = (void *)1L; -#else -#define QUEUEDEBUG_LIST_INSERT_HEAD(head, elm, field) -#define QUEUEDEBUG_LIST_OP(elm, field) -#define QUEUEDEBUG_LIST_POSTREMOVE(elm, field) -#endif - -#define LIST_INIT(head) do { \ - (head)->lh_first = LIST_END(head); \ -} while (/*CONSTCOND*/0) - -#define LIST_INSERT_AFTER(listelm, elm, field) do { \ - QUEUEDEBUG_LIST_OP((listelm), field) \ - if (((elm)->field.le_next = (listelm)->field.le_next) != \ - LIST_END(head)) \ - (listelm)->field.le_next->field.le_prev = \ - &(elm)->field.le_next; \ - (listelm)->field.le_next = (elm); \ - (elm)->field.le_prev = &(listelm)->field.le_next; \ -} while (/*CONSTCOND*/0) - -#define LIST_INSERT_BEFORE(listelm, elm, field) do { \ - QUEUEDEBUG_LIST_OP((listelm), field) \ - (elm)->field.le_prev = (listelm)->field.le_prev; \ - (elm)->field.le_next = (listelm); \ - *(listelm)->field.le_prev = (elm); \ - (listelm)->field.le_prev = &(elm)->field.le_next; \ -} while (/*CONSTCOND*/0) - -#define LIST_INSERT_HEAD(head, elm, field) do { \ - QUEUEDEBUG_LIST_INSERT_HEAD((head), (elm), field) \ - if (((elm)->field.le_next = (head)->lh_first) != LIST_END(head))\ - (head)->lh_first->field.le_prev = &(elm)->field.le_next;\ - (head)->lh_first = (elm); \ - (elm)->field.le_prev = &(head)->lh_first; \ -} while (/*CONSTCOND*/0) - -#define LIST_REMOVE(elm, field) do { \ - QUEUEDEBUG_LIST_OP((elm), field) \ - if ((elm)->field.le_next != NULL) \ - (elm)->field.le_next->field.le_prev = \ - (elm)->field.le_prev; \ - *(elm)->field.le_prev = (elm)->field.le_next; \ - QUEUEDEBUG_LIST_POSTREMOVE((elm), field) \ -} while (/*CONSTCOND*/0) - -#define LIST_REPLACE(elm, elm2, field) do { \ - if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \ - (elm2)->field.le_next->field.le_prev = \ - &(elm2)->field.le_next; \ - (elm2)->field.le_prev = (elm)->field.le_prev; \ - *(elm2)->field.le_prev = (elm2); \ - QUEUEDEBUG_LIST_POSTREMOVE((elm), field) \ -} while (/*CONSTCOND*/0) - -/* - * Simple queue definitions. - */ -#define SIMPLEQ_HEAD(name, type) \ -struct name { \ - struct type *sqh_first; /* first element */ \ - struct type **sqh_last; /* addr of last next element */ \ -} - -#define SIMPLEQ_HEAD_INITIALIZER(head) \ - { NULL, &(head).sqh_first } - -#define SIMPLEQ_ENTRY(type) \ -struct { \ - struct type *sqe_next; /* next element */ \ -} - -/* - * Simple queue access methods. - */ -#define SIMPLEQ_FIRST(head) ((head)->sqh_first) -#define SIMPLEQ_END(head) NULL -#define SIMPLEQ_EMPTY(head) ((head)->sqh_first == SIMPLEQ_END(head)) -#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next) - -#define SIMPLEQ_FOREACH(var, head, field) \ - for ((var) = ((head)->sqh_first); \ - (var) != SIMPLEQ_END(head); \ - (var) = ((var)->field.sqe_next)) - -#define SIMPLEQ_FOREACH_SAFE(var, head, field, next) \ - for ((var) = ((head)->sqh_first); \ - (var) != SIMPLEQ_END(head) && \ - ((next = ((var)->field.sqe_next)), 1); \ - (var) = (next)) - -/* - * Simple queue functions. - */ -#define SIMPLEQ_INIT(head) do { \ - (head)->sqh_first = NULL; \ - (head)->sqh_last = &(head)->sqh_first; \ -} while (/*CONSTCOND*/0) - -#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \ - if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \ - (head)->sqh_last = &(elm)->field.sqe_next; \ - (head)->sqh_first = (elm); \ -} while (/*CONSTCOND*/0) - -#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \ - (elm)->field.sqe_next = NULL; \ - *(head)->sqh_last = (elm); \ - (head)->sqh_last = &(elm)->field.sqe_next; \ -} while (/*CONSTCOND*/0) - -#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ - if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\ - (head)->sqh_last = &(elm)->field.sqe_next; \ - (listelm)->field.sqe_next = (elm); \ -} while (/*CONSTCOND*/0) - -#define SIMPLEQ_REMOVE_HEAD(head, field) do { \ - if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \ - (head)->sqh_last = &(head)->sqh_first; \ -} while (/*CONSTCOND*/0) - -#define SIMPLEQ_REMOVE_AFTER(head, elm, field) do { \ - if (((elm)->field.sqe_next = (elm)->field.sqe_next->field.sqe_next) \ - == NULL) \ - (head)->sqh_last = &(elm)->field.sqe_next; \ -} while (/*CONSTCOND*/0) - -#define SIMPLEQ_REMOVE(head, elm, type, field) do { \ - if ((head)->sqh_first == (elm)) { \ - SIMPLEQ_REMOVE_HEAD((head), field); \ - } else { \ - struct type *curelm = (head)->sqh_first; \ - while (curelm->field.sqe_next != (elm)) \ - curelm = curelm->field.sqe_next; \ - if ((curelm->field.sqe_next = \ - curelm->field.sqe_next->field.sqe_next) == NULL) \ - (head)->sqh_last = &(curelm)->field.sqe_next; \ - } \ -} while (/*CONSTCOND*/0) - -#define SIMPLEQ_CONCAT(head1, head2) do { \ - if (!SIMPLEQ_EMPTY((head2))) { \ - *(head1)->sqh_last = (head2)->sqh_first; \ - (head1)->sqh_last = (head2)->sqh_last; \ - SIMPLEQ_INIT((head2)); \ - } \ -} while (/*CONSTCOND*/0) - -#define SIMPLEQ_LAST(head, type, field) \ - (SIMPLEQ_EMPTY((head)) ? \ - NULL : \ - ((struct type *)(void *) \ - ((char *)((head)->sqh_last) - offsetof(struct type, field)))) - -/* - * Tail queue definitions. - */ -#define _TAILQ_HEAD(name, type, qual) \ -struct name { \ - qual type *tqh_first; /* first element */ \ - qual type *qual *tqh_last; /* addr of last next element */ \ -} -#define TAILQ_HEAD(name, type) _TAILQ_HEAD(name, struct type,) - -#define TAILQ_HEAD_INITIALIZER(head) \ - { TAILQ_END(head), &(head).tqh_first } - -#define _TAILQ_ENTRY(type, qual) \ -struct { \ - qual type *tqe_next; /* next element */ \ - qual type *qual *tqe_prev; /* address of previous next element */\ -} -#define TAILQ_ENTRY(type) _TAILQ_ENTRY(struct type,) - -/* - * Tail queue access methods. - */ -#define TAILQ_FIRST(head) ((head)->tqh_first) -#define TAILQ_END(head) (NULL) -#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) -#define TAILQ_LAST(head, headname) \ - (*(((struct headname *)((head)->tqh_last))->tqh_last)) -#define TAILQ_PREV(elm, headname, field) \ - (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) -#define TAILQ_EMPTY(head) (TAILQ_FIRST(head) == TAILQ_END(head)) - - -#define TAILQ_FOREACH(var, head, field) \ - for ((var) = ((head)->tqh_first); \ - (var) != TAILQ_END(head); \ - (var) = ((var)->field.tqe_next)) - -#define TAILQ_FOREACH_SAFE(var, head, field, next) \ - for ((var) = ((head)->tqh_first); \ - (var) != TAILQ_END(head) && \ - ((next) = TAILQ_NEXT(var, field), 1); (var) = (next)) - -#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ - for ((var) = (*(((struct headname *)((head)->tqh_last))->tqh_last));\ - (var) != TAILQ_END(head); \ - (var) = (*(((struct headname *)((var)->field.tqe_prev))->tqh_last))) - -#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, prev) \ - for ((var) = TAILQ_LAST((head), headname); \ - (var) != TAILQ_END(head) && \ - ((prev) = TAILQ_PREV((var), headname, field), 1); (var) = (prev)) - -/* - * Tail queue functions. - */ -#if defined(QUEUEDEBUG) -#define QUEUEDEBUG_TAILQ_INSERT_HEAD(head, elm, field) \ - if ((head)->tqh_first && \ - (head)->tqh_first->field.tqe_prev != &(head)->tqh_first) \ - QUEUEDEBUG_ABORT("TAILQ_INSERT_HEAD %p %s:%d", (head), \ - __FILE__, __LINE__); -#define QUEUEDEBUG_TAILQ_INSERT_TAIL(head, elm, field) \ - if (*(head)->tqh_last != NULL) \ - QUEUEDEBUG_ABORT("TAILQ_INSERT_TAIL %p %s:%d", (head), \ - __FILE__, __LINE__); -#define QUEUEDEBUG_TAILQ_OP(elm, field) \ - if ((elm)->field.tqe_next && \ - (elm)->field.tqe_next->field.tqe_prev != \ - &(elm)->field.tqe_next) \ - QUEUEDEBUG_ABORT("TAILQ_* forw %p %s:%d", (elm), \ - __FILE__, __LINE__); \ - if (*(elm)->field.tqe_prev != (elm)) \ - QUEUEDEBUG_ABORT("TAILQ_* back %p %s:%d", (elm), \ - __FILE__, __LINE__); -#define QUEUEDEBUG_TAILQ_PREREMOVE(head, elm, field) \ - if ((elm)->field.tqe_next == NULL && \ - (head)->tqh_last != &(elm)->field.tqe_next) \ - QUEUEDEBUG_ABORT("TAILQ_PREREMOVE head %p elm %p %s:%d",\ - (head), (elm), __FILE__, __LINE__); -#define QUEUEDEBUG_TAILQ_POSTREMOVE(elm, field) \ - (elm)->field.tqe_next = (void *)1L; \ - (elm)->field.tqe_prev = (void *)1L; -#else -#define QUEUEDEBUG_TAILQ_INSERT_HEAD(head, elm, field) -#define QUEUEDEBUG_TAILQ_INSERT_TAIL(head, elm, field) -#define QUEUEDEBUG_TAILQ_OP(elm, field) -#define QUEUEDEBUG_TAILQ_PREREMOVE(head, elm, field) -#define QUEUEDEBUG_TAILQ_POSTREMOVE(elm, field) -#endif - -#define TAILQ_INIT(head) do { \ - (head)->tqh_first = TAILQ_END(head); \ - (head)->tqh_last = &(head)->tqh_first; \ -} while (/*CONSTCOND*/0) - -#define TAILQ_INSERT_HEAD(head, elm, field) do { \ - QUEUEDEBUG_TAILQ_INSERT_HEAD((head), (elm), field) \ - if (((elm)->field.tqe_next = (head)->tqh_first) != TAILQ_END(head))\ - (head)->tqh_first->field.tqe_prev = \ - &(elm)->field.tqe_next; \ - else \ - (head)->tqh_last = &(elm)->field.tqe_next; \ - (head)->tqh_first = (elm); \ - (elm)->field.tqe_prev = &(head)->tqh_first; \ -} while (/*CONSTCOND*/0) - -#define TAILQ_INSERT_TAIL(head, elm, field) do { \ - QUEUEDEBUG_TAILQ_INSERT_TAIL((head), (elm), field) \ - (elm)->field.tqe_next = TAILQ_END(head); \ - (elm)->field.tqe_prev = (head)->tqh_last; \ - *(head)->tqh_last = (elm); \ - (head)->tqh_last = &(elm)->field.tqe_next; \ -} while (/*CONSTCOND*/0) - -#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ - QUEUEDEBUG_TAILQ_OP((listelm), field) \ - if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != \ - TAILQ_END(head)) \ - (elm)->field.tqe_next->field.tqe_prev = \ - &(elm)->field.tqe_next; \ - else \ - (head)->tqh_last = &(elm)->field.tqe_next; \ - (listelm)->field.tqe_next = (elm); \ - (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \ -} while (/*CONSTCOND*/0) - -#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ - QUEUEDEBUG_TAILQ_OP((listelm), field) \ - (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ - (elm)->field.tqe_next = (listelm); \ - *(listelm)->field.tqe_prev = (elm); \ - (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \ -} while (/*CONSTCOND*/0) - -#define TAILQ_REMOVE(head, elm, field) do { \ - QUEUEDEBUG_TAILQ_PREREMOVE((head), (elm), field) \ - QUEUEDEBUG_TAILQ_OP((elm), field) \ - if (((elm)->field.tqe_next) != TAILQ_END(head)) \ - (elm)->field.tqe_next->field.tqe_prev = \ - (elm)->field.tqe_prev; \ - else \ - (head)->tqh_last = (elm)->field.tqe_prev; \ - *(elm)->field.tqe_prev = (elm)->field.tqe_next; \ - QUEUEDEBUG_TAILQ_POSTREMOVE((elm), field); \ -} while (/*CONSTCOND*/0) - -#define TAILQ_REPLACE(head, elm, elm2, field) do { \ - if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != \ - TAILQ_END(head)) \ - (elm2)->field.tqe_next->field.tqe_prev = \ - &(elm2)->field.tqe_next; \ - else \ - (head)->tqh_last = &(elm2)->field.tqe_next; \ - (elm2)->field.tqe_prev = (elm)->field.tqe_prev; \ - *(elm2)->field.tqe_prev = (elm2); \ - QUEUEDEBUG_TAILQ_POSTREMOVE((elm), field); \ -} while (/*CONSTCOND*/0) - -#define TAILQ_CONCAT(head1, head2, field) do { \ - if (!TAILQ_EMPTY(head2)) { \ - *(head1)->tqh_last = (head2)->tqh_first; \ - (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \ - (head1)->tqh_last = (head2)->tqh_last; \ - TAILQ_INIT((head2)); \ - } \ -} while (/*CONSTCOND*/0) - -/* - * Singly-linked Tail queue declarations. - */ -#define STAILQ_HEAD(name, type) \ -struct name { \ - struct type *stqh_first; /* first element */ \ - struct type **stqh_last; /* addr of last next element */ \ -} - -#define STAILQ_HEAD_INITIALIZER(head) \ - { NULL, &(head).stqh_first } - -#define STAILQ_ENTRY(type) \ -struct { \ - struct type *stqe_next; /* next element */ \ -} - -/* - * Singly-linked Tail queue access methods. - */ -#define STAILQ_FIRST(head) ((head)->stqh_first) -#define STAILQ_END(head) NULL -#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next) -#define STAILQ_EMPTY(head) (STAILQ_FIRST(head) == STAILQ_END(head)) - -/* - * Singly-linked Tail queue functions. - */ -#define STAILQ_INIT(head) do { \ - (head)->stqh_first = NULL; \ - (head)->stqh_last = &(head)->stqh_first; \ -} while (/*CONSTCOND*/0) - -#define STAILQ_INSERT_HEAD(head, elm, field) do { \ - if (((elm)->field.stqe_next = (head)->stqh_first) == NULL) \ - (head)->stqh_last = &(elm)->field.stqe_next; \ - (head)->stqh_first = (elm); \ -} while (/*CONSTCOND*/0) - -#define STAILQ_INSERT_TAIL(head, elm, field) do { \ - (elm)->field.stqe_next = NULL; \ - *(head)->stqh_last = (elm); \ - (head)->stqh_last = &(elm)->field.stqe_next; \ -} while (/*CONSTCOND*/0) - -#define STAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ - if (((elm)->field.stqe_next = (listelm)->field.stqe_next) == NULL)\ - (head)->stqh_last = &(elm)->field.stqe_next; \ - (listelm)->field.stqe_next = (elm); \ -} while (/*CONSTCOND*/0) - -#define STAILQ_REMOVE_HEAD(head, field) do { \ - if (((head)->stqh_first = (head)->stqh_first->field.stqe_next) == NULL) \ - (head)->stqh_last = &(head)->stqh_first; \ -} while (/*CONSTCOND*/0) - -#define STAILQ_REMOVE(head, elm, type, field) do { \ - if ((head)->stqh_first == (elm)) { \ - STAILQ_REMOVE_HEAD((head), field); \ - } else { \ - struct type *curelm = (head)->stqh_first; \ - while (curelm->field.stqe_next != (elm)) \ - curelm = curelm->field.stqe_next; \ - if ((curelm->field.stqe_next = \ - curelm->field.stqe_next->field.stqe_next) == NULL) \ - (head)->stqh_last = &(curelm)->field.stqe_next; \ - } \ -} while (/*CONSTCOND*/0) - -#define STAILQ_FOREACH(var, head, field) \ - for ((var) = ((head)->stqh_first); \ - (var); \ - (var) = ((var)->field.stqe_next)) - -#define STAILQ_FOREACH_SAFE(var, head, field, tvar) \ - for ((var) = STAILQ_FIRST((head)); \ - (var) && ((tvar) = STAILQ_NEXT((var), field), 1); \ - (var) = (tvar)) - -#define STAILQ_CONCAT(head1, head2) do { \ - if (!STAILQ_EMPTY((head2))) { \ - *(head1)->stqh_last = (head2)->stqh_first; \ - (head1)->stqh_last = (head2)->stqh_last; \ - STAILQ_INIT((head2)); \ - } \ -} while (/*CONSTCOND*/0) - -#define STAILQ_LAST(head, type, field) \ - (STAILQ_EMPTY((head)) ? \ - NULL : \ - ((struct type *)(void *) \ - ((char *)((head)->stqh_last) - offsetof(struct type, field)))) - - -#ifndef _KERNEL -/* - * Circular queue definitions. Do not use. We still keep the macros - * for compatibility but because of pointer aliasing issues their use - * is discouraged! - */ - -/* - * __launder_type(): We use this ugly hack to work around the the compiler - * noticing that two types may not alias each other and elide tests in code. - * We hit this in the CIRCLEQ macros when comparing 'struct name *' and - * 'struct type *' (see CIRCLEQ_HEAD()). Modern compilers (such as GCC - * 4.8) declare these comparisons as always false, causing the code to - * not run as designed. - * - * This hack is only to be used for comparisons and thus can be fully const. - * Do not use for assignment. - * - * If we ever choose to change the ABI of the CIRCLEQ macros, we could fix - * this by changing the head/tail sentinal values, but see the note above - * this one. - */ -static __inline const void * __launder_type(const void *); -static __inline const void * -__launder_type(const void *__x) -{ - __asm __volatile("" : "+r" (__x)); - return __x; -} - -#if defined(QUEUEDEBUG) -#define QUEUEDEBUG_CIRCLEQ_HEAD(head, field) \ - if ((head)->cqh_first != CIRCLEQ_ENDC(head) && \ - (head)->cqh_first->field.cqe_prev != CIRCLEQ_ENDC(head)) \ - QUEUEDEBUG_ABORT("CIRCLEQ head forw %p %s:%d", (head), \ - __FILE__, __LINE__); \ - if ((head)->cqh_last != CIRCLEQ_ENDC(head) && \ - (head)->cqh_last->field.cqe_next != CIRCLEQ_ENDC(head)) \ - QUEUEDEBUG_ABORT("CIRCLEQ head back %p %s:%d", (head), \ - __FILE__, __LINE__); -#define QUEUEDEBUG_CIRCLEQ_ELM(head, elm, field) \ - if ((elm)->field.cqe_next == CIRCLEQ_ENDC(head)) { \ - if ((head)->cqh_last != (elm)) \ - QUEUEDEBUG_ABORT("CIRCLEQ elm last %p %s:%d", \ - (elm), __FILE__, __LINE__); \ - } else { \ - if ((elm)->field.cqe_next->field.cqe_prev != (elm)) \ - QUEUEDEBUG_ABORT("CIRCLEQ elm forw %p %s:%d", \ - (elm), __FILE__, __LINE__); \ - } \ - if ((elm)->field.cqe_prev == CIRCLEQ_ENDC(head)) { \ - if ((head)->cqh_first != (elm)) \ - QUEUEDEBUG_ABORT("CIRCLEQ elm first %p %s:%d", \ - (elm), __FILE__, __LINE__); \ - } else { \ - if ((elm)->field.cqe_prev->field.cqe_next != (elm)) \ - QUEUEDEBUG_ABORT("CIRCLEQ elm prev %p %s:%d", \ - (elm), __FILE__, __LINE__); \ - } -#define QUEUEDEBUG_CIRCLEQ_POSTREMOVE(elm, field) \ - (elm)->field.cqe_next = (void *)1L; \ - (elm)->field.cqe_prev = (void *)1L; -#else -#define QUEUEDEBUG_CIRCLEQ_HEAD(head, field) -#define QUEUEDEBUG_CIRCLEQ_ELM(head, elm, field) -#define QUEUEDEBUG_CIRCLEQ_POSTREMOVE(elm, field) -#endif - -#define CIRCLEQ_HEAD(name, type) \ -struct name { \ - struct type *cqh_first; /* first element */ \ - struct type *cqh_last; /* last element */ \ -} - -#define CIRCLEQ_HEAD_INITIALIZER(head) \ - { CIRCLEQ_END(&head), CIRCLEQ_END(&head) } - -#define CIRCLEQ_ENTRY(type) \ -struct { \ - struct type *cqe_next; /* next element */ \ - struct type *cqe_prev; /* previous element */ \ -} - -/* - * Circular queue functions. - */ -#define CIRCLEQ_INIT(head) do { \ - (head)->cqh_first = CIRCLEQ_END(head); \ - (head)->cqh_last = CIRCLEQ_END(head); \ -} while (/*CONSTCOND*/0) - -#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ - QUEUEDEBUG_CIRCLEQ_HEAD((head), field) \ - QUEUEDEBUG_CIRCLEQ_ELM((head), (listelm), field) \ - (elm)->field.cqe_next = (listelm)->field.cqe_next; \ - (elm)->field.cqe_prev = (listelm); \ - if ((listelm)->field.cqe_next == CIRCLEQ_ENDC(head)) \ - (head)->cqh_last = (elm); \ - else \ - (listelm)->field.cqe_next->field.cqe_prev = (elm); \ - (listelm)->field.cqe_next = (elm); \ -} while (/*CONSTCOND*/0) - -#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \ - QUEUEDEBUG_CIRCLEQ_HEAD((head), field) \ - QUEUEDEBUG_CIRCLEQ_ELM((head), (listelm), field) \ - (elm)->field.cqe_next = (listelm); \ - (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \ - if ((listelm)->field.cqe_prev == CIRCLEQ_ENDC(head)) \ - (head)->cqh_first = (elm); \ - else \ - (listelm)->field.cqe_prev->field.cqe_next = (elm); \ - (listelm)->field.cqe_prev = (elm); \ -} while (/*CONSTCOND*/0) - -#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \ - QUEUEDEBUG_CIRCLEQ_HEAD((head), field) \ - (elm)->field.cqe_next = (head)->cqh_first; \ - (elm)->field.cqe_prev = CIRCLEQ_END(head); \ - if ((head)->cqh_last == CIRCLEQ_ENDC(head)) \ - (head)->cqh_last = (elm); \ - else \ - (head)->cqh_first->field.cqe_prev = (elm); \ - (head)->cqh_first = (elm); \ -} while (/*CONSTCOND*/0) - -#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \ - QUEUEDEBUG_CIRCLEQ_HEAD((head), field) \ - (elm)->field.cqe_next = CIRCLEQ_END(head); \ - (elm)->field.cqe_prev = (head)->cqh_last; \ - if ((head)->cqh_first == CIRCLEQ_ENDC(head)) \ - (head)->cqh_first = (elm); \ - else \ - (head)->cqh_last->field.cqe_next = (elm); \ - (head)->cqh_last = (elm); \ -} while (/*CONSTCOND*/0) - -#define CIRCLEQ_REMOVE(head, elm, field) do { \ - QUEUEDEBUG_CIRCLEQ_HEAD((head), field) \ - QUEUEDEBUG_CIRCLEQ_ELM((head), (elm), field) \ - if ((elm)->field.cqe_next == CIRCLEQ_ENDC(head)) \ - (head)->cqh_last = (elm)->field.cqe_prev; \ - else \ - (elm)->field.cqe_next->field.cqe_prev = \ - (elm)->field.cqe_prev; \ - if ((elm)->field.cqe_prev == CIRCLEQ_ENDC(head)) \ - (head)->cqh_first = (elm)->field.cqe_next; \ - else \ - (elm)->field.cqe_prev->field.cqe_next = \ - (elm)->field.cqe_next; \ - QUEUEDEBUG_CIRCLEQ_POSTREMOVE((elm), field) \ -} while (/*CONSTCOND*/0) - -#define CIRCLEQ_FOREACH(var, head, field) \ - for ((var) = ((head)->cqh_first); \ - (var) != CIRCLEQ_ENDC(head); \ - (var) = ((var)->field.cqe_next)) - -#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \ - for ((var) = ((head)->cqh_last); \ - (var) != CIRCLEQ_ENDC(head); \ - (var) = ((var)->field.cqe_prev)) - -/* - * Circular queue access methods. - */ -#define CIRCLEQ_FIRST(head) ((head)->cqh_first) -#define CIRCLEQ_LAST(head) ((head)->cqh_last) -/* For comparisons */ -#define CIRCLEQ_ENDC(head) (__launder_type(head)) -/* For assignments */ -#define CIRCLEQ_END(head) ((void *)(head)) -#define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next) -#define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev) -#define CIRCLEQ_EMPTY(head) \ - (CIRCLEQ_FIRST(head) == CIRCLEQ_ENDC(head)) - -#define CIRCLEQ_LOOP_NEXT(head, elm, field) \ - (((elm)->field.cqe_next == CIRCLEQ_ENDC(head)) \ - ? ((head)->cqh_first) \ - : (elm->field.cqe_next)) -#define CIRCLEQ_LOOP_PREV(head, elm, field) \ - (((elm)->field.cqe_prev == CIRCLEQ_ENDC(head)) \ - ? ((head)->cqh_last) \ - : (elm->field.cqe_prev)) -#endif /* !_KERNEL */ - -#endif /* !_SYS_QUEUE_H_ */ diff --git a/src/includes/rc-misc.h b/src/includes/rc-misc.h deleted file mode 100644 index 8e37608f..00000000 --- a/src/includes/rc-misc.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - * rc-misc.h - * This is private to us and not for user consumption -*/ - -/* - * Copyright (c) 2007-2015 The OpenRC Authors. - * See the Authors file at the top-level directory of this distribution and - * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS - * - * This file is part of OpenRC. It is subject to the license terms in - * the LICENSE file found in the top-level directory of this - * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE - * This file may not be copied, modified, propagated, or distributed - * except according to the terms contained in the LICENSE file. - */ - -#ifndef __RC_MISC_H__ -#define __RC_MISC_H__ - -#include -#include -#include -#include -#include -#include -#include - -#include "helpers.h" - -#define RC_LEVEL_BOOT "boot" -#define RC_LEVEL_DEFAULT "default" - -#define RC_DEPTREE_CACHE RC_SVCDIR "/deptree" -#define RC_DEPTREE_SKEWED RC_SVCDIR "/clock-skewed" -#define RC_KRUNLEVEL RC_SVCDIR "/krunlevel" -#define RC_STARTING RC_SVCDIR "/rc.starting" -#define RC_STOPPING RC_SVCDIR "/rc.stopping" - -#define RC_SVCDIR_STARTING RC_SVCDIR "/starting" -#define RC_SVCDIR_INACTIVE RC_SVCDIR "/inactive" -#define RC_SVCDIR_STARTED RC_SVCDIR "/started" -#define RC_SVCDIR_COLDPLUGGED RC_SVCDIR "/coldplugged" - -char *rc_conf_value(const char *var); -bool rc_conf_yesno(const char *var); -void env_filter(void); -void env_config(void); -int signal_setup(int sig, void (*handler)(int)); -int signal_setup_restart(int sig, void (*handler)(int)); -int svc_lock(const char *); -int svc_unlock(const char *, int); -pid_t exec_service(const char *, const char *); - -/* - * Check whether path is writable or not, - * this also works properly with read-only filesystems - */ -int is_writable(const char *); - -#define service_start(service) exec_service(service, "start"); -#define service_stop(service) exec_service(service, "stop"); - -int parse_mode(mode_t *, char *); - -/* Handy function so we can wrap einfo around our deptree */ -RC_DEPTREE *_rc_deptree_load (int, int *); - -RC_SERVICE lookup_service_state(const char *service); -void from_time_t(char *time_string, time_t tv); -time_t to_time_t(char *timestring); -pid_t get_pid(const char *applet, const char *pidfile); - -#endif diff --git a/src/includes/rc-wtmp.h b/src/includes/rc-wtmp.h deleted file mode 100644 index edab322c..00000000 --- a/src/includes/rc-wtmp.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * rc-wtmp.h - * This is private to us and not for user consumption -*/ - -/* - * Copyright (c) 2017 The OpenRC Authors. - * See the Authors file at the top-level directory of this distribution and - * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS - * - * This file is part of OpenRC. It is subject to the license terms in - * the LICENSE file found in the top-level directory of this - * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE - * This file may not be copied, modified, propagated, or distributed - * except according to the terms contained in the LICENSE file. - */ - -#ifndef __RC_WTMP_H__ -#define __RC_WTMP_H__ - -#include - -void log_wtmp(const char *user, const char *id, pid_t pid, int type, - const char *line); - -#endif diff --git a/src/is_newer_than/is_newer_than.c b/src/is_newer_than/is_newer_than.c new file mode 100644 index 00000000..bd916ec5 --- /dev/null +++ b/src/is_newer_than/is_newer_than.c @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2016 The OpenRC Authors. + * See the Authors file at the top-level directory of this distribution and + * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS + * + * This file is part of OpenRC. It is subject to the license terms in + * the LICENSE file found in the top-level directory of this + * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE + * This file may not be copied, modified, propagated, or distributed + * except according to the terms contained in the LICENSE file. + */ + +#include +#include +#include +#include + +#include "rc.h" +#include "misc.h" + +int main(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; +} diff --git a/src/is_newer_than/meson.build b/src/is_newer_than/meson.build new file mode 100644 index 00000000..b20ebf58 --- /dev/null +++ b/src/is_newer_than/meson.build @@ -0,0 +1,6 @@ +executable('is_newer_than', + ['is_newer_than.c', misc_c, version_h], + include_directories: [incdir, einfo_incdir, rc_incdir], + link_with: [libeinfo, librc], + install: true, + install_dir: rc_bindir) diff --git a/src/is_older_than/is_older_than.c b/src/is_older_than/is_older_than.c new file mode 100644 index 00000000..b3f7072b --- /dev/null +++ b/src/is_older_than/is_older_than.c @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2016 The OpenRC Authors. + * See the Authors file at the top-level directory of this distribution and + * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS + * + * This file is part of OpenRC. It is subject to the license terms in + * the LICENSE file found in the top-level directory of this + * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE + * This file may not be copied, modified, propagated, or distributed + * except according to the terms contained in the LICENSE file. + */ + +#include +#include +#include +#include + +#include "rc.h" +#include "misc.h" + +int main(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; +} diff --git a/src/is_older_than/meson.build b/src/is_older_than/meson.build new file mode 100644 index 00000000..cd4c1c43 --- /dev/null +++ b/src/is_older_than/meson.build @@ -0,0 +1,6 @@ +executable('is_older_than', + ['is_older_than.c', misc_c, version_h], + include_directories: [incdir, einfo_incdir, rc_incdir], + link_with: [libeinfo, librc], + install: true, + install_dir: rc_bindir) diff --git a/src/kill_all/kill_all.c b/src/kill_all/kill_all.c new file mode 100644 index 00000000..704f30b8 --- /dev/null +++ b/src/kill_all/kill_all.c @@ -0,0 +1,260 @@ +/* + * kill_all.c + * Sends a signal to all processes on the system. + */ + +/* + * Copyright (c) 2017 The OpenRC Authors. + * See the Authors file at the top-level directory of this distribution and + * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS + * + * This file is part of OpenRC. It is subject to the license terms in + * the LICENSE file found in the top-level directory of this + * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE + * This file may not be copied, modified, propagated, or distributed + * except according to the terms contained in the LICENSE file. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "einfo.h" +#include "rc.h" +#include "misc.h" +#include "_usage.h" + +const char *applet = NULL; +const char *extraopts = "[signal number]"; +const char getoptstring[] = "do:" getoptstring_COMMON; +const struct option longopts[] = { + { "dry-run", 0, NULL, 'd' }, + { "omit", 1, NULL, 'o' }, + longopts_COMMON +}; +const char * const longopts_help[] = { + "print what would be done", + "omit this pid (can be repeated)", + longopts_help_COMMON +}; +const char *usagestring = NULL; + +static int mount_proc(void) +{ + pid_t pid; + pid_t rc; + int status; + + if (exists("/proc/version")) + return 0; + pid = fork(); + switch (pid) { + case -1: + syslog(LOG_ERR, "Unable to fork"); + return -1; + break; + case 0: + /* attempt to mount /proc */ + execlp("mount", "mount", "-t", "proc", "proc", "/proc", NULL); + syslog(LOG_ERR, "Unable to execute mount"); + exit(1); + break; + default: + /* wait for child process */ + while ((rc = wait(&status)) != pid) + if (rc < 0 && errno == ECHILD) + break; + if (rc != pid || WEXITSTATUS(status) != 0) + syslog(LOG_ERR, "mount returned non-zero exit status"); + break; + } + if (!exists("/proc/version")) { + syslog(LOG_ERR, "Could not mount /proc"); + return -1; + } + return 0; +} + +static bool is_user_process(pid_t pid) +{ + char *buf = NULL; + FILE *fp; + char *path = NULL; + pid_t temp_pid; + size_t size; + bool user_process = true; + + while (pid >0 && user_process) { + if (pid == 2) { + user_process = false; + continue; + } + xasprintf(&path, "/proc/%d/status", pid); + fp = fopen(path, "r"); + free(path); + /* + * if we could not open the file, the process disappeared, which + * leaves us no way to determine for sure whether it was a user + * process or kernel thread, so we say it is a kernel thread to + * avoid accidentally killing it. + */ + if (!fp) { + user_process = false; + continue; + } + temp_pid = -1; + while (!feof(fp)) { + buf = NULL; + if (getline(&buf, &size, fp) != -1) { + sscanf(buf, "PPid: %d", &temp_pid); + free(buf); + } else { + free(buf); + break; + } + } + fclose(fp); + if (temp_pid == -1) { + syslog(LOG_ERR, "Unable to read pid from /proc/%d/status", pid); + user_process = false; + continue; + } + pid = temp_pid; + } + return user_process; +} + +static int signal_processes(int sig, RC_STRINGLIST *omits, bool dryrun) +{ + sigset_t signals; + sigset_t oldsigs; + DIR *dir; + struct dirent *d; + char *buf = NULL; + pid_t pid; + int sendcount = 0; + + kill(-1, SIGSTOP); + sigfillset(&signals); + sigemptyset(&oldsigs); + sigprocmask(SIG_SETMASK, &signals, &oldsigs); + /* + * Open the /proc directory. + * CWD must be /proc to avoid problems if / is affected by the killing + * (i.e. depends on fuse). + */ + if (chdir("/proc") == -1) { + syslog(LOG_ERR, "chdir /proc failed"); + sigprocmask(SIG_SETMASK, &oldsigs, NULL); + kill(-1, SIGCONT); + return -1; + } + dir = opendir("."); + if (!dir) { + syslog(LOG_ERR, "cannot opendir(/proc)"); + sigprocmask(SIG_SETMASK, &oldsigs, NULL); + kill(-1, SIGCONT); + return -1; + } + + /* Walk through the directory. */ + while ((d = readdir(dir)) != NULL) { + /* Is this a process? */ + pid = (pid_t) atoi(d->d_name); + if (pid == 0) + continue; + + /* Is this a process we have been requested to omit? */ + if (buf) { + free(buf); + buf = NULL; + } + xasprintf(&buf, "%d", pid); + if (rc_stringlist_find(omits, buf)) + continue; + + /* Is this process in our session? */ + if (getsid(getpid()) == getsid(pid)) + continue; + + /* Is this a kernel thread? */ + if (!is_user_process(pid)) + continue; + + if (dryrun) + einfo("Would send signal %d to process %d", sig, pid); + else if (kill(pid, sig) == 0) + sendcount++; + } + closedir(dir); + sigprocmask(SIG_SETMASK, &oldsigs, NULL); + kill(-1, SIGCONT); + return sendcount; +} + +int main(int argc, char **argv) +{ + char *arg = NULL; + int opt; + bool dryrun = false; + RC_STRINGLIST *omits = rc_stringlist_new(); + int sig = SIGKILL; + char *here; + char *token; + + /* Ensure that we are only quiet when explicitly told to be */ + unsetenv("EINFO_QUIET"); + + applet = basename_c(argv[0]); + rc_stringlist_addu(omits, "1"); + while ((opt = getopt_long(argc, argv, getoptstring, + longopts, (int *) 0)) != -1) + { + switch (opt) { + case 'd': + dryrun = true; + break; + case 'o': + here = optarg; + while ((token = strsep(&here, ",;:"))) { + if ((pid_t) atoi(token) > 0) + rc_stringlist_addu(omits, token); + else { + eerror("Invalid omit pid value %s", token); + usage(EXIT_FAILURE); + } + } + break; + case_RC_COMMON_GETOPT + } + } + + if (argc > optind) { + arg = argv[optind]; + sig = atoi(arg); + if (sig <= 0 || sig > 31) { + rc_stringlist_free(omits); + eerror("Invalid signal %s", arg); + usage(EXIT_FAILURE); + } + } + + openlog(applet, LOG_CONS|LOG_PID, LOG_DAEMON); + if (mount_proc() != 0) { + rc_stringlist_free(omits); + eerrorx("Unable to mount /proc file system"); + } + signal_processes(sig, omits, dryrun); + rc_stringlist_free(omits); + return 0; +} diff --git a/src/kill_all/meson.build b/src/kill_all/meson.build new file mode 100644 index 00000000..177b537e --- /dev/null +++ b/src/kill_all/meson.build @@ -0,0 +1,9 @@ +if os == 'Linux' + executable('kill_all', + ['kill_all.c', usage_c, version_h], + c_args : cc_branding_flags, + include_directories: [incdir, einfo_incdir, rc_incdir], + link_with: [libeinfo,librc], + install: true, + install_dir: rc_bindir) +endif diff --git a/src/libeinfo/.gitignore b/src/libeinfo/.gitignore deleted file mode 100644 index 373284db..00000000 --- a/src/libeinfo/.gitignore +++ /dev/null @@ -1 +0,0 @@ -libeinfo.so.1 diff --git a/src/libeinfo/Makefile b/src/libeinfo/Makefile deleted file mode 100644 index e6ccb650..00000000 --- a/src/libeinfo/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -LIB= einfo -SHLIB_MAJOR= 1 -SRCS= libeinfo.c -INCS= einfo.h -VERSION_MAP= einfo.map - -LOCAL_CPPFLAGS+= -I../includes - -MK= ../../mk -include ${MK}/lib.mk -include ${MK}/cc.mk -include ${MK}/termcap.mk diff --git a/src/librc/.gitignore b/src/librc/.gitignore deleted file mode 100644 index e7fafe8c..00000000 --- a/src/librc/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -librc.so.1 -rc.h diff --git a/src/librc/Makefile b/src/librc/Makefile deleted file mode 100644 index 08c599ef..00000000 --- a/src/librc/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -LIB= rc -SHLIB_MAJOR= 1 -SRCS= librc.c librc-daemon.c librc-depend.c librc-misc.c \ - librc-stringlist.c -INCS= rc.h -VERSION_MAP= rc.map - -LDADD+= ${LIBKVM} - -LOCAL_CPPFLAGS+= -I../includes - -MK= ../../mk -include ${MK}/lib.mk -include ${MK}/cc.mk - -# Massage our header file for our dirs -SED_CMD= -e 's:@PREFIX@:${PREFIX}:g' -SED_CMD+= -e 's:@LIB@:${LIBNAME}:g' -SED_CMD+= -e 's:@SYSCONFDIR@:${SYSCONFDIR}:g' -SED_CMD+= -e 's:@LIBEXECDIR@:${LIBEXECDIR}:g' -SED_CMD+= -e 's:@BINDIR@:${BINDIR}:g' -SED_CMD+= -e 's:@SBINDIR@:${SBINDIR}:g' - -_PKG_PREFIX= -e 's:.*@PKG_PREFIX@.*:\#undef RC_PKG_PREFIX:g' -ifneq (${PKG_PREFIX},) -ifneq (${PKG_PREFIX},/) -ifneq (${PKG_PREFIX},${PREFIX}) -_PKG_PREFIX= -e 's:@PKG_PREFIX@:${PKG_PREFIX}:g' -endif -endif -endif -SED_CMD+= ${_PKG_PREFIX} - -_LCL_PREFIX= -e 's:@LOCAL_PREFIX@::g' -ifneq (${LOCAL_PREFIX},) -ifneq (${LOCAL_PREFIX},/) -ifneq (${LOCAL_PREFIX},${PREFIX}) -_LCL_PREFIX= -e 's:@LOCAL_PREFIX@:${LOCAL_PREFIX}:g' -endif -endif -endif -SED_CMD+= ${_LCL_PREFIX} - -%.h: %.h.in - ${SED} ${SED_CMD} $< > $@ -${SRCS}: rc.h - -CLEANFILES+= rc.h diff --git a/src/librc/librc.h b/src/librc/librc.h index 64198b2e..09ab9276 100644 --- a/src/librc/librc.h +++ b/src/librc/librc.h @@ -54,6 +54,6 @@ #endif #include "rc.h" -#include "rc-misc.h" +#include "misc.h" #endif diff --git a/src/mark_service/mark_service.c b/src/mark_service/mark_service.c new file mode 100644 index 00000000..eaab6eed --- /dev/null +++ b/src/mark_service/mark_service.c @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2007-2015 The OpenRC Authors. + * See the Authors file at the top-level directory of this distribution and + * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS + * + * This file is part of OpenRC. It is subject to the license terms in + * the LICENSE file found in the top-level directory of this + * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE + * This file may not be copied, modified, propagated, or distributed + * except according to the terms contained in the LICENSE file. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "einfo.h" +#include "rc.h" +#include "misc.h" + +const char *applet = NULL; + +int main(int argc, char **argv) +{ + bool ok = false; + char *svcname = getenv("RC_SVCNAME"); + char *service = NULL; + char *openrc_pid; + /* char *mtime; */ + pid_t pid; + RC_SERVICE bit; + /* size_t l; */ + + applet = basename_c(argv[0]); + 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 + openrc-run 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) { + openrc_pid = getenv("RC_OPENRC_PID"); + if (openrc_pid && sscanf(openrc_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(openrc_pid) + 4; + mtime = xmalloc(l); + snprintf(mtime, l, RC_SVCDIR "/exclusive/%s.%s", + svcname, openrc_pid); + if (exists(mtime) && unlink(mtime) != 0) + eerror("%s: unlink: %s", applet, strerror(errno)); + free(mtime); + */ + } + + return ok ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/src/mark_service/meson.build b/src/mark_service/meson.build new file mode 100644 index 00000000..ba046dcb --- /dev/null +++ b/src/mark_service/meson.build @@ -0,0 +1,20 @@ +mark_service_execs = [ + 'mark_service_starting', + 'mark_service_started', + 'mark_service_stopping', + 'mark_service_stopped', + 'mark_service_inactive', + 'mark_service_wasinactive', + 'mark_service_hotplugged', + 'mark_service_failed', + 'mark_service_crashed', + ] + +foreach exec : mark_service_execs + executable(exec, + ['mark_service.c', misc_c, version_h], + include_directories: [incdir, einfo_incdir, rc_incdir], + link_with: [libeinfo,librc], + install: true, + install_dir: rc_sbindir) +endforeach diff --git a/src/meson.build b/src/meson.build index d7390f54..76f6d8a1 100644 --- a/src/meson.build +++ b/src/meson.build @@ -1,4 +1,36 @@ -subdir('common') +script_conf_data = configuration_data() +script_conf_data.set('SBINDIR', sbindir) + +subdir('shared') subdir('libeinfo') subdir('librc') -subdir('rc') +subdir('checkpath') +subdir('einfo') +subdir('fstabinfo') +subdir('halt') +subdir('is_newer_than') +subdir('is_older_than') +subdir('kill_all') +subdir('mark_service') +subdir('mountinfo') +subdir('on_ac_power') +subdir('openrc') +subdir('openrc-init') +subdir('openrc-run') +subdir('openrc-shutdown') +subdir('poweroff') +subdir('rc-abort') +subdir('rc-depend') +subdir('rc-service') +subdir('rc-sstat') +subdir('rc-status') +subdir('rc-update') +subdir('reboot') +subdir('service') +subdir('seedrng') +subdir('shell_var') +subdir('shutdown') +subdir('start-stop-daemon') +subdir('supervise-daemon') +subdir('swclock') +subdir('value') diff --git a/src/mountinfo/meson.build b/src/mountinfo/meson.build new file mode 100644 index 00000000..71b8a652 --- /dev/null +++ b/src/mountinfo/meson.build @@ -0,0 +1,7 @@ +executable('mountinfo', + ['mountinfo.c', misc_c, usage_c, version_h], + c_args : cc_branding_flags, + include_directories: [incdir, einfo_incdir, rc_incdir], + link_with: [libeinfo, librc], + install: true, + install_dir: rc_bindir) diff --git a/src/mountinfo/mountinfo.c b/src/mountinfo/mountinfo.c new file mode 100644 index 00000000..c55b12ab --- /dev/null +++ b/src/mountinfo/mountinfo.c @@ -0,0 +1,488 @@ +/* + * mountinfo.c + * Obtains information about mounted filesystems. + */ + +/* + * Copyright 2007-2015 The OpenRC Authors. + * See the Authors file at the top-level directory of this distribution and + * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS + * + * This file is part of OpenRC. It is subject to the license terms in + * the LICENSE file found in the top-level directory of this + * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE + * This file may not be copied, modified, propagated, or distributed + * except according to the terms contained in the LICENSE file. + */ + +#include +#include + +#if defined(__DragonFly__) || defined(__FreeBSD__) +# include +# include +# define F_FLAGS f_flags +#elif defined(BSD) && !defined(__GNU__) +# include +# define statfs statvfs +# define F_FLAGS f_flag +#elif defined(__linux__) || (defined(__FreeBSD_kernel__) && \ + defined(__GLIBC__)) || defined(__GNU__) +# include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "einfo.h" +#include "queue.h" +#include "rc.h" +#include "misc.h" +#include "_usage.h" + +const char *applet = NULL; +const char *procmounts = "/proc/mounts"; +const char *extraopts = "[mount1] [mount2] ..."; +const char getoptstring[] = "f:F:n:N:o:O:p:P:iste:E:" getoptstring_COMMON; +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 +}; +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 +}; +const char *usagestring = NULL; + +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; + + 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 { + xasprintf(&tmp, "%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__)) || defined(__GNU__) +static struct mntent * +getmntfile(const char *file) +{ + struct mntent *ent = NULL; + FILE *fp; + + if (!exists("/etc/fstab")) + return NULL; + + 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; + size_t size; + char *p; + char *from; + char *to; + char *fst; + char *opts; + struct mntent *ent; + int netdev; + RC_STRINGLIST *list; + + if ((fp = fopen(procmounts, "r")) == NULL) + eerrorx("getmntinfo: %s", strerror(errno)); + + list = rc_stringlist_new(); + + buffer = NULL; + while (getline(&buffer, &size, fp) != -1) { + 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; + else + netdev = 1; + } + + process_mount(list, args, from, to, fst, opts, netdev); + free(buffer); + buffer = NULL; + } + 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; +} + +int main(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 = NULL; + 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); } + + applet = basename_c(argv[0]); + 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++]; + real_path = realpath(this_path, NULL); + if (real_path) + this_path = real_path; + rc_stringlist_add(args.mounts, this_path); + free(real_path); + real_path = NULL; + } + 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/on_ac_power/meson.build b/src/on_ac_power/meson.build new file mode 100644 index 00000000..aedc4daa --- /dev/null +++ b/src/on_ac_power/meson.build @@ -0,0 +1,3 @@ +install_data('on_ac_power', + install_dir : rc_bindir, + install_mode: 'rwxr-xr-x') diff --git a/src/on_ac_power/on_ac_power b/src/on_ac_power/on_ac_power new file mode 100644 index 00000000..a4c1cf9c --- /dev/null +++ b/src/on_ac_power/on_ac_power @@ -0,0 +1,47 @@ +#!/bin/sh +# Detect AC power or not in a portable way +# Exit 0 if on AC power, 1 if not and 255 if we don't know how to work it out + +# Copyright (c) 2007-2015 The OpenRC Authors. +# See the Authors file at the top-level directory of this distribution and +# https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS +# +# This file is part of OpenRC. It is subject to the license terms in +# the LICENSE file found in the top-level directory of this +# distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE +# This file may not be copied, modified, propagated, or distributed +# except according to the terms contained in the LICENSE file. + +if [ -f /proc/acpi/ac_adapter/*/state ]; then + cat /proc/acpi/ac_adapter/*/state | while read line; do + case "$line" in + "state:"*"off-line") exit 128;; + esac + done +elif [ -d /sys/class/power_supply ]; then + for dir in /sys/class/power_supply/*/; do + [ "$(cat "${dir}/type")" != "Mains" ] && continue + [ "$(cat "${dir}/online")" = 0 ] && exit 128 + done +elif [ -f /proc/pmu/info ]; then + cat /proc/pmu/info | while read line; do + case "$line" in + "AC Power"*": 0") exit 128;; + esac + done +elif command -v envstat >/dev/null 2>&1; then + # NetBSD has envstat + envstat -d acpiacad0 2>/dev/null | while read line; do + case "$line" in + "connected:"*"OFF") exit 128;; + esac + done +elif sysctl -q hw.acpi.acline >/dev/null 2>/dev/null; then + case $(sysctl -n hw.acpi.acline) in + 0) exit 1;; + *) exit 0;; + esac +else + exit 255 +fi +[ $? != 128 ] diff --git a/src/openrc-init/meson.build b/src/openrc-init/meson.build new file mode 100644 index 00000000..08cc318d --- /dev/null +++ b/src/openrc-init/meson.build @@ -0,0 +1,10 @@ +if os == 'Linux' + executable('openrc-init', + ['openrc-init.c', plugin_c, wtmp_c, version_h], + c_args : cc_selinux_flags, + include_directories: [incdir, einfo_incdir, rc_incdir], + link_with: [libeinfo, librc], + dependencies: [dl_dep, selinux_dep], + install: true, + install_dir: sbindir) +endif diff --git a/src/openrc-init/openrc-init.c b/src/openrc-init/openrc-init.c new file mode 100644 index 00000000..2e6d3677 --- /dev/null +++ b/src/openrc-init/openrc-init.c @@ -0,0 +1,333 @@ +/* + * openrc-init.c + * This is the init process (pid 1) for OpenRC. + * + * This is based on code written by James Hammons , so + * I would like to publically thank him for his work. + */ + +/* + * Copyright (c) 2017 The OpenRC Authors. + * See the Authors file at the top-level directory of this distribution and + * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS + * + * This file is part of OpenRC. It is subject to the license terms in + * the LICENSE file found in the top-level directory of this + * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE + * This file may not be copied, modified, propagated, or distributed + * except according to the terms contained in the LICENSE file. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_SELINUX +# include +#endif + +#include "helpers.h" +#include "rc.h" +#include "plugin.h" +#include "wtmp.h" +#include "version.h" + +static const char *path_default = "/sbin:/usr/sbin:/bin:/usr/bin"; +static const char *rc_default_runlevel = "default"; + +static void do_openrc(const char *runlevel) +{ + pid_t pid; + sigset_t all_signals; + sigset_t our_signals; + + sigfillset(&all_signals); + /* block all signals */ + sigprocmask(SIG_BLOCK, &all_signals, &our_signals); + pid = fork(); + switch (pid) { + case -1: + perror("fork"); + exit(1); + break; + case 0: + setsid(); + /* unblock all signals */ + sigprocmask(SIG_UNBLOCK, &all_signals, NULL); + printf("Starting %s runlevel\n", runlevel); + execlp("openrc", "openrc", runlevel, NULL); + perror("exec"); + exit(1); + break; + default: + /* restore our signal mask */ + sigprocmask(SIG_SETMASK, &our_signals, NULL); + while (waitpid(pid, NULL, 0) != pid) + if (errno == ECHILD) + break; + break; + } +} + +static void init(const char *default_runlevel) +{ + const char *runlevel = NULL; + do_openrc("sysinit"); + do_openrc("boot"); + if (default_runlevel) + runlevel = default_runlevel; + else + runlevel = rc_conf_value("rc_default_runlevel"); + if (!runlevel) + runlevel = rc_default_runlevel; + if (!rc_runlevel_exists(runlevel)) { + printf("%s is an invalid runlevel\n", runlevel); + runlevel = rc_default_runlevel; + } + do_openrc(runlevel); + log_wtmp("reboot", "~~", 0, RUN_LVL, "~~"); +} + +static void handle_reexec(char *my_name) +{ + execlp(my_name, my_name, "reexec", NULL); + return; +} + +static void handle_shutdown(const char *runlevel, int cmd) +{ + struct timespec ts; + + do_openrc(runlevel); + printf("Sending the final term signal\n"); + kill(-1, SIGTERM); + ts.tv_sec = 3; + ts.tv_nsec = 0; + nanosleep(&ts, NULL); + printf("Sending the final kill signal\n"); + kill(-1, SIGKILL); + sync(); + reboot(cmd); +} + +static void run_program(const char *prog) +{ + sigset_t full; + sigset_t old; + pid_t pid; + + /* We need to block signals until we have forked */ + sigfillset(&full); + sigprocmask(SIG_SETMASK, &full, &old); + pid = fork(); + if (pid == -1) { + perror("init"); + return; + } + if (pid == 0) { + /* Unmask signals */ + sigprocmask(SIG_SETMASK, &old, NULL); + execl(prog, prog, (char *)NULL); + perror("init"); + exit(1); + } + /* Unmask signals and wait for child */ + sigprocmask(SIG_SETMASK, &old, NULL); + if (rc_waitpid(pid) == -1) + perror("init"); +} + +static void open_shell(void) +{ + const char *shell; + struct passwd *pw; + +#ifdef __linux__ + const char *sys = rc_sys(); + + /* VSERVER systems cannot really drop to shells */ + if (sys && strcmp(sys, RC_SYS_VSERVER) == 0) + { + execlp("halt", "halt", "-f", (char *) NULL); + perror("init"); + return; + } +#endif + + shell = rc_conf_value("rc_shell"); + /* No shell set, so obey env, then passwd, then default to /bin/sh */ + if (!shell) { + shell = getenv("SHELL"); + if (!shell) { + pw = getpwuid(getuid()); + if (pw) + shell = pw->pw_shell; + if (!shell) + shell = "/bin/sh"; + } + } + run_program(shell); +} + +static void handle_single(void) +{ + do_openrc("single"); +} + +static void reap_zombies(void) +{ + pid_t pid; + + for (;;) { + pid = waitpid(-1, NULL, WNOHANG); + if (pid == 0) + break; + else if (pid == -1) { + if (errno == ECHILD) + break; + perror("waitpid"); + continue; + } + } +} + +static void signal_handler(int sig) +{ + switch (sig) { + case SIGINT: + handle_shutdown("reboot", RB_AUTOBOOT); + break; + case SIGTERM: +#ifdef SIGPWR + case SIGPWR: +#endif + handle_shutdown("shutdown", RB_HALT_SYSTEM); + break; + case SIGCHLD: + reap_zombies(); + break; + default: + printf("Unknown signal received, %d\n", sig); + break; + } +} + +int main(int argc, char **argv) +{ + char *default_runlevel; + char buf[2048]; + int count; + FILE *fifo; + bool reexec = false; + sigset_t signals; + struct sigaction sa; +#ifdef HAVE_SELINUX + int enforce = 0; +#endif + + if (getpid() != 1) + return 1; + +#ifdef HAVE_SELINUX + if (getenv("SELINUX_INIT") == NULL) { + if (is_selinux_enabled() != 1) { + if (selinux_init_load_policy(&enforce) == 0) { + putenv("SELINUX_INIT=YES"); + execv(argv[0], argv); + } else { + if (enforce > 0) { + /* + * SELinux in enforcing mode but load_policy failed + * At this point, we probably can't open /dev/console, + * so log() won't work + */ + fprintf(stderr,"Unable to load SELinux Policy.\n"); + fprintf(stderr,"Machine is in enforcing mode.\n"); + fprintf(stderr,"Halting now.\n"); + exit(1); + } + } + } + } +#endif + + printf("OpenRC init version %s starting\n", VERSION); + + if (argc > 1) + default_runlevel = argv[1]; + else + default_runlevel = NULL; + + if (default_runlevel && strcmp(default_runlevel, "reexec") == 0) + reexec = true; + + /* block all signals we do not handle */ + sigfillset(&signals); + sigdelset(&signals, SIGCHLD); + sigdelset(&signals, SIGINT); + sigdelset(&signals, SIGTERM); +#ifdef SIGPWR + sigdelset(&signals, SIGPWR); +#endif + sigprocmask(SIG_SETMASK, &signals, NULL); + + /* install signal handler */ + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = signal_handler; + sigaction(SIGCHLD, &sa, NULL); + sigaction(SIGINT, &sa, NULL); + sigaction(SIGTERM, &sa, NULL); +#ifdef SIGPWR + sigaction(SIGPWR, &sa, NULL); +#endif + reboot(RB_DISABLE_CAD); + + /* set default path */ + setenv("PATH", path_default, 1); + + if (!reexec) + init(default_runlevel); + + if (mkfifo(RC_INIT_FIFO, 0600) == -1 && errno != EEXIST) + perror("mkfifo"); + + for (;;) { + /* This will block until a command is sent down the pipe... */ + fifo = fopen(RC_INIT_FIFO, "r"); + if (!fifo) { + if (errno != EINTR) + perror("fopen"); + continue; + } + count = fread(buf, 1, sizeof(buf) - 1, fifo); + buf[count] = 0; + fclose(fifo); + printf("PID1: Received \"%s\" from FIFO...\n", buf); + if (strcmp(buf, "halt") == 0) + handle_shutdown("shutdown", RB_HALT_SYSTEM); + else if (strcmp(buf, "kexec") == 0) + handle_shutdown("reboot", RB_KEXEC); + else if (strcmp(buf, "poweroff") == 0) + handle_shutdown("shutdown", RB_POWER_OFF); + else if (strcmp(buf, "reboot") == 0) + handle_shutdown("reboot", RB_AUTOBOOT); + else if (strcmp(buf, "reexec") == 0) + handle_reexec(argv[0]); + else if (strcmp(buf, "single") == 0) { + handle_single(); + open_shell(); + init(default_runlevel); + } + } + return 0; +} diff --git a/src/openrc-run/meson.build b/src/openrc-run/meson.build new file mode 100644 index 00000000..2e4b29a0 --- /dev/null +++ b/src/openrc-run/meson.build @@ -0,0 +1,17 @@ +executable('openrc-run', + ['openrc-run.c', misc_c, plugin_c, selinux_c, usage_c, version_h], + c_args : [cc_audit_flags, cc_branding_flags, cc_pam_flags, cc_selinux_flags], + link_with: [libeinfo, librc], + dependencies: [audit_dep, dl_dep, pam_dep, pam_misc_dep, selinux_dep, util_dep, crypt_dep], + include_directories: [incdir, einfo_incdir, rc_incdir], + install: true, + install_dir: sbindir) + +executable('runscript', + ['openrc-run.c', misc_c, plugin_c, selinux_c, usage_c, version_h], + c_args : [cc_audit_flags, cc_branding_flags, cc_pam_flags, cc_selinux_flags], + link_with: [libeinfo, librc], + dependencies: [audit_dep, dl_dep, pam_dep, pam_misc_dep, selinux_dep, util_dep, crypt_dep], + include_directories: [incdir, einfo_incdir, rc_incdir], + install: true, + install_dir: sbindir) diff --git a/src/openrc-run/openrc-run.c b/src/openrc-run/openrc-run.c new file mode 100644 index 00000000..f9056110 --- /dev/null +++ b/src/openrc-run/openrc-run.c @@ -0,0 +1,1436 @@ +/* + * openrc-run.c + * Handle launching of init scripts. + */ + +/* + * Copyright (c) 2007-2015 The OpenRC Authors. + * See the Authors file at the top-level directory of this distribution and + * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS + * + * This file is part of OpenRC. It is subject to the license terms in + * the LICENSE file found in the top-level directory of this + * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE + * This file may not be copied, modified, propagated, or distributed + * except according to the terms contained in the LICENSE file. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(__linux__) || (defined(__FreeBSD_kernel__) && defined(__GLIBC__)) \ + || defined(__GNU__) +# include +#elif defined(__NetBSD__) || defined(__OpenBSD__) +# include +#else +# include +#endif + +#include "einfo.h" +#include "queue.h" +#include "rc.h" +#include "misc.h" +#include "plugin.h" +#include "selinux.h" +#include "_usage.h" + +#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 */ + +const char *applet = NULL; +const char *extraopts = "stop | start | restart | describe | zap"; +const char getoptstring[] = "dDsSvl:Z" getoptstring_COMMON; +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 +}; +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 +}; +const char *usagestring = NULL; + +static char *service, *runlevel, *ibsave, *prefix; +static RC_DEPTREE *deptree; +static RC_STRINGLIST *applet_list, *services, *tmplist; +static RC_STRINGLIST *restart_services; +static RC_STRINGLIST *need_services; +static RC_STRINGLIST *use_services; +static RC_STRINGLIST *want_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 *deptypes_b; /* broken deps */ +static RC_STRINGLIST *deptypes_n; /* needed deps */ +static RC_STRINGLIST *deptypes_nw; /* need+want deps */ +static RC_STRINGLIST *deptypes_nwu; /* need+want+use deps */ +static RC_STRINGLIST *deptypes_nwua; /* need+want+use+after deps */ +static RC_STRINGLIST *deptypes_m; /* needed deps for stopping */ +static RC_STRINGLIST *deptypes_mwua; /* need+want+use+after deps for stopping */ + +static void +handle_signal(int sig) +{ + int serrno = errno; + char *signame = NULL; + 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) + xasprintf(&signame, "SIGINT"); + /* FALLTHROUGH */ + case SIGTERM: + if (!signame) + xasprintf(&signame, "SIGTERM"); + /* FALLTHROUGH */ + case SIGQUIT: + if (!signame) + xasprintf(&signame, "SIGQUIT"); + /* Send the signal to our children too */ + if (service_pid > 0) + kill(service_pid, sig); + eerror("%s: caught %s, aborting", applet, signame); + free(signame); + exit(EXIT_FAILURE); + /* NOTREACHED */ + + default: + eerror("%s: caught unknown signal %d", applet, sig); + } + + /* Restore errno */ + errno = serrno; +} + +static void +unhotplug() +{ + char *file = NULL; + + xasprintf(&file, RC_SVCDIR "/hotplugged/%s", applet); + if (exists(file) && unlink(file) != 0) + eerror("%s: unlink `%s': %s", applet, file, strerror(errno)); + free(file); +} + +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(); + + rc_stringlist_free(deptypes_b); + rc_stringlist_free(deptypes_n); + rc_stringlist_free(deptypes_nw); + rc_stringlist_free(deptypes_nwu); + rc_stringlist_free(deptypes_nwua); + rc_stringlist_free(deptypes_m); + rc_stringlist_free(deptypes_mwua); + rc_deptree_free(deptree); + rc_stringlist_free(restart_services); + rc_stringlist_free(need_services); + rc_stringlist_free(use_services); + rc_stringlist_free(want_services); + rc_stringlist_free(services); + rc_stringlist_free(applet_list); + rc_stringlist_free(tmplist); + free(ibsave); + free(service); + free(prefix); + free(runlevel); +} + +/* 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 "/openrc-run.sh")) { + if (arg2) + einfov("Executing: %s %s %s %s %s", + RC_SVCDIR "/openrc-run.sh", RC_SVCDIR "/openrc-run.sh", + service, arg1, arg2); + else + einfov("Executing: %s %s %s %s", + RC_SVCDIR "/openrc-run.sh", RC_SVCDIR "/openrc-run.sh", + service, arg1); + execl(RC_SVCDIR "/openrc-run.sh", + RC_SVCDIR "/openrc-run.sh", + service, arg1, arg2, (char *) NULL); + eerror("%s: exec `" RC_SVCDIR "/openrc-run.sh': %s", + service, strerror(errno)); + _exit(EXIT_FAILURE); + } else { + if (arg2) + einfov("Executing: %s %s %s %s %s", + RC_LIBEXECDIR "/sh/openrc-run.sh", + RC_LIBEXECDIR "/sh/openrc-run.sh", + service, arg1, arg2); + else + einfov("Executing: %s %s %s %s", + RC_LIBEXECDIR "/sh/openrc-run.sh", + RC_LIBEXECDIR "/sh/openrc-run.sh", + service, arg1); + execl(RC_LIBEXECDIR "/sh/openrc-run.sh", + RC_LIBEXECDIR "/sh/openrc-run.sh", + service, arg1, arg2, (char *) NULL); + eerror("%s: exec `" RC_LIBEXECDIR "/sh/openrc-run.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 = NULL; + 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); + + xasprintf(&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); + free(file); + return true; + } + close(fd); + } + if (errno == ENOENT) { + free(file); + return true; + } + if (errno != EWOULDBLOCK) { + eerror("%s: open `%s': %s", applet, file, + strerror(errno)); + free(file); + exit(EXIT_FAILURE); + } + if (nanosleep(&interval, NULL) == -1) { + if (errno != EINTR) + goto finish; + } + if (!forever) { + timespecsub(&timeout, &interval, &timeout); + if (timeout.tv_sec <= 0) + goto finish; + 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; + } + } + } +finish: + free(file); + 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_deptypes(void) +{ + deptypes_b = rc_stringlist_new(); + rc_stringlist_add(deptypes_b, "broken"); + + deptypes_n = rc_stringlist_new(); + rc_stringlist_add(deptypes_n, "ineed"); + + deptypes_nw = rc_stringlist_new(); + rc_stringlist_add(deptypes_nw, "ineed"); + rc_stringlist_add(deptypes_nw, "iwant"); + + deptypes_nwu = rc_stringlist_new(); + rc_stringlist_add(deptypes_nwu, "ineed"); + rc_stringlist_add(deptypes_nwu, "iwant"); + rc_stringlist_add(deptypes_nwu, "iuse"); + + deptypes_nwua = rc_stringlist_new(); + rc_stringlist_add(deptypes_nwua, "ineed"); + rc_stringlist_add(deptypes_nwua, "iwant"); + rc_stringlist_add(deptypes_nwua, "iuse"); + rc_stringlist_add(deptypes_nwua, "iafter"); + + deptypes_m = rc_stringlist_new(); + rc_stringlist_add(deptypes_m, "needsme"); + + deptypes_mwua = rc_stringlist_new(); + rc_stringlist_add(deptypes_mwua, "needsme"); + rc_stringlist_add(deptypes_mwua, "wantsme"); + rc_stringlist_add(deptypes_mwua, "usesme"); + rc_stringlist_add(deptypes_mwua, "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 (!deptypes_b) + setup_deptypes(); + + services = rc_deptree_depends(deptree, deptypes_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, deptypes_n, + applet_list, runlevel, depoptions); + want_services = rc_deptree_depends(deptree, deptypes_nw, + applet_list, runlevel, depoptions); + use_services = rc_deptree_depends(deptree, deptypes_nwu, + 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, deptypes_nwua, 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(want_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 (!deptypes_m) + setup_deptypes(); + + services = rc_deptree_depends(deptree, deptypes_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, deptypes_mwua, 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; + } + } + free(list); + return allow; +} + +int main(int argc, char **argv) +{ + bool doneone = false; + bool runscript = false; + int retval, opt, depoptions = RC_DEP_TRACE; + RC_STRING *svc; + char *path = NULL; + char *lnk = NULL; + char *dir, *save = NULL, *saveLnk = NULL; + char *pidstr = NULL; + 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); + } + + applet = basename_c(argv[0]); + if (strcmp(applet, "runscript") == 0) + runscript = true; + + 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. */ + path = realpath(argv[1], NULL); + if (!path) { + fprintf(stderr, "realpath: %s\n", strerror(errno)); + exit(EXIT_FAILURE); + } + lnk = xmalloc(4096); + memset(lnk, 0, 4096); + if (readlink(argv[1], lnk, 4096-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; + xasprintf(&service, "%s/%s", dir, file); + if (stat(service, &stbuf) != 0) { + free(service); + service = xstrdup(lnk); + } + free(save); + free(saveLnk); + } + free(lnk); + 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 */ + xasprintf(&pidstr, "%d", (int) getpid()); + setenv("RC_OPENRC_PID", pidstr, 1); + /* + * RC_RUNSCRIPT_PID is deprecated, but we will keep it for a while + * for safety. + */ + 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); + memcpy(prefix, applet, ll); + memset(prefix + ll, ' ', l - ll); + memset(prefix + l, 0, 1); + eprefix(prefix); + } + + /* Ok, we are ready to go, so setup selinux if applicable */ + selinux_setup(argv); + + 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 (rc_yesno(getenv("RC_NODEPS"))) + deps = false; + + /* 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_DRYRUN"))) + dry_run = true; + 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); + + if (runscript) + ewarn("%s uses runscript, please convert to openrc-run.", service); + + /* 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, "iwant") == 0 || + strcmp(optarg, "needsme") == 0 || + strcmp(optarg, "usesme") == 0 || + strcmp(optarg, "wantsme") == 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); + tmplist = NULL; + 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; +} diff --git a/src/openrc-shutdown/broadcast.c b/src/openrc-shutdown/broadcast.c new file mode 100644 index 00000000..402a9fb9 --- /dev/null +++ b/src/openrc-shutdown/broadcast.c @@ -0,0 +1,211 @@ +/* + * broadcast.c + * broadcast a message to every logged in user + */ + +/* + * Copyright 2018 Sony Interactive Entertainment Inc. + * + * This file is part of OpenRC. It is subject to the license terms in + * the LICENSE file found in the top-level directory of this + * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE + * This file may not be copied, modified, propagated, or distributed + * except according to the terms contained in the LICENSE file. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "broadcast.h" +#include "helpers.h" + +#ifndef _PATH_DEV +# define _PATH_DEV "/dev/" +#endif + +static sigjmp_buf jbuf; + +/* + * Alarm handler + */ +/*ARGSUSED*/ +# ifdef __GNUC__ +static void handler(int arg __attribute__((unused))) +# else +static void handler(int arg) +# endif +{ + siglongjmp(jbuf, 1); +} + +static void getuidtty(char **userp, char **ttyp) +{ + struct passwd *pwd; + uid_t uid; + char *tty; + static char uidbuf[32]; + char *ttynm = NULL; + + uid = getuid(); + if ((pwd = getpwuid(uid)) != NULL) { + uidbuf[0] = 0; + strncat(uidbuf, pwd->pw_name, sizeof(uidbuf) - 1); + } else { + if (uid) + sprintf(uidbuf, "uid %d", (int) uid); + else + sprintf(uidbuf, "root"); + } + + if ((tty = ttyname(0)) != NULL) { + const size_t plen = strlen(_PATH_DEV); + if (strncmp(tty, _PATH_DEV, plen) == 0) { + tty += plen; + if (tty[0] == '/') + tty++; + } + xasprintf(&ttynm, "(%s) ", tty); + } + + *userp = uidbuf; + *ttyp = ttynm; +} + +/* + * Check whether the given filename looks like a tty device. + */ +static int file_isatty(const char *fname) +{ + struct stat st; + int major; + + if (stat(fname, &st) < 0) + return 0; + + if (st.st_nlink != 1 || !S_ISCHR(st.st_mode)) + return 0; + + /* + * It would be an impossible task to list all major/minors + * of tty devices here, so we just exclude the obvious + * majors of which just opening has side-effects: + * printers and tapes. + */ + major = major(st.st_dev); + if (major == 1 || major == 2 || major == 6 || major == 9 || + major == 12 || major == 16 || major == 21 || major == 27 || + major == 37 || major == 96 || major == 97 || major == 206 || + major == 230) + return 0; + return 1; +} + +/* + * broadcast function. + * + * NB: Not multithread safe. + */ +void broadcast(char *text) +{ + char *tty; + char *user; + struct utsname name; + time_t t; + char *date; + char *p; + char *line = NULL; + struct sigaction sa; + int flags; + char *term = NULL; + struct utmpx *utmp; + /* + * These are set across the sigsetjmp call, so they can't be stored on + * the stack, otherwise they might be clobbered. + */ + static int fd; + static FILE *tp; + + getuidtty(&user, &tty); + + /* + * Get and report current hostname, to make it easier to find out + * which machine is being shut down. + */ + uname(&name); + + /* Get the time */ + time(&t); + date = ctime(&t); + p = strchr(date, '\n'); + if (p) + *p = 0; + + xasprintf(&line, "\007\r\nBroadcast message from %s@%s %s(%s):\r\n\r\n", + user, name.nodename, tty, date); + free(tty); + + /* + * Fork to avoid hanging in a write() + */ + if (fork() != 0) + return; + + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = handler; + sigemptyset(&sa.sa_mask); + sigaction(SIGALRM, &sa, NULL); + + setutxent(); + + while ((utmp = getutxent()) != NULL) { + if (utmp->ut_type != USER_PROCESS || utmp->ut_user[0] == 0) + continue; + if (strncmp(utmp->ut_line, _PATH_DEV, strlen(_PATH_DEV)) == 0) + xasprintf(&term, "%s", utmp->ut_line); + else + xasprintf(&term, "%s%s", _PATH_DEV, utmp->ut_line); + if (strstr(term, "/../")) { + free(term); + continue; + } + + /* + * Open it non-delay + */ + if (sigsetjmp(jbuf, 1) == 0) { + alarm(2); + flags = O_WRONLY|O_NDELAY|O_NOCTTY; + if (file_isatty(term) && (fd = open(term, flags)) >= 0) { + if (isatty(fd) && (tp = fdopen(fd, "w")) != NULL) { + fputs(line, tp); + fputs(text, tp); + fflush(tp); + } + } + } + alarm(0); + if (fd >= 0) + close(fd); + if (tp != NULL) + fclose(tp); + free(term); + } + endutxent(); + free(line); + exit(0); +} diff --git a/src/openrc-shutdown/broadcast.h b/src/openrc-shutdown/broadcast.h new file mode 100644 index 00000000..2255fe67 --- /dev/null +++ b/src/openrc-shutdown/broadcast.h @@ -0,0 +1,16 @@ +/* + * Copyright 2018 Sony Interactive Entertainment Inc. + * + * This file is part of OpenRC. It is subject to the license terms in + * the LICENSE file found in the top-level directory of this + * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE + * This file may not be copied, modified, propagated, or distributed + * except according to the terms contained in the LICENSE file. + */ + +#ifndef BROADCAST_H +#define BROADCAST_H + +void broadcast(char *text); + +#endif diff --git a/src/openrc-shutdown/meson.build b/src/openrc-shutdown/meson.build new file mode 100644 index 00000000..bdd58338 --- /dev/null +++ b/src/openrc-shutdown/meson.build @@ -0,0 +1,10 @@ +if os == 'Linux' + executable('openrc-shutdown', + ['openrc-shutdown.c', 'broadcast.c', 'rc-sysvinit.c', misc_c, + usage_c, wtmp_c, version_h], + c_args : cc_branding_flags, + include_directories: [incdir, einfo_incdir, rc_incdir], + link_with: [libeinfo, librc], + install: true, + install_dir: sbindir) +endif diff --git a/src/openrc-shutdown/openrc-shutdown.c b/src/openrc-shutdown/openrc-shutdown.c new file mode 100644 index 00000000..1234dcfc --- /dev/null +++ b/src/openrc-shutdown/openrc-shutdown.c @@ -0,0 +1,358 @@ +/* + * openrc-shutdown.c + * If you are using OpenRC's provided init, this will shut down or + * reboot your system. + * + * This is based on code written by James Hammons , so + * I would like to publically thank him for his work. + */ + +/* + * Copyright 2017 The OpenRC Authors. + * See the Authors file at the top-level directory of this distribution and + * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS + * + * This file is part of OpenRC. It is subject to the license terms in + * the LICENSE file found in the top-level directory of this + * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE + * This file may not be copied, modified, propagated, or distributed + * except according to the terms contained in the LICENSE file. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "broadcast.h" +#include "einfo.h" +#include "rc.h" +#include "helpers.h" +#include "misc.h" +#include "rc-sysvinit.h" +#include "wtmp.h" +#include "_usage.h" + +const char *applet = NULL; +const char *extraopts = NULL; +const char getoptstring[] = "cdDfFHKpRrsw" getoptstring_COMMON; +const struct option longopts[] = { + { "cancel", no_argument, NULL, 'c'}, + { "no-write", no_argument, NULL, 'd'}, + { "dry-run", no_argument, NULL, 'D'}, + { "halt", no_argument, NULL, 'H'}, + { "kexec", no_argument, NULL, 'K'}, + { "poweroff", no_argument, NULL, 'p'}, + { "reexec", no_argument, NULL, 'R'}, + { "reboot", no_argument, NULL, 'r'}, + { "single", no_argument, NULL, 's'}, + { "write-only", no_argument, NULL, 'w'}, + longopts_COMMON +}; +const char * const longopts_help[] = { + "cancel a pending shutdown", + "do not write wtmp record", + "print actions instead of executing them", + "halt the system", + "reboot the system using kexec", + "power off the system", + "re-execute init (use after upgrading)", + "reboot the system", + "single user mode", + "write wtmp boot record and exit", + longopts_help_COMMON +}; +const char *usagestring = "" \ + "Usage: openrc-shutdown -c | --cancel\n" \ + " or: openrc-shutdown -R | --reexec\n" \ + " or: openrc-shutdown -w | --write-only\n" \ + " or: openrc-shutdown -H | --halt time\n" \ + " or: openrc-shutdown -K | --kexec time\n" \ + " or: openrc-shutdown -p | --poweroff time\n" \ + " or: openrc-shutdown -r | --reboot time\n" \ + " or: openrc-shutdown -s | --single time"; +const char *exclusive = "Select one of " + "--cancel, --halt, --kexec, --poweroff, --reexec, --reboot, --single or \n" + "--write-only"; +const char *nologin_file = RC_SYSCONFDIR"/nologin"; +const char *shutdown_pid = "/run/openrc-shutdown.pid"; + +static bool do_cancel = false; +static bool do_dryrun = false; +static bool do_halt = false; +static bool do_kexec = false; +static bool do_poweroff = false; +static bool do_reboot = false; +static bool do_reexec = false; +static bool do_single = false; +static bool do_wtmp = true; +static bool do_wtmp_only = false; + +static void cancel_shutdown(void) +{ + pid_t pid; + + pid = get_pid(applet, shutdown_pid); + if (pid <= 0) + eerrorx("%s: Unable to cancel shutdown", applet); + + if (kill(pid, SIGTERM) != -1) + einfo("%s: shutdown canceled", applet); + else + eerrorx("%s: Unable to cancel shutdown", applet); +} + +/* + * Create the nologin file. + */ +static void create_nologin(int mins) +{ + FILE *fp; + time_t t; + + time(&t); + t += 60 * mins; + + if ((fp = fopen(nologin_file, "w")) != NULL) { + fprintf(fp, "\rThe system is going down on %s\r\n", ctime(&t)); + fclose(fp); + } +} + +/* + * Send a command to our init + */ +static void send_cmd(const char *cmd) +{ + FILE *fifo; + size_t ignored; + + if (do_dryrun) { + einfo("Would send %s to init", cmd); + return; + } + if (do_wtmp && (do_halt || do_kexec || do_reboot || do_poweroff)) + log_wtmp("shutdown", "~~", 0, RUN_LVL, "~~"); + fifo = fopen(RC_INIT_FIFO, "w"); + if (!fifo) { + perror("fopen"); + return; + } + + ignored = fwrite(cmd, 1, strlen(cmd), fifo); + if (ignored != strlen(cmd)) + printf("Error writing to init fifo\n"); + fclose(fifo); +} + +/* + * sleep without being interrupted. + * The idea for this code came from sysvinit. + */ +static void sleep_no_interrupt(int seconds) +{ + struct timespec duration; + struct timespec remaining; + + duration.tv_sec = seconds; + duration.tv_nsec = 0; + + while (nanosleep(&duration, &remaining) < 0 && errno == EINTR) + duration = remaining; +} + +static void stop_shutdown(int sig) +{ + (void) sig; + unlink(nologin_file); + unlink(shutdown_pid); +einfo("Shutdown canceled"); +exit(0); +} + +int main(int argc, char **argv) +{ + char *ch = NULL; + int opt; + int cmd_count = 0; + int hour = 0; + int min = 0; + int shutdown_delay = 0; + struct sigaction sa; + struct tm *lt; + time_t tv; + bool need_warning = false; + char *msg = NULL; + char *state = NULL; + char *time_arg = NULL; + FILE *fp; + + applet = basename_c(argv[0]); + while ((opt = getopt_long(argc, argv, getoptstring, + longopts, (int *) 0)) != -1) + { + switch (opt) { + case 'c': + do_cancel = true; + cmd_count++; + break; + case 'd': + do_wtmp = false; + break; + case 'D': + do_dryrun = true; + break; + case 'H': + do_halt = true; + xasprintf(&state, "%s", "halt"); + cmd_count++; + break; + case 'K': + do_kexec = true; + xasprintf(&state, "%s", "reboot"); + cmd_count++; + break; + case 'p': + do_poweroff = true; + xasprintf(&state, "%s", "power off"); + cmd_count++; + break; + case 'R': + do_reexec = true; + cmd_count++; + break; + case 'r': + do_reboot = true; + xasprintf(&state, "%s", "reboot"); + cmd_count++; + break; + case 's': + do_single = true; + xasprintf(&state, "%s", "go down for maintenance"); + cmd_count++; + break; + case 'w': + do_wtmp_only = true; + cmd_count++; + break; + case_RC_COMMON_GETOPT + } + } + if (geteuid() != 0) + eerrorx("%s: you must be root\n", applet); + if (cmd_count != 1) { + eerror("%s: %s\n", applet, exclusive); + usage(EXIT_FAILURE); + } + + if (do_cancel) { + cancel_shutdown(); + exit(EXIT_SUCCESS); + } else if (do_reexec) { + send_cmd("reexec"); + exit(EXIT_SUCCESS); + } else if (do_wtmp_only) { + log_wtmp("shutdown", "~~", 0, RUN_LVL, "~~"); + exit(EXIT_SUCCESS); + } + + if (optind >= argc) { + eerror("%s: No shutdown time specified", applet); + usage(EXIT_FAILURE); + } + time_arg = argv[optind]; + if (*time_arg == '+') + time_arg++; + if (strcasecmp(time_arg, "now") == 0) + strcpy(time_arg, "0"); + for (ch=time_arg; *ch; ch++) + if ((*ch < '0' || *ch > '9') && *ch != ':') { + eerror("%s: invalid time %s", applet, time_arg); + usage(EXIT_FAILURE); + } + if (strchr(time_arg, ':')) { + if ((sscanf(time_arg, "%2d:%2d", &hour, &min) != 2) || + (hour > 23) || (min > 59)) { + eerror("%s: invalid time %s", applet, time_arg); + usage(EXIT_FAILURE); + } + time(&tv); + lt = localtime(&tv); + shutdown_delay = (hour * 60 + min) - (lt->tm_hour * 60 + lt->tm_min); + if (shutdown_delay < 0) + shutdown_delay += 1440; + } else { + shutdown_delay = atoi(time_arg); + } + + fp = fopen(shutdown_pid, "w"); + if (!fp) + eerrorx("%s: fopen `%s': %s", applet, shutdown_pid, strerror(errno)); + fprintf(fp, "%d\n", getpid()); + fclose(fp); + + openlog(applet, LOG_PID, LOG_DAEMON); + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = stop_shutdown; + sigemptyset(&sa.sa_mask); + sigaction(SIGINT, &sa, NULL); + sigaction(SIGTERM, &sa, NULL); + while (shutdown_delay > 0) { + need_warning = false; + if (shutdown_delay > 180) + need_warning = (shutdown_delay % 60 == 0); + else if (shutdown_delay > 60) + need_warning = (shutdown_delay % 30 == 0); + else if (shutdown_delay > 10) + need_warning = (shutdown_delay % 15 == 0); + else + need_warning = true; + if (shutdown_delay <= 5) + create_nologin(shutdown_delay); + if (need_warning) { + xasprintf(&msg, "\rThe system will %s in %d minutes\r\n", + state, shutdown_delay); + broadcast(msg); + free(msg); + } + sleep_no_interrupt(60); + shutdown_delay--; + } + xasprintf(&msg, "\rThe system will %s now\r\n", state); + broadcast(msg); + syslog(LOG_NOTICE, "The system will %s now", state); + unlink(nologin_file); + unlink(shutdown_pid); + if (do_halt) { + if (exists("/run/initctl")) { + sysvinit_setenv("INIT_HALT", "HALT"); + sysvinit_runlevel('0'); + } else + send_cmd("halt"); + } else if (do_kexec) + send_cmd("kexec"); + else if (do_poweroff) { + if (exists("/run/initctl")) { + sysvinit_setenv("INIT_HALT", "POWEROFF"); + sysvinit_runlevel('0'); + } else + send_cmd("poweroff"); + } else if (do_reboot) { + if (exists("/run/initctl")) + sysvinit_runlevel('6'); + else + send_cmd("reboot"); + } else if (do_single) { + if (exists("/run/initctl")) + sysvinit_runlevel('S'); + else + send_cmd("single"); + } + return 0; +} diff --git a/src/openrc-shutdown/rc-sysvinit.c b/src/openrc-shutdown/rc-sysvinit.c new file mode 100644 index 00000000..8d258b63 --- /dev/null +++ b/src/openrc-shutdown/rc-sysvinit.c @@ -0,0 +1,102 @@ +/* + * rc-sysvinit.c + * Helper to send a runlevel change to sysvinit + */ + +/* + * Copyright (c) 2019 The OpenRC Authors. + * See the Authors file at the top-level directory of this distribution and + * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS + * + * This file is part of OpenRC. It is subject to the license terms in + * the LICENSE file found in the top-level directory of this + * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE + * This file may not be copied, modified, propagated, or distributed + * except according to the terms contained in the LICENSE file. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "einfo.h" +#include "rc-sysvinit.h" + +static void sysvinit_send_cmd(struct init_request *request) +{ + int fd; + char *p; + size_t bytes; + ssize_t r; + + fd = open("/run/initctl", O_WRONLY|O_NONBLOCK|O_CLOEXEC|O_NOCTTY); + if (fd < 0) { + if (errno != ENOENT) + eerror("Failed to open initctl fifo: %s", strerror(errno)); + return; + } + p = (char *) request; + bytes = sizeof(*request); + do { + r = write(fd, p, bytes); + if (r < 0) { + if ((errno == EAGAIN) || (errno == EINTR)) + continue; + eerror("Failed to write to /run/initctl: %s", strerror(errno)); + return; + } + p += r; + bytes -= r; + } while (bytes > 0); +} + +void sysvinit_runlevel(char rl) +{ + struct init_request request; + + if (!rl) + return; + + request = (struct init_request) { + .magic = INIT_MAGIC, + .sleeptime = 0, + .cmd = INIT_CMD_RUNLVL, + .runlevel = rl, + }; + sysvinit_send_cmd(&request); + return; +} + +/* + * Set environment variables in the init process. + */ +void sysvinit_setenv(const char *name, const char *value) +{ + struct init_request request; + size_t nl; + size_t vl; + + memset(&request, 0, sizeof(request)); + request.magic = INIT_MAGIC; + request.cmd = INIT_CMD_SETENV; + nl = strlen(name); + if (value) + vl = strlen(value); +else + vl = 0; + + if (nl + vl + 3 >= (int)sizeof(request.i.data)) + return; + + memcpy(request.i.data, name, nl); + if (value) { + request.i.data[nl] = '='; + memcpy(request.i.data + nl + 1, value, vl); + } + sysvinit_send_cmd(&request); + return; +} diff --git a/src/openrc-shutdown/rc-sysvinit.h b/src/openrc-shutdown/rc-sysvinit.h new file mode 100644 index 00000000..6142cdd6 --- /dev/null +++ b/src/openrc-shutdown/rc-sysvinit.h @@ -0,0 +1,72 @@ +/* + * rc-sysvinit.h - Interface to communicate with sysvinit via /run/initctl. + */ + +/* + * Copyright (c) 2019 The OpenRC Authors. + * See the Authors file at the top-level directory of this distribution and + * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS + * + * This file is part of OpenRC. It is subject to the license terms in + * the LICENSE file found in the top-level directory of this + * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE + * This file may not be copied, modified, propagated, or distributed + * except according to the terms contained in the LICENSE file. + */ + +#ifndef _RC_SYSVINIT_H +#define _RC_SYSVINIT_H + +/* + * The #defines and structures below are taken from initreq.h in + * sysvinit and must be used by any program wishing to communicate with + * it. + */ + +#define INIT_MAGIC 0x03091969 +#define INIT_CMD_START 0 +#define INIT_CMD_RUNLVL 1 +#define INIT_CMD_POWERFAIL 2 +#define INIT_CMD_POWERFAILNOW 3 +#define INIT_CMD_POWEROK 4 +#define INIT_CMD_BSD 5 +#define INIT_CMD_SETENV 6 +#define INIT_CMD_UNSETENV 7 + +/* + * This is what BSD 4.4 uses when talking to init. + * Linux doesn't use this right now. + */ +struct init_request_bsd { + char gen_id[8]; /* Beats me.. telnetd uses "fe" */ + char tty_id[16]; /* Tty name minus /dev/tty */ + char host[64]; /* Hostname */ + char term_type[16]; /* Terminal type */ + int signal; /* Signal to send */ + int pid; /* Process to send to */ + char exec_name[128]; /* Program to execute */ + char reserved[128]; /* For future expansion. */ +}; + +/* + * Because of legacy interfaces, "runlevel" and "sleeptime" + * aren't in a seperate struct in the union. + * + * The weird sizes are because init expects the whole + * struct to be 384 bytes. + */ +struct init_request { + int magic; /* Magic number */ + int cmd; /* What kind of request */ + int runlevel; /* Runlevel to change to */ + int sleeptime; /* Time between TERM and KILL */ + union { + struct init_request_bsd bsd; + char data[368]; + } i; +}; + +void sysvinit_runlevel(char rl); +void sysvinit_setenv(const char *name, const char *value); + +#endif diff --git a/src/openrc/meson.build b/src/openrc/meson.build new file mode 100644 index 00000000..9ab32ef4 --- /dev/null +++ b/src/openrc/meson.build @@ -0,0 +1,17 @@ +executable('openrc', + ['rc.c', 'rc-logger.c', misc_c, plugin_c, usage_c, version_h], + c_args : cc_branding_flags, + link_with: [libeinfo, librc], + dependencies: [dl_dep, util_dep], + include_directories: [incdir, einfo_incdir, rc_incdir], + install: true, + install_dir: sbindir) + +executable('rc', + ['rc.c', 'rc-logger.c', misc_c, plugin_c, usage_c, version_h], + c_args : cc_branding_flags, + link_with: [libeinfo, librc], + dependencies: [dl_dep, util_dep], + include_directories: [incdir, einfo_incdir, rc_incdir], + install: true, + install_dir: sbindir) diff --git a/src/openrc/rc-logger.c b/src/openrc/rc-logger.c new file mode 100644 index 00000000..b00550a7 --- /dev/null +++ b/src/openrc/rc-logger.c @@ -0,0 +1,314 @@ +/* + * 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-2015 The OpenRC Authors. + * See the Authors file at the top-level directory of this distribution and + * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS + * + * This file is part of OpenRC. It is subject to the license terms in + * the LICENSE file found in the top-level directory of this + * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE + * This file may not be copied, modified, propagated, or distributed + * except according to the terms contained in the LICENSE file. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(__linux__) || (defined(__FreeBSD_kernel__) && defined(__GLIBC__)) \ + || defined(__GNU__) +# include +#elif defined(__NetBSD__) || defined(__OpenBSD__) +# include +#else +# include +#endif + +#include "einfo.h" +#include "rc-logger.h" +#include "queue.h" +#include "rc.h" +#include "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 (!isprint((int) *p) && *p != '\n') + goto cont; + 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 (!strcmp(logfile, TMPLOG)) { + eerror("Cowardly refusing to concatenate a logfile into itself."); + eerrorx("Please change rc_log_path to something other than %s to get rid of this message", TMPLOG); + } + + 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; + } + } + fclose(log); + } else { + log_error = 1; + eerror("Error: fopen(%s) failed: %s", TMPLOG, strerror(errno)); + } + + 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/openrc/rc-logger.h b/src/openrc/rc-logger.h new file mode 100644 index 00000000..52ca9717 --- /dev/null +++ b/src/openrc/rc-logger.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2007-2015 The OpenRC Authors. + * See the Authors file at the top-level directory of this distribution and + * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS + * + * This file is part of OpenRC. It is subject to the license terms in + * the LICENSE file found in the top-level directory of this + * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE + * This file may not be copied, modified, propagated, or distributed + * except according to the terms contained in the LICENSE file. + */ + +#ifndef RC_LOGGER_H +#define RC_LOGGER_H + +extern pid_t rc_logger_pid; +extern 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/openrc/rc.c b/src/openrc/rc.c new file mode 100644 index 00000000..bab731ed --- /dev/null +++ b/src/openrc/rc.c @@ -0,0 +1,1118 @@ +/* + * 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-2015 The OpenRC Authors. + * See the Authors file at the top-level directory of this distribution and + * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS + * + * This file is part of OpenRC. It is subject to the license terms in + * the LICENSE file found in the top-level directory of this + * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE + * This file may not be copied, modified, propagated, or distributed + * except according to the terms contained in the LICENSE file. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "einfo.h" +#include "queue.h" +#include "rc.h" +#include "rc-logger.h" +#include "misc.h" +#include "plugin.h" + +#include "version.h" +#include "_usage.h" + +const char *extraopts = NULL; +const char getoptstring[] = "a:no:s:S" getoptstring_COMMON; +const struct option longopts[] = { + { "no-stop", 0, NULL, 'n' }, + { "override", 1, NULL, 'o' }, + { "service", 1, NULL, 's' }, + { "sys", 0, NULL, 'S' }, + longopts_COMMON +}; +const char * const longopts_help[] = { + "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 +}; +const char *usagestring = "" \ + "Usage: openrc [options] []"; + +#define INITSH RC_LIBEXECDIR "/sh/init.sh" +#define INITEARLYSH RC_LIBEXECDIR "/sh/init-early.sh" + +#define INTERACTIVE RC_SVCDIR "/interactive" + +#define DEVBOOT "/dev/.rcboot" + +const char *applet = NULL; +static RC_STRINGLIST *main_hotplugged_services; +static RC_STRINGLIST *main_stop_services; +static RC_STRINGLIST *main_start_services; +static RC_STRINGLIST *main_types_nw; +static RC_STRINGLIST *main_types_nwua; +static RC_DEPTREE *main_deptree; +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; + 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; + + xasprintf(&path, RC_SVCDIR "/failed/%s", d->d_name); + if (unlink(path)) + eerror("%s: unlink `%s': %s", + applet, path, strerror(errno)); + free(path); + } + closedir(dp); + } +} + +static void +cleanup(void) +{ + RC_PID *p1 = LIST_FIRST(&service_pids); + RC_PID *p2; + + 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(); + } + + while (p1) { + p2 = LIST_NEXT(p1, entries); + free(p1); + p1 = p2; + } + + rc_stringlist_free(main_hotplugged_services); + rc_stringlist_free(main_stop_services); + rc_stringlist_free(main_start_services); + rc_stringlist_free(main_types_nw); + rc_stringlist_free(main_types_nwua); + rc_deptree_free(main_deptree); + free(runlevel); +} + +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 systems cannot really drop to shells */ + if (sys && strcmp(sys, RC_SYS_VSERVER) == 0) + { + execlp("halt", "halt", "-f", (char *) NULL); + eerrorx("%s: unable to exec `halt -f': %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 char *get_krunlevel(void) +{ + char *buffer = NULL; + FILE *fp; + size_t i = 0; + + if (!exists(RC_KRUNLEVEL)) + return NULL; + if (!(fp = fopen(RC_KRUNLEVEL, "r"))) { + eerror("fopen `%s': %s", RC_KRUNLEVEL, strerror(errno)); + return NULL; + } + + if (getline(&buffer, &i, fp) != -1) { + i = strlen(buffer); + if (buffer[i - 1] == '\n') + buffer[i - 1] = 0; + } + fclose(fp); + return buffer; +} + +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 = NULL; + 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) + xasprintf(&signame, "SIGINT"); + /* FALLTHROUGH */ + case SIGTERM: + if (!signame) + xasprintf(&signame, "SIGTERM"); + /* FALLTHROUGH */ + case SIGQUIT: + if (!signame) + xasprintf(&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); + /* force an update of the dependency tree */ + if ((main_deptree = _rc_deptree_load(1, NULL)) == NULL) + eerrorx("failed to load deptree"); +} + +static bool +runlevel_config(const char *service, const char *level) +{ + char *init = rc_service_resolve(service); + char *conf, *dir; + bool retval; + + dir = dirname(init); + dir = dirname(init); + xasprintf(&conf, "%s/conf.d/%s.%s", dir, service, level); + retval = exists(conf); + free(conf); + free(init); + return retval; +} + +static void +do_stop_services(RC_STRINGLIST *types_nw, 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_nw) { + types_nw = rc_stringlist_new(); + rc_stringlist_add(types_nw, "needsme"); + rc_stringlist_add(types_nw, "wantsme"); + } + + 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_nw, + 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) { + 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); + if (pid == -1) + break; + /* 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 + +int main(int argc, char **argv) +{ + const char *bootlevel = NULL; + char *newlevel = NULL; + const char *systype = NULL; + RC_STRINGLIST *deporder = NULL; + RC_STRINGLIST *tmplist; + RC_STRING *service; + bool going_down = false; + int depoptions = RC_DEP_STRICT | RC_DEP_TRACE; + char *krunlevel = NULL; + char *pidstr = NULL; + 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"); + + 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 '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': + systype = rc_sys(); + if (systype) + printf("%s\n", systype); + exit(EXIT_SUCCESS); + /* NOTREACHED */ + case_RC_COMMON_GETOPT + } + } + + if (strcmp(applet, "rc") == 0) + ewarn("rc is deprecated, please use openrc instead."); + 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 */ + xasprintf(&pidstr, "%d", getpid()); + setenv("RC_PID", pidstr, 1); + free(pidstr); + + /* 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)) + { + krunlevel = get_krunlevel(); + if (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 ((main_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. + */ + main_stop_services = rc_services_in_state(RC_SERVICE_STARTED); + tmplist = rc_services_in_state(RC_SERVICE_INACTIVE); + TAILQ_CONCAT(main_stop_services, tmplist, entries); + free(tmplist); + tmplist = rc_services_in_state(RC_SERVICE_STARTING); + TAILQ_CONCAT(main_stop_services, tmplist, entries); + free(tmplist); + if (main_stop_services) + rc_stringlist_sort(&main_stop_services); + + main_types_nwua = rc_stringlist_new(); + rc_stringlist_add(main_types_nwua, "ineed"); + rc_stringlist_add(main_types_nwua, "iwant"); + rc_stringlist_add(main_types_nwua, "iuse"); + rc_stringlist_add(main_types_nwua, "iafter"); + + if (main_stop_services) { + tmplist = rc_deptree_depends(main_deptree, main_types_nwua, main_stop_services, + runlevel, depoptions | RC_DEP_STOP); + rc_stringlist_free(main_stop_services); + main_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. + */ + main_hotplugged_services = rc_services_in_state(RC_SERVICE_HOTPLUGGED); + main_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(main_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(main_start_services, tmplist, entries); + free(tmplist); + } + if (main_hotplugged_services) { + TAILQ_FOREACH(service, main_hotplugged_services, + entries) + rc_stringlist_addu(main_start_services, + service->value); + } + } + } + + parallel = rc_conf_yesno("rc_parallel"); + + /* Now stop the services that shouldn't be running */ + if (main_stop_services && !nostop) + do_stop_services(main_types_nw, main_start_services, main_stop_services, main_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 (main_hotplugged_services) + TAILQ_FOREACH(service, main_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 (main_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(main_deptree, main_types_nwua, 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/poweroff/meson.build b/src/poweroff/meson.build new file mode 100644 index 00000000..c7da747f --- /dev/null +++ b/src/poweroff/meson.build @@ -0,0 +1,7 @@ +if os == 'Linux' and get_option('sysvinit') + configure_file(input : 'poweroff.in', + output : '@BASENAME@', + configuration : script_conf_data, + install_dir: sbindir, + install_mode: 'rwxr-xr-x') +endif diff --git a/src/poweroff/poweroff.in b/src/poweroff/poweroff.in new file mode 100644 index 00000000..4e944bdc --- /dev/null +++ b/src/poweroff/poweroff.in @@ -0,0 +1,28 @@ +#!/bin/sh + +option_arg= +poweroff_arg= +while getopts :nwdfiph opt; do + case "$opt" in + n) ;; + w) poweroff_arg=--write-only ;; + d) option_arg=--no-write ;; + f) ;; + i) ;; + [?]) printf "%s\n" "${0##*/}: invalid command line option" >&2 + exit 1 + ;; + esac +done +shift $((OPTIND-1)) + +if [ -z "${poweroff_arg}" ]; then + poweroff_arg=--poweroff +fi + +script_args="$@" +if [ -z "${script_args}" ]; then + script_args=now +fi + +exec @SBINDIR@/openrc-shutdown ${option_arg} ${poweroff_arg} "${script_args}" diff --git a/src/rc-abort/meson.build b/src/rc-abort/meson.build new file mode 100644 index 00000000..137cd028 --- /dev/null +++ b/src/rc-abort/meson.build @@ -0,0 +1,6 @@ +executable('rc-abort', + 'rc-abort.c', + include_directories: [einfo_incdir], + link_with: [libeinfo], + install: true, + install_dir: rc_sbindir) diff --git a/src/rc-abort/rc-abort.c b/src/rc-abort/rc-abort.c new file mode 100644 index 00000000..39a22c94 --- /dev/null +++ b/src/rc-abort/rc-abort.c @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2007-2015 The OpenRC Authors. + * See the Authors file at the top-level directory of this distribution and + * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS + * + * This file is part of OpenRC. It is subject to the license terms in + * the LICENSE file found in the top-level directory of this + * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE + * This file may not be copied, modified, propagated, or distributed + * except according to the terms contained in the LICENSE file. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "einfo.h" + +int main(void) +{ + 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; +} diff --git a/src/rc-depend/meson.build b/src/rc-depend/meson.build new file mode 100644 index 00000000..6835a36e --- /dev/null +++ b/src/rc-depend/meson.build @@ -0,0 +1,7 @@ +executable('rc-depend', + ['rc-depend.c', misc_c, usage_c, version_h], + c_args : cc_branding_flags, + include_directories: [incdir, einfo_incdir, rc_incdir], + link_with: [libeinfo, librc], + install: true, + install_dir: rc_bindir) diff --git a/src/rc-depend/rc-depend.c b/src/rc-depend/rc-depend.c new file mode 100644 index 00000000..9f833b70 --- /dev/null +++ b/src/rc-depend/rc-depend.c @@ -0,0 +1,177 @@ +/* + * rc-depend + * rc service dependency and ordering + */ + +/* + * Copyright (c) 2007-2015 The OpenRC Authors. + * See the Authors file at the top-level directory of this distribution and + * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS + * + * This file is part of OpenRC. It is subject to the license terms in + * the LICENSE file found in the top-level directory of this + * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE + * This file may not be copied, modified, propagated, or distributed + * except according to the terms contained in the LICENSE file. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "einfo.h" +#include "queue.h" +#include "rc.h" +#include "misc.h" +#include "_usage.h" + +const char *applet = NULL; +const char *extraopts = NULL; +const char getoptstring[] = "aot:suTF:" getoptstring_COMMON; +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 +}; +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 +}; +const char *usagestring = NULL; + +int main(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; + + applet = basename_c(argv[0]); + 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-service/meson.build b/src/rc-service/meson.build new file mode 100644 index 00000000..3a109d8e --- /dev/null +++ b/src/rc-service/meson.build @@ -0,0 +1,7 @@ +executable('rc-service', + ['rc-service.c', misc_c, usage_c, version_h], + c_args : cc_branding_flags, + link_with: [libeinfo, librc], + include_directories: [incdir, einfo_incdir, rc_incdir], + install: true, + install_dir: sbindir) diff --git a/src/rc-service/rc-service.c b/src/rc-service/rc-service.c new file mode 100644 index 00000000..69acac19 --- /dev/null +++ b/src/rc-service/rc-service.c @@ -0,0 +1,171 @@ +/* + * rc-service.c + * Finds all OpenRC services + */ + +/* + * Copyright (c) 2008-2015 The OpenRC Authors. + * See the Authors file at the top-level directory of this distribution and + * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS + * + * This file is part of OpenRC. It is subject to the license terms in + * the LICENSE file found in the top-level directory of this + * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE + * This file may not be copied, modified, propagated, or distributed + * except according to the terms contained in the LICENSE file. + */ + +#include +#include +#include +#include +#include + +#include "einfo.h" +#include "queue.h" +#include "rc.h" +#include "misc.h" +#include "_usage.h" + +const char *applet = NULL; +const char *extraopts = NULL; +const char getoptstring[] = "cdDe:ilr:INsSZ" getoptstring_COMMON; +const struct option longopts[] = { + { "debug", 0, NULL, 'd' }, + { "nodeps", 0, NULL, 'D' }, + { "exists", 1, NULL, 'e' }, + { "ifcrashed", 0, NULL, 'c' }, + { "ifexists", 0, NULL, 'i' }, + { "ifinactive", 0, NULL, 'I' }, + { "ifnotstarted", 0, NULL, 'N' }, + { "ifstarted", 0, NULL, 's' }, + { "ifstopped", 0, NULL, 'S' }, + { "list", 0, NULL, 'l' }, + { "resolve", 1, NULL, 'r' }, + { "dry-run", 0, NULL, 'Z' }, + longopts_COMMON +}; +const char * const longopts_help[] = { + "set xtrace when running the command", + "ignore dependencies", + "tests if the service exists or not", + "if the service is crashed run the command", + "if the service exists run the command", + "if the service is inactive run the command", + "if the service is not started run the command", + "if the service is started run the command", + "if the service is stopped run the command", + "list all available services", + "resolve the service name to an init script", + "dry run (show what would happen)", + longopts_help_COMMON +}; +const char *usagestring = "" \ + "Usage: rc-service [options] [-i] ...\n" \ + " or: rc-service [options] -e \n" \ + " or: rc-service [options] -l\n" \ + " or: rc-service [options] -r "; + +int main(int argc, char **argv) +{ + int opt; + char *service; + RC_STRINGLIST *list; + RC_STRING *s; + RC_SERVICE state; + bool if_crashed = false; + bool if_exists = false; + bool if_inactive = false; + bool if_notstarted = false; + bool if_started = false; + bool if_stopped = false; + + applet = basename_c(argv[0]); + /* 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 'd': + setenv("RC_DEBUG", "yes", 1); + break; + case 'D': + setenv("RC_NODEPS", "yes", 1); + break; + case 'e': + service = rc_service_resolve(optarg); + opt = service ? EXIT_SUCCESS : EXIT_FAILURE; + free(service); + return opt; + /* NOTREACHED */ + case 'c': + if_crashed = true; + break; + case 'i': + if_exists = true; + break; + case 'I': + if_inactive = true; + break; + case 'N': + if_notstarted = 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); + rc_stringlist_free(list); + return EXIT_SUCCESS; + /* NOTREACHED */ + case 'r': + service = rc_service_resolve(optarg); + if (service == NULL) + return EXIT_FAILURE; + printf("%s\n", service); + free(service); + return EXIT_SUCCESS; + /* NOTREACHED */ + case 's': + if_started = true; + break; + case 'S': + if_stopped = true; + break; + case 'Z': + setenv("IN_DRYRUN", "yes", 1); + break; + + 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); + } + state = rc_service_state(*argv); + if (if_crashed && !(rc_service_daemons_crashed(*argv) && errno != EACCES)) + return 0; + if (if_inactive && !(state & RC_SERVICE_INACTIVE)) + return 0; + if (if_notstarted && (state & RC_SERVICE_STARTED)) + return 0; + if (if_started && !(state & RC_SERVICE_STARTED)) + return 0; + if (if_stopped && !(state & RC_SERVICE_STOPPED)) + return 0; + *argv = service; + execv(*argv, argv); + eerrorx("%s: %s", applet, strerror(errno)); + /* NOTREACHED */ +} diff --git a/src/rc-sstat/meson.build b/src/rc-sstat/meson.build new file mode 100644 index 00000000..85b7a5f0 --- /dev/null +++ b/src/rc-sstat/meson.build @@ -0,0 +1,5 @@ +if os == 'Linux' + install_data('rc-sstat', + install_dir: sbindir, + install_mode: 'rwxr-xr-x') +endif diff --git a/src/rc-sstat/rc-sstat b/src/rc-sstat/rc-sstat new file mode 100644 index 00000000..4811293d --- /dev/null +++ b/src/rc-sstat/rc-sstat @@ -0,0 +1,149 @@ +#!/bin/sh +# Copyright (c) 2015 The OpenRC Authors. +# See the Authors file at the top-level directory of this distribution and +# https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS +# +# This file is part of OpenRC. It is subject to the license terms in +# the LICENSE file found in the top-level directory of this +# distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE +# This file may not be copied, modified, propagated, or distributed +# except according to the terms contained in the LICENSE file. + +# Define variables +scandir="/run/openrc/s6-scan" +statfile=/dev/shm/s6-svstat.${USER} + +color_red='\E[01;31m' +color_green='\E[32m' +color_yellow='\E[01;33m' + +# Time Modules +uptimeModules() { + # Given a single integer argument representing seconds of uptime... + # convert uptime to a friendly human readable string: '2d 16h 58m 46s' + # define a variable to keep track of the longest length uptime string + uSec=${1:-0} + + uDay=$(( $uSec / 86400 )) + uSec=$(( $uSec % 86400 )) + uHour=$(( $uSec / 3600 )) + uSec=$(( $uSec % 3600 )) + uMin=$(( $uSec / 60 )) + uSec=$(( $uSec % 60 )) + + [ $uDay -ne 0 ] && pDay="${uDay}d " || pDay="" + [ $uHour -ne 0 ] && pHour="${uHour}h " || pHour="" + [ $uMin -ne 0 ] && pMin="${uMin}m " || pMin="" + [ $uSec -ne 0 ] && pSec="${uSec}s " || pSec="" + + parsedUptime="$( echo ${pDay}${pHour}${pMin}${pSec} | sed 's#[ \t]*$##' )" + uCharCount=${#parsedUptime} +} + +# Make sure we are running as root +if [ $(id -u) != 0 ]; then + printf "This command must be run as root\n" + exit 1 +fi + +# Make sure scandir exists +if [ ! -d $scandir ]; then + printf "%s\n" "$scandir does not exist" + exit 1 +fi + +# Make sure s6-svscan is running +if ! pgrep s6-svscan >/dev/null ; then + printf "s6-svscan is not running\n" + exit 1 +fi + +# If TERM is undefined (launching sstat through an ssh command) then make it vt100 +if [ -z $TERM -o $TERM = "dumb" ]; then + export TERM=vt100 +fi + +# Gather list of candidate services s6-supervise may be supervising +# filter for folders and symlinks at /run/openrc/s6-scan/* ommiting output starting with '.' +services="$(find $scandir -maxdepth 1 -mindepth 1 \( -type d -or -type l \) | awk -F'/' '{ if ( $NF !~ "^\\." ) print $NF}')" +if [ -z "$services" ]; then + printf "s6 found no services configured for supervision\n" + exit 1 +fi + +# Gather status for each service from s6-svstat +# write to tmp file in memory for non I/O bound repeatative access +rm -f $statfile 2>/dev/null +for service in $services ; do + echo "$service $(s6-svstat ${scandir}/${service})" >> $statfile +done + +# Define longest string from parsed uptime (default to 7 to match string length of 'Up Time') +timeStringLength=7 +for uptime in $(awk '$2 == "up" {print $5}' $statfile | sort -run) +do + uptimeModules $uptime + [ ${uCharCount} -gt $timeStringLength ] && timeStringLength=$uCharCount +done + + +# Print the status header like so... +# Service Name State PID Up Time Start Time +#---------------------------- ----- ----- -------------- ------------------- +printf "\n" +printf "%28s %5s %5s %${timeStringLength}s %19s\n" "Service Name" "State" "PID" "Up Time" "Start Time" +for dashes in 28 5 5 $timeStringLength 19 ; do + printf "%0.s-" $(seq 1 $dashes) ; echo -n ' ' +done && printf "\n" + + +# sshd up (pid 26300) 80373 seconds +cat $statfile | \ +while read line +do + set $line + + service=$1 + state=$2 + pid=${4/)/} + time=$5 + + # call function to convert time in seconds and define additional variables + uptimeModules $time + + if [ "$state" = up ]; then + if [ $time -lt 30 ]; then + # uptime < 30 seconds, color the whole line yellow + echo -en "$color_yellow" + # 1st 4 columns are printed with printf for space padding + printf "%28s %5s %5s %${timeStringLength}s" $service $state $pid "$parsedUptime" + # 4th column is output from date -d + echo -e " $(date -d "${time} seconds ago" "+%F %T")" + # reset terminal colors + tput sgr0 + else + printf "%28s" $service + # uptime > 30 seconds, color just the "state" value green + echo -en "$color_green" + printf " %5s" $state + # reset terminal colors + tput sgr0 + printf " %5s" $pid + printf " %${timeStringLength}s" "$parsedUptime" + echo -e " $(date -d "${time} seconds ago" "+%F %T")" + fi + else + printf "%28s" $service + echo -en "$color_red" + printf " %5s" $state + tput sgr0 + echo "" + fi +done + +# Cleanup +rm -f $statfile 2>/dev/null + +printf "\n\n" + +rc-status diff --git a/src/rc-status/meson.build b/src/rc-status/meson.build new file mode 100644 index 00000000..7377d837 --- /dev/null +++ b/src/rc-status/meson.build @@ -0,0 +1,8 @@ +executable('rc-status', + ['rc-status.c', misc_c, usage_c, version_h], + c_args : cc_branding_flags, + link_with: [libeinfo, librc], + dependencies: [util_dep], + include_directories: [incdir, einfo_incdir, rc_incdir], + install: true, + install_dir: bindir) diff --git a/src/rc-status/rc-status.c b/src/rc-status/rc-status.c new file mode 100644 index 00000000..cb701e24 --- /dev/null +++ b/src/rc-status/rc-status.c @@ -0,0 +1,465 @@ +/* + * rc-status.c + * Display the status of the services in runlevels + */ + +/* + * Copyright (c) 2007-2015 The OpenRC Authors. + * See the Authors file at the top-level directory of this distribution and + * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS + * + * This file is part of OpenRC. It is subject to the license terms in + * the LICENSE file found in the top-level directory of this + * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE + * This file may not be copied, modified, propagated, or distributed + * except according to the terms contained in the LICENSE file. + */ + +#include +#include +#include +#include +#include +#include + +#include "einfo.h" +#include "queue.h" +#include "rc.h" +#include "misc.h" +#include "_usage.h" + +enum format_t { + FORMAT_DEFAULT, + FORMAT_INI, +}; + +const char *applet = NULL; +const char *extraopts = NULL; +const char getoptstring[] = "acf:lmrsSu" getoptstring_COMMON; +const struct option longopts[] = { + {"all", 0, NULL, 'a'}, + {"crashed", 0, NULL, 'c'}, + {"format", 1, NULL, 'f'}, + {"list", 0, NULL, 'l'}, + {"manual", 0, NULL, 'm'}, + {"runlevel", 0, NULL, 'r'}, + {"servicelist", 0, NULL, 's'}, + {"supervised", 0, NULL, 'S'}, + {"unused", 0, NULL, 'u'}, + longopts_COMMON +}; +const char * const longopts_help[] = { + "Show services from all run levels", + "Show crashed services", + "format status to be parsable (currently arg must be ini)", + "Show list of run levels", + "Show manually started services", + "Show the name of the current runlevel", + "Show service list", + "show supervised services", + "Show services not assigned to any runlevel", + longopts_help_COMMON +}; +const char *usagestring = "" \ + "Usage: rc-status [options] -f ini ...\n" \ + " or: rc-status [options] [-a | -c | -l | -m | -r | -s | -u]"; + +static RC_DEPTREE *deptree; +static RC_STRINGLIST *types; + +static RC_STRINGLIST *levels, *services, *tmp, *alist; +static RC_STRINGLIST *sservices, *nservices, *needsme; + +static void print_level(const char *prefix, const char *level, + enum format_t format) +{ + switch (format) { + case FORMAT_DEFAULT: + 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); + break; + case FORMAT_INI: + printf("%s", "["); + if (prefix) + printf("%s ", prefix); + printf("%s]\n", level); + break; + } +} + +static char *get_uptime(const char *service) +{ + RC_SERVICE state = rc_service_state(service); + char *start_count; + char *start_time_string; + time_t start_time; + int64_t diff_days; + int64_t diff_hours; + int64_t diff_mins; + int64_t diff_secs; + char *uptime = NULL; + + if (state & RC_SERVICE_STARTED) { + start_count = rc_service_value_get(service, "start_count"); + start_time_string = rc_service_value_get(service, "start_time"); + if (start_count && start_time_string) { + start_time = to_time_t(start_time_string); + diff_secs = (int64_t) difftime(time(NULL), start_time); + diff_days = diff_secs / 86400; + diff_secs = diff_secs % 86400; + diff_hours = diff_secs / 3600; + diff_secs = diff_secs % 3600; + diff_mins = diff_secs / 60; + diff_secs = diff_secs % 60; + if (diff_days > 0) + xasprintf(&uptime, + "%"PRId64" day(s) %02"PRId64":%02"PRId64":%02"PRId64" (%s)", + diff_days, diff_hours, diff_mins, diff_secs, + start_count); + else + xasprintf(&uptime, + "%02"PRId64":%02"PRId64":%02"PRId64" (%s)", + diff_hours, diff_mins, diff_secs, start_count); + } + } + return uptime; +} + +static void print_service(const char *service, enum format_t format) +{ + char *status = NULL; + char *uptime = NULL; + char *child_pid = NULL; + char *start_time = NULL; + int cols; + const char *c = ecolor(ECOLOR_GOOD); + RC_SERVICE state = rc_service_state(service); + ECOLOR color = ECOLOR_BAD; + + if (state & RC_SERVICE_STOPPING) + xasprintf(&status, "stopping "); + else if (state & RC_SERVICE_STARTING) { + xasprintf(&status, "starting "); + color = ECOLOR_WARN; + } else if (state & RC_SERVICE_INACTIVE) { + xasprintf(&status, "inactive "); + color = ECOLOR_WARN; + } else if (state & RC_SERVICE_STARTED) { + errno = 0; + if (rc_service_daemons_crashed(service) && errno != EACCES) + { + child_pid = rc_service_value_get(service, "child_pid"); + start_time = rc_service_value_get(service, "start_time"); + if (start_time && child_pid) + xasprintf(&status, " unsupervised "); + else + xasprintf(&status, " crashed "); + free(child_pid); + free(start_time); + } else { + uptime = get_uptime(service); + if (uptime) { + xasprintf(&status, " started %s", uptime); + free(uptime); + } else + xasprintf(&status, " started "); + color = ECOLOR_GOOD; + } + } else if (state & RC_SERVICE_SCHEDULED) { + xasprintf(&status, "scheduled"); + color = ECOLOR_WARN; + } else if (state & RC_SERVICE_FAILED) { + xasprintf(&status, "failed"); + color = ECOLOR_WARN; + } else + xasprintf(&status, " stopped "); + + errno = 0; + switch (format) { + case FORMAT_DEFAULT: + cols = printf(" %s", service); + if (c && *c && isatty(fileno(stdout))) + printf("\n"); + ebracket(cols, color, status); + break; + case FORMAT_INI: + printf("%s = %s\n", service, status); + break; + } + free(status); +} + +static void print_services(const char *runlevel, RC_STRINGLIST *svcs, + enum format_t format) +{ + 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, format); + 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, format); + } + rc_stringlist_free(l); +} + +static void print_stacked_services(const char *runlevel, enum format_t format) +{ + 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, format); + servicelist = rc_services_in_runlevel(stackedlevel->value); + print_services(stackedlevel->value, servicelist, format); + rc_stringlist_free(servicelist); + } + rc_stringlist_free(stackedlevels); + stackedlevels = NULL; +} + +int main(int argc, char **argv) +{ + RC_SERVICE state; + RC_STRING *s, *l, *t, *level; + enum format_t format = FORMAT_DEFAULT; + bool levels_given = false; + bool show_all = false; + char *p, *runlevel = NULL; + int opt, retval = 0; + + applet = basename_c(argv[0]); + while ((opt = getopt_long(argc, argv, getoptstring, longopts, + (int *) 0)) != -1) + switch (opt) { + case 'a': + show_all = true; + 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 'f': + if (strcasecmp(optarg, "ini") == 0) { + format = FORMAT_INI; + setenv("EINFO_QUIET", "YES", 1); + } else + eerrorx("%s: invalid argument to --format switch\n", applet); + break; + case 'l': + levels = rc_runlevel_list(); + TAILQ_FOREACH(l, levels, entries) + printf("%s\n", l->value); + goto exit; + case 'm': + 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; + } + } + TAILQ_FOREACH_SAFE(s, services, entries, t) + if (rc_service_state(s->value) & + (RC_SERVICE_STOPPED | RC_SERVICE_HOTPLUGGED)) { + TAILQ_REMOVE(services, s, entries); + free(s->value); + free(s); + } + print_services(NULL, services, FORMAT_DEFAULT); + goto exit; + case 'r': + runlevel = rc_runlevel_get(); + printf("%s\n", runlevel); + goto exit; + /* NOTREACHED */ + case 'S': + services = rc_services_in_state(RC_SERVICE_STARTED); + TAILQ_FOREACH_SAFE(s, services, entries, t) + if (!rc_service_value_get(s->value, "child_pid")) + TAILQ_REMOVE(services, s, entries); + print_services(NULL, services, FORMAT_DEFAULT); + goto exit; + /* NOTREACHED */ + case 's': + services = rc_services_in_runlevel(NULL); + print_services(NULL, services, FORMAT_DEFAULT); + 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, FORMAT_DEFAULT); + 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])) { + levels_given = true; + 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, format); + services = rc_services_in_runlevel(l->value); + print_services(l->value, services, format); + print_stacked_services(l->value, format); + rc_stringlist_free(nservices); + nservices = NULL; + rc_stringlist_free(services); + services = NULL; + } + + if (show_all || !levels_given) { + /* Show hotplugged services */ + print_level("Dynamic", "hotplugged", format); + services = rc_services_in_state(RC_SERVICE_HOTPLUGGED); + print_services(NULL, services, format); + rc_stringlist_free(services); + services = NULL; + + /* Show manually started and unassigned depended services */ + if (show_all) { + 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) { + state = rc_service_state(s->value); + if ((rc_stringlist_find(sservices, s->value) || + (state & ( RC_SERVICE_STOPPED | RC_SERVICE_HOTPLUGGED)))) { + if (!(state & RC_SERVICE_FAILED)) { + TAILQ_REMOVE(services, s, entries); + free(s->value); + free(s); + } + } + } + needsme = rc_stringlist_new(); + rc_stringlist_add(needsme, "needsme"); + rc_stringlist_add(needsme, "wantsme"); + 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/wanted", format); + print_services(NULL, nservices, format); + print_level("Dynamic", "manual", format); + print_services(NULL, services, format); + } + +exit: + free(runlevel); + 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); + + return retval; +} diff --git a/src/rc-update/meson.build b/src/rc-update/meson.build new file mode 100644 index 00000000..7d284979 --- /dev/null +++ b/src/rc-update/meson.build @@ -0,0 +1,7 @@ +executable('rc-update', + ['rc-update.c', misc_c, usage_c, version_h], + c_args : cc_branding_flags, + link_with: [libeinfo, librc], + include_directories: [incdir, einfo_incdir, rc_incdir], + install: true, + install_dir: sbindir) diff --git a/src/rc-update/rc-update.c b/src/rc-update/rc-update.c new file mode 100644 index 00000000..445db4e8 --- /dev/null +++ b/src/rc-update/rc-update.c @@ -0,0 +1,353 @@ +/* + * rc-update + * Manage init scripts and runlevels + */ + +/* + * Copyright (c) 2007-2015 The OpenRC Authors. + * See the Authors file at the top-level directory of this distribution and + * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS + * + * This file is part of OpenRC. It is subject to the license terms in + * the LICENSE file found in the top-level directory of this + * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE + * This file may not be copied, modified, propagated, or distributed + * except according to the terms contained in the LICENSE file. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "einfo.h" +#include "queue.h" +#include "rc.h" +#include "misc.h" +#include "_usage.h" + +const char *applet = NULL; +const char *extraopts = NULL; +const char *usagestring = "" \ + "Usage: rc-update [options] add [...]\n" \ + " or: rc-update [options] del [...]\n" \ + " or: rc-update [options] [show [...]]"; +const char getoptstring[] = "asu" getoptstring_COMMON; +const struct option longopts[] = { + { "all", 0, NULL, 'a' }, + { "stack", 0, NULL, 's' }, + { "update", 0, NULL, 'u' }, + longopts_COMMON +}; +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 +}; + +/* 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 executable", + 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 = NULL; + 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); + buffer = xmalloc(l+1); + memset (buffer, ' ', l); + buffer[l] = 0; + rc_stringlist_add (in, buffer); + free(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); +} + +#define DOADD (1 << 1) +#define DODELETE (1 << 2) +#define DOSHOW (1 << 3) + +int main(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; + + applet = basename_c(argv[0]); + 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/.gitignore b/src/rc/.gitignore deleted file mode 100644 index a43088aa..00000000 --- a/src/rc/.gitignore +++ /dev/null @@ -1,66 +0,0 @@ -version.h -rc-status -rc-service -rc-update -runscript -service -start-stop-daemon -supervise-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 -mark_service_crashed -rc-abort -rc -openrc -openrc-init -openrc-run -openrc-shutdown -kill_all diff --git a/src/rc/Makefile b/src/rc/Makefile deleted file mode 100644 index 306f6d8d..00000000 --- a/src/rc/Makefile +++ /dev/null @@ -1,186 +0,0 @@ -include ../../Makefile.inc -MK= ../../mk -include ${MK}/os.mk - -SRCS= checkpath.c do_e.c do_mark_service.c do_service.c \ - do_value.c fstabinfo.c is_newer_than.c is_older_than.c \ - mountinfo.c openrc-run.c rc-abort.c rc.c \ - rc-depend.c rc-logger.c rc-misc.c rc-pipes.c \ - rc-plugin.c rc-service.c rc-status.c rc-update.c \ - shell_var.c start-stop-daemon.c supervise-daemon.c swclock.c _usage.c - -ifeq (${MKSELINUX},yes) -SRCS+= rc-selinux.c -endif - -ifeq (${OS},Linux) -SRCS+= kill_all.c openrc-init.c openrc-shutdown.c rc-sysvinit.c broadcast.c \ - rc-wtmp.c seedrng.c -endif - -CLEANFILES= version.h rc-selinux.o - -BINDIR= ${PREFIX}/bin -SBINDIR= ${PREFIX}/sbin -LINKDIR= ${LIBEXECDIR} - -BINPROGS= rc-status -SBINPROGS = openrc openrc-run rc rc-service rc-update runscript \ - start-stop-daemon supervise-daemon -RC_BINPROGS= einfon einfo ewarnn ewarn eerrorn eerror ebegin eend ewend \ - eindent eoutdent esyslog eval_ecolors ewaitfile \ - veinfo vewarn vebegin veend vewend veindent veoutdent \ - checkpath fstabinfo mountinfo rc-depend \ - is_newer_than is_older_than \ - service_get_value service_set_value get_options save_options \ - service_starting service_started \ - service_stopping service_stopped \ - service_inactive service_wasinactive \ - service_hotplugged service_started_daemon service_crashed \ - shell_var -RC_SBINPROGS= mark_service_starting mark_service_started \ - mark_service_stopping mark_service_stopped \ - mark_service_inactive mark_service_wasinactive \ - mark_service_hotplugged mark_service_failed \ - mark_service_crashed \ - rc-abort swclock - -ifeq (${OS},Linux) -RC_BINPROGS+= kill_all -RC_SBINPROGS+= seedrng -SBINPROGS+= openrc-init openrc-shutdown -endif - -ALL_PROGS= ${BINPROGS} ${SBINPROGS} ${RC_BINPROGS} ${RC_SBINPROGS} -CLEANFILES+= ${ALL_PROGS} - -LOCAL_CPPFLAGS=-I../includes -I../librc -I../libeinfo -LOCAL_LDFLAGS=-L../librc -L../libeinfo -LDADD+= -lutil -lrc -leinfo - -include ${MK}/prog.mk -include ${MK}/gitver.mk -include ${MK}/cc.mk - -include ${MK}/termcap.mk -LDADD+= ${LIBDL} ${LIBKVM} -include ${MK}/pam.mk - -${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} ${SBINPROGS} ${DESTDIR}${SBINDIR} - ${INSTALL} -d ${DESTDIR}${BINDIR} - ${INSTALL} -m ${BINMODE} ${BINPROGS} ${DESTDIR}${BINDIR} - ${INSTALL} -d ${DESTDIR}${LINKDIR}/bin - ${INSTALL} -m ${BINMODE} ${RC_BINPROGS} ${DESTDIR}${LINKDIR}/bin - ${INSTALL} -d ${DESTDIR}${LINKDIR}/sbin - ${INSTALL} -m ${BINMODE} ${RC_SBINPROGS} ${DESTDIR}${LINKDIR}/sbin - if test "${MKPAM}" = pam; then \ - ${INSTALL} -d ${DESTDIR}${PAMDIR}; \ - ${INSTALL} -m ${PAMMODE} start-stop-daemon.pam ${DESTDIR}${PAMDIR}/start-stop-daemon; \ - ${INSTALL} -m ${PAMMODE} supervise-daemon.pam ${DESTDIR}${PAMDIR}/supervise-daemon; \ - fi - -check test:: - -all: ${ALL_PROGS} - -checkpath: checkpath.o _usage.o rc-misc.o -ifeq (${MKSELINUX},yes) -checkpath: rc-selinux.o -endif - ${CC} ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ $^ ${LDADD} - -kill_all: kill_all.o _usage.o - ${CC} ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ $^ ${LDADD} - -einfon einfo ewarnn ewarn eerrorn eerror ebegin eend ewend \ -eindent eoutdent esyslog eval_ecolors ewaitfile \ -veinfo vewarn vebegin veend vewend veindent veoutdent: do_e.o rc-misc.o - ${CC} ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ $^ ${LDADD} - -fstabinfo: fstabinfo.o _usage.o rc-misc.o - ${CC} ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ $^ ${LDADD} - -openrc-init: openrc-init.o rc-plugin.o rc-wtmp.o - ${CC} ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ $^ ${LDADD} - -is_newer_than: is_newer_than.o rc-misc.o - ${CC} ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ $^ ${LDADD} - -is_older_than: is_older_than.o rc-misc.o - ${CC} ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ $^ ${LDADD} - -mark_service_starting mark_service_started \ -mark_service_stopping mark_service_stopped \ -mark_service_inactive mark_service_wasinactive \ -mark_service_hotplugged mark_service_failed \ -mark_service_crashed: do_mark_service.o rc-misc.o - ${CC} ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ $^ ${LDADD} - -mountinfo: mountinfo.o _usage.o rc-misc.o - ${CC} ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ $^ ${LDADD} - -openrc rc: rc.o rc-logger.o rc-misc.o rc-plugin.o _usage.o - ${CC} ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ $^ ${LDADD} - -openrc-shutdown: openrc-shutdown.o rc-misc.o _usage.o broadcast.o rc-wtmp.o rc-sysvinit.o - ${CC} ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ $^ ${LDADD} - -openrc-run runscript: openrc-run.o _usage.o rc-misc.o rc-plugin.o -ifeq (${MKSELINUX},yes) -openrc-run runscript: rc-selinux.o -endif - ${CC} ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ $^ ${LDADD} - -rc-abort: rc-abort.o - ${CC} ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ $^ -leinfo - -rc-depend: rc-depend.o _usage.o rc-misc.o - ${CC} ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ $^ ${LDADD} - -rc-status: rc-status.o _usage.o rc-misc.o - ${CC} ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ $^ ${LDADD} - -rc-service: rc-service.o _usage.o rc-misc.o - ${CC} ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ $^ ${LDADD} - -rc-update: rc-update.o _usage.o rc-misc.o - ${CC} ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ $^ ${LDADD} - -start-stop-daemon: start-stop-daemon.o _usage.o rc-misc.o rc-pipes.o rc-schedules.o - ${CC} ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ $^ ${LDADD} - -supervise-daemon: supervise-daemon.o _usage.o rc-misc.o rc-plugin.o rc-schedules.o - ${CC} ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ $^ ${LDADD} - -service_get_value service_set_value get_options save_options: do_value.o rc-misc.o - ${CC} ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ $^ ${LDADD} - -service_starting service_started \ -service_stopping service_stopped \ -service_inactive service_wasinactive \ -service_hotplugged service_started_daemon \ -service_crashed: do_service.o rc-misc.o - ${CC} ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ $^ ${LDADD} - -shell_var: shell_var.o - ${CC} ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ $^ - -swclock: swclock.o _usage.o rc-misc.o - ${CC} ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ $^ ${LDADD} - -seedrng: seedrng.o _usage.o - ${CC} ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ $^ ${LDADD} diff --git a/src/rc/_usage.c b/src/rc/_usage.c deleted file mode 100644 index ed4c63e0..00000000 --- a/src/rc/_usage.c +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (c) 2007-2015 The OpenRC Authors. - * See the Authors file at the top-level directory of this distribution and - * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS - * - * This file is part of OpenRC. It is subject to the license terms in - * the LICENSE file found in the top-level directory of this - * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE - * This file may not be copied, modified, propagated, or distributed - * except according to the terms contained in the LICENSE file. - */ - -#include -#include -#include -#include "rc.h" -#include "rc-misc.h" -#include "_usage.h" -#include "version.h" - -#if lint -# define _noreturn -#endif -#if __GNUC__ > 2 || defined(__INTEL_COMPILER) -# define _noreturn __attribute__ ((__noreturn__)) -#else -# define _noreturn -#endif - -void set_quiet_options(void) -{ - static int qcount = 0; - - qcount ++; - switch (qcount) { - case 1: - setenv ("EINFO_QUIET", "YES", 1); - break; - case 2: - setenv ("EERROR_QUIET", "YES", 1); - break; - } -} - -_noreturn void show_version(void) -{ - const char *systype = NULL; - - printf("%s (OpenRC", applet); - if ((systype = rc_sys())) - printf(" [%s]", systype); - printf(") %s", VERSION); -#ifdef BRANDING - printf(" (%s)", BRANDING); -#endif - printf("\n"); - exit(EXIT_SUCCESS); -} - -_noreturn void usage(int exit_status) -{ - const char * const has_arg[] = { "", "", "[arg]" }; - int i; - int len; - char *lo; - char *p; - char *token; - char val[4] = "-?,"; - - if (usagestring) - printf("%s", usagestring); - else - printf("Usage: %s [options] ", applet); - - if (extraopts) - printf("%s", extraopts); - - printf("\n\nOptions: [ %s ]\n", getoptstring); - for (i = 0; longopts[i].name; ++i) { - val[1] = longopts[i].val; - len = printf(" %3s --%s %s", isprint(longopts[i].val) ? val : "", - longopts[i].name, has_arg[longopts[i].has_arg]); - - lo = p = xstrdup(longopts_help[i]); - while ((token = strsep(&p, "\n"))) { - len = 36 - len; - if (len > 0) - printf("%*s", len, ""); - puts(token); - len = 0; - } - free(lo); - } - exit(exit_status); -} diff --git a/src/rc/_usage.h b/src/rc/_usage.h deleted file mode 100644 index 62c131d4..00000000 --- a/src/rc/_usage.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (c) 2007-2015 The OpenRC Authors. - * See the Authors file at the top-level directory of this distribution and - * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS - * - * This file is part of OpenRC. It is subject to the license terms in - * the LICENSE file found in the top-level directory of this - * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE - * This file may not be copied, modified, propagated, or distributed - * except according to the terms contained in the LICENSE file. - */ - -#include - -#define getoptstring_COMMON "ChqVv" - -#define longopts_COMMON \ - { "help", 0, NULL, 'h'}, \ - { "nocolor", 0, NULL, 'C'}, \ - { "version", 0, NULL, 'V'}, \ - { "verbose", 0, NULL, 'v'}, \ - { "quiet", 0, NULL, 'q'}, \ - { NULL, 0, NULL, 0 } - -#define longopts_help_COMMON \ - "Display this help output", \ - "Disable color output", \ - "Display software version", \ - "Run verbosely", \ - "Run quietly (repeat to suppress errors)" - -#define case_RC_COMMON_getopt_case_C setenv ("EINFO_COLOR", "NO", 1); -#define case_RC_COMMON_getopt_case_h usage (EXIT_SUCCESS); -#define case_RC_COMMON_getopt_case_V if (argc == 2) show_version(); -#define case_RC_COMMON_getopt_case_v setenv ("EINFO_VERBOSE", "YES", 1); -#define case_RC_COMMON_getopt_case_q set_quiet_options(); -#define case_RC_COMMON_getopt_default usage (EXIT_FAILURE); - -#define case_RC_COMMON_GETOPT \ - case 'C': case_RC_COMMON_getopt_case_C; break; \ - case 'h': case_RC_COMMON_getopt_case_h; break; \ - case 'V': case_RC_COMMON_getopt_case_V; break; \ - case 'v': case_RC_COMMON_getopt_case_v; break; \ - case 'q': case_RC_COMMON_getopt_case_q; break; \ - default: case_RC_COMMON_getopt_default; break; - -extern const char *applet; -extern const char *extraopts; -extern const char getoptstring[]; -extern const struct option longopts[]; -extern const char * const longopts_help[]; -extern const char *usagestring; - -void set_quiet_options(void); -void show_version(void); -void usage(int exit_status); diff --git a/src/rc/broadcast.c b/src/rc/broadcast.c deleted file mode 100644 index 402a9fb9..00000000 --- a/src/rc/broadcast.c +++ /dev/null @@ -1,211 +0,0 @@ -/* - * broadcast.c - * broadcast a message to every logged in user - */ - -/* - * Copyright 2018 Sony Interactive Entertainment Inc. - * - * This file is part of OpenRC. It is subject to the license terms in - * the LICENSE file found in the top-level directory of this - * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE - * This file may not be copied, modified, propagated, or distributed - * except according to the terms contained in the LICENSE file. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "broadcast.h" -#include "helpers.h" - -#ifndef _PATH_DEV -# define _PATH_DEV "/dev/" -#endif - -static sigjmp_buf jbuf; - -/* - * Alarm handler - */ -/*ARGSUSED*/ -# ifdef __GNUC__ -static void handler(int arg __attribute__((unused))) -# else -static void handler(int arg) -# endif -{ - siglongjmp(jbuf, 1); -} - -static void getuidtty(char **userp, char **ttyp) -{ - struct passwd *pwd; - uid_t uid; - char *tty; - static char uidbuf[32]; - char *ttynm = NULL; - - uid = getuid(); - if ((pwd = getpwuid(uid)) != NULL) { - uidbuf[0] = 0; - strncat(uidbuf, pwd->pw_name, sizeof(uidbuf) - 1); - } else { - if (uid) - sprintf(uidbuf, "uid %d", (int) uid); - else - sprintf(uidbuf, "root"); - } - - if ((tty = ttyname(0)) != NULL) { - const size_t plen = strlen(_PATH_DEV); - if (strncmp(tty, _PATH_DEV, plen) == 0) { - tty += plen; - if (tty[0] == '/') - tty++; - } - xasprintf(&ttynm, "(%s) ", tty); - } - - *userp = uidbuf; - *ttyp = ttynm; -} - -/* - * Check whether the given filename looks like a tty device. - */ -static int file_isatty(const char *fname) -{ - struct stat st; - int major; - - if (stat(fname, &st) < 0) - return 0; - - if (st.st_nlink != 1 || !S_ISCHR(st.st_mode)) - return 0; - - /* - * It would be an impossible task to list all major/minors - * of tty devices here, so we just exclude the obvious - * majors of which just opening has side-effects: - * printers and tapes. - */ - major = major(st.st_dev); - if (major == 1 || major == 2 || major == 6 || major == 9 || - major == 12 || major == 16 || major == 21 || major == 27 || - major == 37 || major == 96 || major == 97 || major == 206 || - major == 230) - return 0; - return 1; -} - -/* - * broadcast function. - * - * NB: Not multithread safe. - */ -void broadcast(char *text) -{ - char *tty; - char *user; - struct utsname name; - time_t t; - char *date; - char *p; - char *line = NULL; - struct sigaction sa; - int flags; - char *term = NULL; - struct utmpx *utmp; - /* - * These are set across the sigsetjmp call, so they can't be stored on - * the stack, otherwise they might be clobbered. - */ - static int fd; - static FILE *tp; - - getuidtty(&user, &tty); - - /* - * Get and report current hostname, to make it easier to find out - * which machine is being shut down. - */ - uname(&name); - - /* Get the time */ - time(&t); - date = ctime(&t); - p = strchr(date, '\n'); - if (p) - *p = 0; - - xasprintf(&line, "\007\r\nBroadcast message from %s@%s %s(%s):\r\n\r\n", - user, name.nodename, tty, date); - free(tty); - - /* - * Fork to avoid hanging in a write() - */ - if (fork() != 0) - return; - - memset(&sa, 0, sizeof(sa)); - sa.sa_handler = handler; - sigemptyset(&sa.sa_mask); - sigaction(SIGALRM, &sa, NULL); - - setutxent(); - - while ((utmp = getutxent()) != NULL) { - if (utmp->ut_type != USER_PROCESS || utmp->ut_user[0] == 0) - continue; - if (strncmp(utmp->ut_line, _PATH_DEV, strlen(_PATH_DEV)) == 0) - xasprintf(&term, "%s", utmp->ut_line); - else - xasprintf(&term, "%s%s", _PATH_DEV, utmp->ut_line); - if (strstr(term, "/../")) { - free(term); - continue; - } - - /* - * Open it non-delay - */ - if (sigsetjmp(jbuf, 1) == 0) { - alarm(2); - flags = O_WRONLY|O_NDELAY|O_NOCTTY; - if (file_isatty(term) && (fd = open(term, flags)) >= 0) { - if (isatty(fd) && (tp = fdopen(fd, "w")) != NULL) { - fputs(line, tp); - fputs(text, tp); - fflush(tp); - } - } - } - alarm(0); - if (fd >= 0) - close(fd); - if (tp != NULL) - fclose(tp); - free(term); - } - endutxent(); - free(line); - exit(0); -} diff --git a/src/rc/broadcast.h b/src/rc/broadcast.h deleted file mode 100644 index 2255fe67..00000000 --- a/src/rc/broadcast.h +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright 2018 Sony Interactive Entertainment Inc. - * - * This file is part of OpenRC. It is subject to the license terms in - * the LICENSE file found in the top-level directory of this - * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE - * This file may not be copied, modified, propagated, or distributed - * except according to the terms contained in the LICENSE file. - */ - -#ifndef BROADCAST_H -#define BROADCAST_H - -void broadcast(char *text); - -#endif diff --git a/src/rc/checkpath.c b/src/rc/checkpath.c deleted file mode 100644 index 550e7cea..00000000 --- a/src/rc/checkpath.c +++ /dev/null @@ -1,452 +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-2015 The OpenRC Authors. - * See the Authors file at the top-level directory of this distribution and - * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS - * - * This file is part of OpenRC. It is subject to the license terms in - * the LICENSE file found in the top-level directory of this - * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE - * This file may not be copied, modified, propagated, or distributed - * except according to the terms contained in the LICENSE file. - */ - -#define _GNU_SOURCE -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "einfo.h" -#include "rc.h" -#include "rc-misc.h" -#include "rc-selinux.h" -#include "_usage.h" - -typedef enum { - inode_unknown = 0, - inode_file = 1, - inode_dir = 2, - inode_fifo = 3, -} inode_t; - -const char *applet = NULL; -const char *extraopts ="path1 [path2] [...]"; -const char getoptstring[] = "dDfFpm:o:sW" getoptstring_COMMON; -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'}, - { "symlinks", 0, NULL, 's'}, - { "writable", 0, NULL, 'W'}, - longopts_COMMON -}; -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)", - "follow symbolic links (irrelivent on linux)", - "Check whether the path is writable or not", - longopts_help_COMMON -}; -const char *usagestring = NULL; - -static int get_dirfd(char *path, bool symlinks) -{ - char *ch; - char *item; - char *linkpath = NULL; - char *path_dupe; - char *str; - int components = 0; - int dirfd; - int flags = 0; - int new_dirfd; - struct stat st; - ssize_t linksize; - - if (!path || *path != '/') - eerrorx("%s: empty or relative path", applet); - dirfd = openat(dirfd, "/", O_RDONLY); - if (dirfd == -1) - eerrorx("%s: unable to open the root directory: %s", - applet, strerror(errno)); - ch = path; - while (*ch) { - if (*ch == '/') - components++; - ch++; - } - path_dupe = xstrdup(path); - item = strtok(path_dupe, "/"); -#ifdef O_PATH - flags |= O_PATH; -#endif - if (!symlinks) - flags |= O_NOFOLLOW; - flags |= O_RDONLY; - while (dirfd > 0 && item && components > 1) { - str = xstrdup(linkpath ? linkpath : item); - new_dirfd = openat(dirfd, str, flags); - if (new_dirfd == -1) - eerrorx("%s: %s: could not open %s: %s", applet, path, str, - strerror(errno)); - if (fstat(new_dirfd, &st) == -1) - eerrorx("%s: %s: unable to stat %s: %s", applet, path, item, - strerror(errno)); - if (S_ISLNK(st.st_mode) ) { - if (st.st_uid != 0) - eerrorx("%s: %s: symbolic link %s not owned by root", - applet, path, str); - linksize = st.st_size+1; - if (linkpath) - free(linkpath); - linkpath = xmalloc(linksize); - memset(linkpath, 0, linksize); - if (readlinkat(new_dirfd, "", linkpath, linksize) != st.st_size) - eerrorx("%s: symbolic link destination changed", applet); - /* - * now follow the symlink. - */ - close(new_dirfd); - } else { - /* now walk down the directory path */ - close(dirfd); - dirfd = new_dirfd; - free(linkpath); - linkpath = NULL; - item = strtok(NULL, "/"); - components--; - } - } - free(path_dupe); - free(linkpath); - return dirfd; -} - -static char *clean_path(char *path) -{ - char *ch; - char *ch2; - char *str; - str = xmalloc(strlen(path) + 1); - ch = path; - ch2 = str; - while (true) { - *ch2 = *ch; - ch++; - ch2++; - if (!*(ch-1)) - break; - while (*(ch - 1) == '/' && *ch == '/') - ch++; - } - /* get rid of trailing / characters */ - while ((ch = strrchr(str, '/'))) { - if (ch == str) - break; - if (!*(ch+1)) - *ch = 0; - else - break; - } - return str; -} - -static int do_check(char *path, uid_t uid, gid_t gid, mode_t mode, - inode_t type, bool trunc, bool chowner, bool symlinks, bool selinux_on) -{ - struct stat st; - char *name = NULL; - int dirfd; - int fd; - int flags; - int r; - int readfd; - int readflags; - int u; - - memset(&st, 0, sizeof(st)); - flags = O_CREAT|O_NDELAY|O_WRONLY|O_NOCTTY; - readflags = O_NDELAY|O_NOCTTY|O_RDONLY; -#ifdef O_CLOEXEC - flags |= O_CLOEXEC; - readflags |= O_CLOEXEC; -#endif -#ifdef O_NOFOLLOW - flags |= O_NOFOLLOW; - readflags |= O_NOFOLLOW; -#endif - if (trunc) - flags |= O_TRUNC; - xasprintf(&name, "%s", basename_c(path)); - dirfd = get_dirfd(path, symlinks); - readfd = openat(dirfd, name, readflags); - if (readfd == -1 || (type == inode_file && trunc)) { - if (type == inode_file) { - einfo("%s: creating file", path); - if (!mode) /* 664 */ - mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH; - u = umask(0); - fd = openat(dirfd, name, flags, mode); - umask(u); - if (fd == -1) { - eerror("%s: open: %s", applet, strerror(errno)); - return -1; - } - if (readfd != -1 && trunc) - close(readfd); - readfd = 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 = mkdirat(dirfd, name, mode); - umask(u); - if (r == -1 && errno != EEXIST) { - eerror("%s: mkdirat: %s", applet, - strerror (errno)); - return -1; - } - readfd = openat(dirfd, name, readflags); - if (readfd == -1) { - eerror("%s: unable to open directory: %s", applet, - strerror(errno)); - return -1; - } - } 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; - } - readfd = openat(dirfd, name, readflags); - if (readfd == -1) { - eerror("%s: unable to open fifo: %s", applet, - strerror(errno)); - return -1; - } - } - } - if (fstat(readfd, &st) != -1) { - if (type != inode_dir && S_ISDIR(st.st_mode)) { - eerror("%s: is a directory", path); - close(readfd); - return 1; - } - if (type != inode_file && S_ISREG(st.st_mode)) { - eerror("%s: is a file", path); - close(readfd); - return 1; - } - if (type != inode_fifo && S_ISFIFO(st.st_mode)) { - eerror("%s: is a fifo", path); - close(readfd); - return -1; - } - - if (mode && (st.st_mode & 0777) != mode) { - if ((type != inode_dir) && (st.st_nlink > 1)) { - eerror("%s: chmod: Too many hard links to %s", applet, path); - close(readfd); - return -1; - } - if (S_ISLNK(st.st_mode)) { - eerror("%s: chmod: %s %s", applet, path, " is a symbolic link"); - close(readfd); - return -1; - } - einfo("%s: correcting mode", path); - if (fchmod(readfd, mode)) { - eerror("%s: chmod: %s", applet, strerror(errno)); - close(readfd); - return -1; - } - } - - if (chowner && (st.st_uid != uid || st.st_gid != gid)) { - if ((type != inode_dir) && (st.st_nlink > 1)) { - eerror("%s: chown: %s %s", applet, "Too many hard links to", path); - close(readfd); - return -1; - } - if (S_ISLNK(st.st_mode)) { - eerror("%s: chown: %s %s", applet, path, " is a symbolic link"); - close(readfd); - return -1; - } - einfo("%s: correcting owner", path); - if (fchown(readfd, uid, gid)) { - eerror("%s: chown: %s", applet, strerror(errno)); - close(readfd); - return -1; - } - } - if (selinux_on) - selinux_util_label(path); - } else { - eerror("fstat: %s: %s", path, strerror(errno)); - close(readfd); - return -1; - } - close(readfd); - - 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; -} - -int main(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 symlinks = false; - bool writable = false; - bool selinux_on = false; - char *path = NULL; - - applet = basename_c(argv[0]); - while ((opt = getopt_long(argc, argv, getoptstring, - longopts, (int *) 0)) != -1) - { - switch (opt) { - case 'D': - trunc = true; - /* falls through */ - case 'd': - type = inode_dir; - break; - case 'F': - trunc = true; - /* falls through */ - 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 's': -#ifndef O_PATH - symlinks = true; -#endif - 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; - - if (selinux_util_open() == 1) - selinux_on = true; - - while (optind < argc) { - path = clean_path(argv[optind]); - if (writable) - exit(!is_writable(path)); - if (do_check(path, uid, gid, mode, type, trunc, chowner, - symlinks, selinux_on)) - retval = EXIT_FAILURE; - optind++; - free(path); - } - - if (selinux_on) - selinux_util_close(); - - return retval; -} diff --git a/src/rc/do_e.c b/src/rc/do_e.c deleted file mode 100644 index 1778efe4..00000000 --- a/src/rc/do_e.c +++ /dev/null @@ -1,228 +0,0 @@ -/* - * Copyright (c) 2007-2015 The OpenRC Authors. - * See the Authors file at the top-level directory of this distribution and - * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS - * - * This file is part of OpenRC. It is subject to the license terms in - * the LICENSE file found in the top-level directory of this - * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE - * This file may not be copied, modified, propagated, or distributed - * except according to the terms contained in the LICENSE file. - */ - -#define SYSLOG_NAMES - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "einfo.h" -#include "helpers.h" - -/* usecs to wait while we poll the file existance */ -#define WAIT_INTERVAL 20000000 - -const char *applet = NULL; - -static int syslog_decode(char *name, const CODE *codetab) -{ - const 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; -} - -int main(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; - - applet = basename_c(argv[0]); - 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; -} diff --git a/src/rc/do_mark_service.c b/src/rc/do_mark_service.c deleted file mode 100644 index d7b2658a..00000000 --- a/src/rc/do_mark_service.c +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (c) 2007-2015 The OpenRC Authors. - * See the Authors file at the top-level directory of this distribution and - * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS - * - * This file is part of OpenRC. It is subject to the license terms in - * the LICENSE file found in the top-level directory of this - * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE - * This file may not be copied, modified, propagated, or distributed - * except according to the terms contained in the LICENSE file. - */ - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "einfo.h" -#include "rc.h" -#include "rc-misc.h" - -const char *applet = NULL; - -int main(int argc, char **argv) -{ - bool ok = false; - char *svcname = getenv("RC_SVCNAME"); - char *service = NULL; - char *openrc_pid; - /* char *mtime; */ - pid_t pid; - RC_SERVICE bit; - /* size_t l; */ - - applet = basename_c(argv[0]); - 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 - openrc-run 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) { - openrc_pid = getenv("RC_OPENRC_PID"); - if (openrc_pid && sscanf(openrc_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(openrc_pid) + 4; - mtime = xmalloc(l); - snprintf(mtime, l, RC_SVCDIR "/exclusive/%s.%s", - svcname, openrc_pid); - if (exists(mtime) && unlink(mtime) != 0) - eerror("%s: unlink: %s", applet, strerror(errno)); - free(mtime); - */ - } - - return ok ? EXIT_SUCCESS : EXIT_FAILURE; -} diff --git a/src/rc/do_service.c b/src/rc/do_service.c deleted file mode 100644 index eca498a6..00000000 --- a/src/rc/do_service.c +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (c) 2007-2015 The OpenRC Authors. - * See the Authors file at the top-level directory of this distribution and - * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS - * - * This file is part of OpenRC. It is subject to the license terms in - * the LICENSE file found in the top-level directory of this - * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE - * This file may not be copied, modified, propagated, or distributed - * except according to the terms contained in the LICENSE file. - */ - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "einfo.h" -#include "rc.h" -#include "rc-misc.h" - -const char *applet = NULL; - -int main(int argc, char **argv) -{ - bool ok = false; - char *service; - char *exec; - int idx = 0; - RC_SERVICE state, bit; - - applet = basename_c(argv[0]); - 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_service_daemons_crashed(service) && errno != EACCES); - } else - eerrorx("%s: unknown applet", applet); - - return ok ? EXIT_SUCCESS : EXIT_FAILURE; -} diff --git a/src/rc/do_value.c b/src/rc/do_value.c deleted file mode 100644 index 9ec5facf..00000000 --- a/src/rc/do_value.c +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (c) 2016 The OpenRC Authors. - * See the Authors file at the top-level directory of this distribution and - * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS - * - * This file is part of OpenRC. It is subject to the license terms in - * the LICENSE file found in the top-level directory of this - * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE - * This file may not be copied, modified, propagated, or distributed - * except according to the terms contained in the LICENSE file. - */ - -#define SYSLOG_NAMES - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "einfo.h" -#include "rc.h" -#include "rc-misc.h" - -const char *applet = NULL; - -int main(int argc, char **argv) -{ - bool ok = false; - char *service = getenv("RC_SVCNAME"); - char *option; - - applet = basename_c(argv[0]); - 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; -} diff --git a/src/rc/fstabinfo.c b/src/rc/fstabinfo.c deleted file mode 100644 index 2a1a12ea..00000000 --- a/src/rc/fstabinfo.c +++ /dev/null @@ -1,339 +0,0 @@ -/* - * fstabinfo.c - * Gets information about /etc/fstab. - */ - -/* - * Copyright (c) 2007-2015 The OpenRC Authors. - * See the Authors file at the top-level directory of this distribution and - * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS - * - * This file is part of OpenRC. It is subject to the license terms in - * the LICENSE file found in the top-level directory of this - * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE - * This file may not be copied, modified, propagated, or distributed - * except according to the terms contained in the LICENSE file. - */ - -#include - -#include -#include -#include -#include -#include -#include - -/* 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 -# 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 -# 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 "einfo.h" -#include "queue.h" -#include "rc.h" -#include "rc-misc.h" -#include "_usage.h" - -const char *applet = NULL; -const char *extraopts = NULL; -const char getoptstring[] = "MRbmop:t:" getoptstring_COMMON; -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 -}; -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 -}; -const char *usagestring = NULL; - -#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: execvp: %s", applet, strerror(errno)); - _exit(EXIT_FAILURE); - /* NOTREACHED */ - default: - waitpid(pid, &status, 0); - if (WIFEXITED(status)) - return WEXITSTATUS(status); - else - return -1; - /* NOTREACHED */ - } -} - -#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 main(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 - - /* fail if there is no /etc/fstab */ - if (!exists("/etc/fstab")) - eerrorx("/etc/fstab does not exist"); - /* Ensure that we are only quiet when explicitly told to be */ - unsetenv("EINFO_QUIET"); - - applet = basename_c(argv[0]); - 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/is_newer_than.c b/src/rc/is_newer_than.c deleted file mode 100644 index 957ecbf4..00000000 --- a/src/rc/is_newer_than.c +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2016 The OpenRC Authors. - * See the Authors file at the top-level directory of this distribution and - * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS - * - * This file is part of OpenRC. It is subject to the license terms in - * the LICENSE file found in the top-level directory of this - * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE - * This file may not be copied, modified, propagated, or distributed - * except according to the terms contained in the LICENSE file. - */ - -#include -#include -#include -#include - -#include "rc.h" -#include "rc-misc.h" - -int main(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; -} diff --git a/src/rc/is_older_than.c b/src/rc/is_older_than.c deleted file mode 100644 index c9e25f30..00000000 --- a/src/rc/is_older_than.c +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2016 The OpenRC Authors. - * See the Authors file at the top-level directory of this distribution and - * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS - * - * This file is part of OpenRC. It is subject to the license terms in - * the LICENSE file found in the top-level directory of this - * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE - * This file may not be copied, modified, propagated, or distributed - * except according to the terms contained in the LICENSE file. - */ - -#include -#include -#include -#include - -#include "rc.h" -#include "rc-misc.h" - -int main(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; -} diff --git a/src/rc/kill_all.c b/src/rc/kill_all.c deleted file mode 100644 index 551572b7..00000000 --- a/src/rc/kill_all.c +++ /dev/null @@ -1,260 +0,0 @@ -/* - * kill_all.c - * Sends a signal to all processes on the system. - */ - -/* - * Copyright (c) 2017 The OpenRC Authors. - * See the Authors file at the top-level directory of this distribution and - * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS - * - * This file is part of OpenRC. It is subject to the license terms in - * the LICENSE file found in the top-level directory of this - * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE - * This file may not be copied, modified, propagated, or distributed - * except according to the terms contained in the LICENSE file. - */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "einfo.h" -#include "rc.h" -#include "rc-misc.h" -#include "_usage.h" - -const char *applet = NULL; -const char *extraopts = "[signal number]"; -const char getoptstring[] = "do:" getoptstring_COMMON; -const struct option longopts[] = { - { "dry-run", 0, NULL, 'd' }, - { "omit", 1, NULL, 'o' }, - longopts_COMMON -}; -const char * const longopts_help[] = { - "print what would be done", - "omit this pid (can be repeated)", - longopts_help_COMMON -}; -const char *usagestring = NULL; - -static int mount_proc(void) -{ - pid_t pid; - pid_t rc; - int status; - - if (exists("/proc/version")) - return 0; - pid = fork(); - switch (pid) { - case -1: - syslog(LOG_ERR, "Unable to fork"); - return -1; - break; - case 0: - /* attempt to mount /proc */ - execlp("mount", "mount", "-t", "proc", "proc", "/proc", NULL); - syslog(LOG_ERR, "Unable to execute mount"); - exit(1); - break; - default: - /* wait for child process */ - while ((rc = wait(&status)) != pid) - if (rc < 0 && errno == ECHILD) - break; - if (rc != pid || WEXITSTATUS(status) != 0) - syslog(LOG_ERR, "mount returned non-zero exit status"); - break; - } - if (!exists("/proc/version")) { - syslog(LOG_ERR, "Could not mount /proc"); - return -1; - } - return 0; -} - -static bool is_user_process(pid_t pid) -{ - char *buf = NULL; - FILE *fp; - char *path = NULL; - pid_t temp_pid; - size_t size; - bool user_process = true; - - while (pid >0 && user_process) { - if (pid == 2) { - user_process = false; - continue; - } - xasprintf(&path, "/proc/%d/status", pid); - fp = fopen(path, "r"); - free(path); - /* - * if we could not open the file, the process disappeared, which - * leaves us no way to determine for sure whether it was a user - * process or kernel thread, so we say it is a kernel thread to - * avoid accidentally killing it. - */ - if (!fp) { - user_process = false; - continue; - } - temp_pid = -1; - while (!feof(fp)) { - buf = NULL; - if (getline(&buf, &size, fp) != -1) { - sscanf(buf, "PPid: %d", &temp_pid); - free(buf); - } else { - free(buf); - break; - } - } - fclose(fp); - if (temp_pid == -1) { - syslog(LOG_ERR, "Unable to read pid from /proc/%d/status", pid); - user_process = false; - continue; - } - pid = temp_pid; - } - return user_process; -} - -static int signal_processes(int sig, RC_STRINGLIST *omits, bool dryrun) -{ - sigset_t signals; - sigset_t oldsigs; - DIR *dir; - struct dirent *d; - char *buf = NULL; - pid_t pid; - int sendcount = 0; - - kill(-1, SIGSTOP); - sigfillset(&signals); - sigemptyset(&oldsigs); - sigprocmask(SIG_SETMASK, &signals, &oldsigs); - /* - * Open the /proc directory. - * CWD must be /proc to avoid problems if / is affected by the killing - * (i.e. depends on fuse). - */ - if (chdir("/proc") == -1) { - syslog(LOG_ERR, "chdir /proc failed"); - sigprocmask(SIG_SETMASK, &oldsigs, NULL); - kill(-1, SIGCONT); - return -1; - } - dir = opendir("."); - if (!dir) { - syslog(LOG_ERR, "cannot opendir(/proc)"); - sigprocmask(SIG_SETMASK, &oldsigs, NULL); - kill(-1, SIGCONT); - return -1; - } - - /* Walk through the directory. */ - while ((d = readdir(dir)) != NULL) { - /* Is this a process? */ - pid = (pid_t) atoi(d->d_name); - if (pid == 0) - continue; - - /* Is this a process we have been requested to omit? */ - if (buf) { - free(buf); - buf = NULL; - } - xasprintf(&buf, "%d", pid); - if (rc_stringlist_find(omits, buf)) - continue; - - /* Is this process in our session? */ - if (getsid(getpid()) == getsid(pid)) - continue; - - /* Is this a kernel thread? */ - if (!is_user_process(pid)) - continue; - - if (dryrun) - einfo("Would send signal %d to process %d", sig, pid); - else if (kill(pid, sig) == 0) - sendcount++; - } - closedir(dir); - sigprocmask(SIG_SETMASK, &oldsigs, NULL); - kill(-1, SIGCONT); - return sendcount; -} - -int main(int argc, char **argv) -{ - char *arg = NULL; - int opt; - bool dryrun = false; - RC_STRINGLIST *omits = rc_stringlist_new(); - int sig = SIGKILL; - char *here; - char *token; - - /* Ensure that we are only quiet when explicitly told to be */ - unsetenv("EINFO_QUIET"); - - applet = basename_c(argv[0]); - rc_stringlist_addu(omits, "1"); - while ((opt = getopt_long(argc, argv, getoptstring, - longopts, (int *) 0)) != -1) - { - switch (opt) { - case 'd': - dryrun = true; - break; - case 'o': - here = optarg; - while ((token = strsep(&here, ",;:"))) { - if ((pid_t) atoi(token) > 0) - rc_stringlist_addu(omits, token); - else { - eerror("Invalid omit pid value %s", token); - usage(EXIT_FAILURE); - } - } - break; - case_RC_COMMON_GETOPT - } - } - - if (argc > optind) { - arg = argv[optind]; - sig = atoi(arg); - if (sig <= 0 || sig > 31) { - rc_stringlist_free(omits); - eerror("Invalid signal %s", arg); - usage(EXIT_FAILURE); - } - } - - openlog(applet, LOG_CONS|LOG_PID, LOG_DAEMON); - if (mount_proc() != 0) { - rc_stringlist_free(omits); - eerrorx("Unable to mount /proc file system"); - } - signal_processes(sig, omits, dryrun); - rc_stringlist_free(omits); - return 0; -} diff --git a/src/rc/meson.build b/src/rc/meson.build deleted file mode 100644 index edea4a16..00000000 --- a/src/rc/meson.build +++ /dev/null @@ -1,320 +0,0 @@ -rc_misc_c = files([ - 'rc-misc.c', - ]) - -rc_plugin_c = files([ - 'rc-plugin.c', - ]) - -rc_schedules_c = files([ - 'rc-schedules.c', - ]) - -usage_c = files([ - '_usage.c', - ]) - -if selinux_dep.found() - rc_selinux_c = files([ - 'rc-selinux.c', - ]) -else - rc_selinux_c = [] -endif - -rc_wtmp_c = files([ - 'rc-wtmp.c', - ]) - -executable('rc-status', - ['rc-status.c', rc_misc_c, usage_c, version_h], - c_args : cc_branding_flags, - link_with: [libeinfo, librc], - dependencies: [util_dep], - include_directories: [incdir, einfo_incdir, rc_incdir], - install: true, - install_dir: bindir) - -executable('openrc', - ['rc.c', 'rc-logger.c', rc_misc_c, rc_plugin_c, usage_c, - version_h], - c_args : cc_branding_flags, - link_with: [libeinfo, librc], - dependencies: [dl_dep, util_dep], - include_directories: [incdir, einfo_incdir, rc_incdir], - install: true, - install_dir: sbindir) - -executable('openrc-run', - ['openrc-run.c', rc_misc_c, rc_plugin_c, usage_c, - rc_selinux_c, version_h], - c_args : [cc_audit_flags, cc_branding_flags, cc_pam_flags, cc_selinux_flags], - link_with: [libeinfo, librc], - dependencies: [audit_dep, dl_dep, pam_dep, pam_misc_dep, selinux_dep, util_dep, crypt_dep], - include_directories: [incdir, einfo_incdir, rc_incdir], - install: true, - install_dir: sbindir) - -executable('rc', - ['rc.c', 'rc-logger.c', rc_misc_c, rc_plugin_c, usage_c, version_h], - c_args : cc_branding_flags, - link_with: [libeinfo, librc], - dependencies: [dl_dep, util_dep], - include_directories: [incdir, einfo_incdir, rc_incdir], - install: true, - install_dir: sbindir) - -executable('rc-service', - ['rc-service.c', rc_misc_c, usage_c, version_h], - c_args : cc_branding_flags, - link_with: [libeinfo, librc], - include_directories: [incdir, einfo_incdir, rc_incdir], - install: true, - install_dir: sbindir) - -executable('rc-update', - ['rc-update.c', rc_misc_c, usage_c, version_h], - c_args : cc_branding_flags, - link_with: [libeinfo, librc], - include_directories: [incdir, einfo_incdir, rc_incdir], - install: true, - install_dir: sbindir) - -executable('runscript', - ['openrc-run.c', rc_misc_c, usage_c, 'rc-plugin.c', - rc_selinux_c, version_h], - c_args : [cc_audit_flags, cc_branding_flags, cc_pam_flags, cc_selinux_flags], - link_with: [libeinfo, librc], - dependencies: [audit_dep, dl_dep, pam_dep, pam_misc_dep, util_dep, selinux_dep, crypt_dep], - include_directories: [incdir, einfo_incdir, rc_incdir], - install: true, - install_dir: sbindir) - -executable('start-stop-daemon', - ['start-stop-daemon.c', 'rc-pipes.c', rc_misc_c, rc_schedules_c, - rc_selinux_c, usage_c, version_h], - c_args : [cc_audit_flags, cc_branding_flags, cc_pam_flags, cc_cap_flags, cc_selinux_flags], - link_with: [libeinfo, librc], - dependencies: [audit_dep, dl_dep, pam_dep, cap_dep, pam_misc_dep, util_dep, selinux_dep, crypt_dep], - include_directories: [incdir, einfo_incdir, rc_incdir], - install: true, - install_dir: sbindir) - -if get_option('pam') - install_data('start-stop-daemon.pam', - rename : 'start-stop-daemon', - install_dir : pamdir) -endif - -executable('supervise-daemon', - ['supervise-daemon.c', rc_misc_c, rc_plugin_c, rc_schedules_c, - usage_c, version_h], - c_args : [cc_branding_flags, cc_pam_flags, cc_cap_flags, cc_selinux_flags], - link_with: [libeinfo, librc], - dependencies: [dl_dep, pam_dep, cap_dep, util_dep, selinux_dep], - include_directories: [incdir, einfo_incdir, rc_incdir], - install: true, - install_dir: sbindir) - -if get_option('pam') - install_data('supervise-daemon.pam', - rename : 'supervise-daemon', - install_dir : pamdir) -endif - -if os == 'Linux' - executable('openrc-init', - ['openrc-init.c', rc_plugin_c, rc_wtmp_c, version_h], - c_args : cc_selinux_flags, - include_directories: [incdir, einfo_incdir, rc_incdir], - link_with: [libeinfo, librc], - dependencies: [dl_dep, selinux_dep], - install: true, - install_dir: sbindir) - - executable('openrc-shutdown', - ['openrc-shutdown.c', 'broadcast.c', 'rc-sysvinit.c', rc_misc_c, - usage_c, rc_wtmp_c, version_h], - c_args : cc_branding_flags, - include_directories: [incdir, einfo_incdir, rc_incdir], - link_with: [libeinfo, librc], - install: true, - install_dir: sbindir) -endif - -einfo_execs = [ - 'einfon', - 'einfo', - 'ewarnn', - 'ewarn', - 'eerrorn', - 'eerror', - 'ebegin', - 'eend', - 'ewend', - 'eindent', - 'eoutdent', - 'esyslog', - 'eval_ecolors', - 'ewaitfile', - 'veinfo', - 'vewarn', - 'vebegin', - 'veend', - 'vewend', - 'veindent', - 'veoutdent', - ] - -foreach exec: einfo_execs - executable(exec, - ['do_e.c', rc_misc_c, version_h], - include_directories: [incdir, einfo_incdir, rc_incdir], - link_with: [libeinfo, librc], - install: true, - install_dir: rc_bindir) -endforeach - -executable('checkpath', - ['checkpath.c', rc_misc_c, usage_c, rc_selinux_c, - version_h], - c_args : [cc_audit_flags, cc_branding_flags, cc_pam_flags, cc_selinux_flags], - include_directories: [incdir, einfo_incdir, rc_incdir], - link_with: [libeinfo, librc], - dependencies: [audit_dep, pam_dep, pam_misc_dep, selinux_dep, crypt_dep], - install: true, - install_dir: rc_bindir) - -executable('fstabinfo', - ['fstabinfo.c', rc_misc_c, usage_c, version_h], - c_args : cc_branding_flags, - include_directories: [incdir, einfo_incdir, rc_incdir], - link_with: [libeinfo, librc], - install: true, - install_dir: rc_bindir) - -executable('mountinfo', - ['mountinfo.c', rc_misc_c, usage_c, version_h], - c_args : cc_branding_flags, - include_directories: [incdir, einfo_incdir, rc_incdir], - link_with: [libeinfo, librc], - install: true, - install_dir: rc_bindir) - -executable('rc-depend', - ['rc-depend.c', rc_misc_c, usage_c, version_h], - c_args : cc_branding_flags, - include_directories: [incdir, einfo_incdir, rc_incdir], - link_with: [libeinfo, librc], - install: true, - install_dir: rc_bindir) - -executable('is_newer_than', - ['is_newer_than.c', rc_misc_c, version_h], - include_directories: [incdir, einfo_incdir, rc_incdir], - link_with: [libeinfo, librc], - install: true, - install_dir: rc_bindir) - -executable('is_older_than', - ['is_older_than.c', rc_misc_c, version_h], - include_directories: [incdir, einfo_incdir, rc_incdir], - link_with: [libeinfo, librc], - install: true, - install_dir: rc_bindir) - -service_execs = [ - 'service_starting', - 'service_started', - 'service_stopping', - 'service_stopped', - 'service_inactive', - 'service_wasinactive', - 'service_hotplugged', - 'service_started_daemon', - 'service_crashed', - ] - -foreach exec : service_execs - executable(exec, - ['do_service.c', rc_misc_c, version_h], - include_directories: [incdir, einfo_incdir, rc_incdir], - link_with: [libeinfo, librc], - install: true, - install_dir: rc_bindir) -endforeach - -value_execs = [ - 'service_get_value', - 'service_set_value', - 'get_options', - 'save_options', - ] - -foreach exec : value_execs - executable(exec, - ['do_value.c', rc_misc_c, version_h], - include_directories: [incdir, einfo_incdir, rc_incdir], - link_with: [libeinfo, librc], - install: true, - install_dir: rc_bindir) -endforeach - -if os == 'Linux' - executable('kill_all', - ['kill_all.c', usage_c, version_h], - c_args : cc_branding_flags, - include_directories: [incdir, einfo_incdir, rc_incdir], - link_with: [libeinfo,librc], - install: true, - install_dir: rc_bindir) - - executable('seedrng', - ['seedrng.c', usage_c, version_h], - c_args : cc_branding_flags, - include_directories: [incdir, einfo_incdir, rc_incdir], - link_with: [libeinfo, librc], - install: true, - install_dir: rc_sbindir) -endif - -executable('shell_var', - ['shell_var.c'], - install: true, - install_dir: rc_bindir) - -mark_service_execs = [ - 'mark_service_starting', - 'mark_service_started', - 'mark_service_stopping', - 'mark_service_stopped', - 'mark_service_inactive', - 'mark_service_wasinactive', - 'mark_service_hotplugged', - 'mark_service_failed', - 'mark_service_crashed', - ] - -foreach exec : mark_service_execs - executable(exec, - ['do_mark_service.c', rc_misc_c, version_h], - include_directories: [incdir, einfo_incdir, rc_incdir], - link_with: [libeinfo,librc], - install: true, - install_dir: rc_sbindir) -endforeach - -executable('rc-abort', - 'rc-abort.c', - include_directories: [einfo_incdir], - link_with: [libeinfo], - install: true, - install_dir: rc_sbindir) - -executable('swclock', - ['swclock.c', rc_misc_c, usage_c, version_h], - c_args : cc_branding_flags, - include_directories: [incdir, einfo_incdir, rc_incdir], - link_with: [libeinfo,librc], - install: true, - install_dir: rc_sbindir) diff --git a/src/rc/mountinfo.c b/src/rc/mountinfo.c deleted file mode 100644 index 6652760d..00000000 --- a/src/rc/mountinfo.c +++ /dev/null @@ -1,488 +0,0 @@ -/* - * mountinfo.c - * Obtains information about mounted filesystems. - */ - -/* - * Copyright 2007-2015 The OpenRC Authors. - * See the Authors file at the top-level directory of this distribution and - * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS - * - * This file is part of OpenRC. It is subject to the license terms in - * the LICENSE file found in the top-level directory of this - * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE - * This file may not be copied, modified, propagated, or distributed - * except according to the terms contained in the LICENSE file. - */ - -#include -#include - -#if defined(__DragonFly__) || defined(__FreeBSD__) -# include -# include -# define F_FLAGS f_flags -#elif defined(BSD) && !defined(__GNU__) -# include -# define statfs statvfs -# define F_FLAGS f_flag -#elif defined(__linux__) || (defined(__FreeBSD_kernel__) && \ - defined(__GLIBC__)) || defined(__GNU__) -# include -#endif - -#include -#include -#include -#include -#include -#include -#include - -#include "einfo.h" -#include "queue.h" -#include "rc.h" -#include "rc-misc.h" -#include "_usage.h" - -const char *applet = NULL; -const char *procmounts = "/proc/mounts"; -const char *extraopts = "[mount1] [mount2] ..."; -const char getoptstring[] = "f:F:n:N:o:O:p:P:iste:E:" getoptstring_COMMON; -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 -}; -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 -}; -const char *usagestring = NULL; - -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; - - 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 { - xasprintf(&tmp, "%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__)) || defined(__GNU__) -static struct mntent * -getmntfile(const char *file) -{ - struct mntent *ent = NULL; - FILE *fp; - - if (!exists("/etc/fstab")) - return NULL; - - 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; - size_t size; - char *p; - char *from; - char *to; - char *fst; - char *opts; - struct mntent *ent; - int netdev; - RC_STRINGLIST *list; - - if ((fp = fopen(procmounts, "r")) == NULL) - eerrorx("getmntinfo: %s", strerror(errno)); - - list = rc_stringlist_new(); - - buffer = NULL; - while (getline(&buffer, &size, fp) != -1) { - 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; - else - netdev = 1; - } - - process_mount(list, args, from, to, fst, opts, netdev); - free(buffer); - buffer = NULL; - } - 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; -} - -int main(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 = NULL; - 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); } - - applet = basename_c(argv[0]); - 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++]; - real_path = realpath(this_path, NULL); - if (real_path) - this_path = real_path; - rc_stringlist_add(args.mounts, this_path); - free(real_path); - real_path = NULL; - } - 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/openrc-init.c b/src/rc/openrc-init.c deleted file mode 100644 index 5bc53434..00000000 --- a/src/rc/openrc-init.c +++ /dev/null @@ -1,333 +0,0 @@ -/* - * openrc-init.c - * This is the init process (pid 1) for OpenRC. - * - * This is based on code written by James Hammons , so - * I would like to publically thank him for his work. - */ - -/* - * Copyright (c) 2017 The OpenRC Authors. - * See the Authors file at the top-level directory of this distribution and - * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS - * - * This file is part of OpenRC. It is subject to the license terms in - * the LICENSE file found in the top-level directory of this - * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE - * This file may not be copied, modified, propagated, or distributed - * except according to the terms contained in the LICENSE file. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef HAVE_SELINUX -# include -#endif - -#include "helpers.h" -#include "rc.h" -#include "rc-plugin.h" -#include "rc-wtmp.h" -#include "version.h" - -static const char *path_default = "/sbin:/usr/sbin:/bin:/usr/bin"; -static const char *rc_default_runlevel = "default"; - -static void do_openrc(const char *runlevel) -{ - pid_t pid; - sigset_t all_signals; - sigset_t our_signals; - - sigfillset(&all_signals); - /* block all signals */ - sigprocmask(SIG_BLOCK, &all_signals, &our_signals); - pid = fork(); - switch (pid) { - case -1: - perror("fork"); - exit(1); - break; - case 0: - setsid(); - /* unblock all signals */ - sigprocmask(SIG_UNBLOCK, &all_signals, NULL); - printf("Starting %s runlevel\n", runlevel); - execlp("openrc", "openrc", runlevel, NULL); - perror("exec"); - exit(1); - break; - default: - /* restore our signal mask */ - sigprocmask(SIG_SETMASK, &our_signals, NULL); - while (waitpid(pid, NULL, 0) != pid) - if (errno == ECHILD) - break; - break; - } -} - -static void init(const char *default_runlevel) -{ - const char *runlevel = NULL; - do_openrc("sysinit"); - do_openrc("boot"); - if (default_runlevel) - runlevel = default_runlevel; - else - runlevel = rc_conf_value("rc_default_runlevel"); - if (!runlevel) - runlevel = rc_default_runlevel; - if (!rc_runlevel_exists(runlevel)) { - printf("%s is an invalid runlevel\n", runlevel); - runlevel = rc_default_runlevel; - } - do_openrc(runlevel); - log_wtmp("reboot", "~~", 0, RUN_LVL, "~~"); -} - -static void handle_reexec(char *my_name) -{ - execlp(my_name, my_name, "reexec", NULL); - return; -} - -static void handle_shutdown(const char *runlevel, int cmd) -{ - struct timespec ts; - - do_openrc(runlevel); - printf("Sending the final term signal\n"); - kill(-1, SIGTERM); - ts.tv_sec = 3; - ts.tv_nsec = 0; - nanosleep(&ts, NULL); - printf("Sending the final kill signal\n"); - kill(-1, SIGKILL); - sync(); - reboot(cmd); -} - -static void run_program(const char *prog) -{ - sigset_t full; - sigset_t old; - pid_t pid; - - /* We need to block signals until we have forked */ - sigfillset(&full); - sigprocmask(SIG_SETMASK, &full, &old); - pid = fork(); - if (pid == -1) { - perror("init"); - return; - } - if (pid == 0) { - /* Unmask signals */ - sigprocmask(SIG_SETMASK, &old, NULL); - execl(prog, prog, (char *)NULL); - perror("init"); - exit(1); - } - /* Unmask signals and wait for child */ - sigprocmask(SIG_SETMASK, &old, NULL); - if (rc_waitpid(pid) == -1) - perror("init"); -} - -static void open_shell(void) -{ - const char *shell; - struct passwd *pw; - -#ifdef __linux__ - const char *sys = rc_sys(); - - /* VSERVER systems cannot really drop to shells */ - if (sys && strcmp(sys, RC_SYS_VSERVER) == 0) - { - execlp("halt", "halt", "-f", (char *) NULL); - perror("init"); - return; - } -#endif - - shell = rc_conf_value("rc_shell"); - /* No shell set, so obey env, then passwd, then default to /bin/sh */ - if (!shell) { - shell = getenv("SHELL"); - if (!shell) { - pw = getpwuid(getuid()); - if (pw) - shell = pw->pw_shell; - if (!shell) - shell = "/bin/sh"; - } - } - run_program(shell); -} - -static void handle_single(void) -{ - do_openrc("single"); -} - -static void reap_zombies(void) -{ - pid_t pid; - - for (;;) { - pid = waitpid(-1, NULL, WNOHANG); - if (pid == 0) - break; - else if (pid == -1) { - if (errno == ECHILD) - break; - perror("waitpid"); - continue; - } - } -} - -static void signal_handler(int sig) -{ - switch (sig) { - case SIGINT: - handle_shutdown("reboot", RB_AUTOBOOT); - break; - case SIGTERM: -#ifdef SIGPWR - case SIGPWR: -#endif - handle_shutdown("shutdown", RB_HALT_SYSTEM); - break; - case SIGCHLD: - reap_zombies(); - break; - default: - printf("Unknown signal received, %d\n", sig); - break; - } -} - -int main(int argc, char **argv) -{ - char *default_runlevel; - char buf[2048]; - int count; - FILE *fifo; - bool reexec = false; - sigset_t signals; - struct sigaction sa; -#ifdef HAVE_SELINUX - int enforce = 0; -#endif - - if (getpid() != 1) - return 1; - -#ifdef HAVE_SELINUX - if (getenv("SELINUX_INIT") == NULL) { - if (is_selinux_enabled() != 1) { - if (selinux_init_load_policy(&enforce) == 0) { - putenv("SELINUX_INIT=YES"); - execv(argv[0], argv); - } else { - if (enforce > 0) { - /* - * SELinux in enforcing mode but load_policy failed - * At this point, we probably can't open /dev/console, - * so log() won't work - */ - fprintf(stderr,"Unable to load SELinux Policy.\n"); - fprintf(stderr,"Machine is in enforcing mode.\n"); - fprintf(stderr,"Halting now.\n"); - exit(1); - } - } - } - } -#endif - - printf("OpenRC init version %s starting\n", VERSION); - - if (argc > 1) - default_runlevel = argv[1]; - else - default_runlevel = NULL; - - if (default_runlevel && strcmp(default_runlevel, "reexec") == 0) - reexec = true; - - /* block all signals we do not handle */ - sigfillset(&signals); - sigdelset(&signals, SIGCHLD); - sigdelset(&signals, SIGINT); - sigdelset(&signals, SIGTERM); -#ifdef SIGPWR - sigdelset(&signals, SIGPWR); -#endif - sigprocmask(SIG_SETMASK, &signals, NULL); - - /* install signal handler */ - memset(&sa, 0, sizeof(sa)); - sa.sa_handler = signal_handler; - sigaction(SIGCHLD, &sa, NULL); - sigaction(SIGINT, &sa, NULL); - sigaction(SIGTERM, &sa, NULL); -#ifdef SIGPWR - sigaction(SIGPWR, &sa, NULL); -#endif - reboot(RB_DISABLE_CAD); - - /* set default path */ - setenv("PATH", path_default, 1); - - if (!reexec) - init(default_runlevel); - - if (mkfifo(RC_INIT_FIFO, 0600) == -1 && errno != EEXIST) - perror("mkfifo"); - - for (;;) { - /* This will block until a command is sent down the pipe... */ - fifo = fopen(RC_INIT_FIFO, "r"); - if (!fifo) { - if (errno != EINTR) - perror("fopen"); - continue; - } - count = fread(buf, 1, sizeof(buf) - 1, fifo); - buf[count] = 0; - fclose(fifo); - printf("PID1: Received \"%s\" from FIFO...\n", buf); - if (strcmp(buf, "halt") == 0) - handle_shutdown("shutdown", RB_HALT_SYSTEM); - else if (strcmp(buf, "kexec") == 0) - handle_shutdown("reboot", RB_KEXEC); - else if (strcmp(buf, "poweroff") == 0) - handle_shutdown("shutdown", RB_POWER_OFF); - else if (strcmp(buf, "reboot") == 0) - handle_shutdown("reboot", RB_AUTOBOOT); - else if (strcmp(buf, "reexec") == 0) - handle_reexec(argv[0]); - else if (strcmp(buf, "single") == 0) { - handle_single(); - open_shell(); - init(default_runlevel); - } - } - return 0; -} diff --git a/src/rc/openrc-run.c b/src/rc/openrc-run.c deleted file mode 100644 index ff9659a3..00000000 --- a/src/rc/openrc-run.c +++ /dev/null @@ -1,1436 +0,0 @@ -/* - * openrc-run.c - * Handle launching of init scripts. - */ - -/* - * Copyright (c) 2007-2015 The OpenRC Authors. - * See the Authors file at the top-level directory of this distribution and - * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS - * - * This file is part of OpenRC. It is subject to the license terms in - * the LICENSE file found in the top-level directory of this - * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE - * This file may not be copied, modified, propagated, or distributed - * except according to the terms contained in the LICENSE file. - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if defined(__linux__) || (defined(__FreeBSD_kernel__) && defined(__GLIBC__)) \ - || defined(__GNU__) -# include -#elif defined(__NetBSD__) || defined(__OpenBSD__) -# include -#else -# include -#endif - -#include "einfo.h" -#include "queue.h" -#include "rc.h" -#include "rc-misc.h" -#include "rc-plugin.h" -#include "rc-selinux.h" -#include "_usage.h" - -#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 */ - -const char *applet = NULL; -const char *extraopts = "stop | start | restart | describe | zap"; -const char getoptstring[] = "dDsSvl:Z" getoptstring_COMMON; -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 -}; -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 -}; -const char *usagestring = NULL; - -static char *service, *runlevel, *ibsave, *prefix; -static RC_DEPTREE *deptree; -static RC_STRINGLIST *applet_list, *services, *tmplist; -static RC_STRINGLIST *restart_services; -static RC_STRINGLIST *need_services; -static RC_STRINGLIST *use_services; -static RC_STRINGLIST *want_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 *deptypes_b; /* broken deps */ -static RC_STRINGLIST *deptypes_n; /* needed deps */ -static RC_STRINGLIST *deptypes_nw; /* need+want deps */ -static RC_STRINGLIST *deptypes_nwu; /* need+want+use deps */ -static RC_STRINGLIST *deptypes_nwua; /* need+want+use+after deps */ -static RC_STRINGLIST *deptypes_m; /* needed deps for stopping */ -static RC_STRINGLIST *deptypes_mwua; /* need+want+use+after deps for stopping */ - -static void -handle_signal(int sig) -{ - int serrno = errno; - char *signame = NULL; - 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) - xasprintf(&signame, "SIGINT"); - /* FALLTHROUGH */ - case SIGTERM: - if (!signame) - xasprintf(&signame, "SIGTERM"); - /* FALLTHROUGH */ - case SIGQUIT: - if (!signame) - xasprintf(&signame, "SIGQUIT"); - /* Send the signal to our children too */ - if (service_pid > 0) - kill(service_pid, sig); - eerror("%s: caught %s, aborting", applet, signame); - free(signame); - exit(EXIT_FAILURE); - /* NOTREACHED */ - - default: - eerror("%s: caught unknown signal %d", applet, sig); - } - - /* Restore errno */ - errno = serrno; -} - -static void -unhotplug() -{ - char *file = NULL; - - xasprintf(&file, RC_SVCDIR "/hotplugged/%s", applet); - if (exists(file) && unlink(file) != 0) - eerror("%s: unlink `%s': %s", applet, file, strerror(errno)); - free(file); -} - -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(); - - rc_stringlist_free(deptypes_b); - rc_stringlist_free(deptypes_n); - rc_stringlist_free(deptypes_nw); - rc_stringlist_free(deptypes_nwu); - rc_stringlist_free(deptypes_nwua); - rc_stringlist_free(deptypes_m); - rc_stringlist_free(deptypes_mwua); - rc_deptree_free(deptree); - rc_stringlist_free(restart_services); - rc_stringlist_free(need_services); - rc_stringlist_free(use_services); - rc_stringlist_free(want_services); - rc_stringlist_free(services); - rc_stringlist_free(applet_list); - rc_stringlist_free(tmplist); - free(ibsave); - free(service); - free(prefix); - free(runlevel); -} - -/* 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 "/openrc-run.sh")) { - if (arg2) - einfov("Executing: %s %s %s %s %s", - RC_SVCDIR "/openrc-run.sh", RC_SVCDIR "/openrc-run.sh", - service, arg1, arg2); - else - einfov("Executing: %s %s %s %s", - RC_SVCDIR "/openrc-run.sh", RC_SVCDIR "/openrc-run.sh", - service, arg1); - execl(RC_SVCDIR "/openrc-run.sh", - RC_SVCDIR "/openrc-run.sh", - service, arg1, arg2, (char *) NULL); - eerror("%s: exec `" RC_SVCDIR "/openrc-run.sh': %s", - service, strerror(errno)); - _exit(EXIT_FAILURE); - } else { - if (arg2) - einfov("Executing: %s %s %s %s %s", - RC_LIBEXECDIR "/sh/openrc-run.sh", - RC_LIBEXECDIR "/sh/openrc-run.sh", - service, arg1, arg2); - else - einfov("Executing: %s %s %s %s", - RC_LIBEXECDIR "/sh/openrc-run.sh", - RC_LIBEXECDIR "/sh/openrc-run.sh", - service, arg1); - execl(RC_LIBEXECDIR "/sh/openrc-run.sh", - RC_LIBEXECDIR "/sh/openrc-run.sh", - service, arg1, arg2, (char *) NULL); - eerror("%s: exec `" RC_LIBEXECDIR "/sh/openrc-run.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 = NULL; - 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); - - xasprintf(&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); - free(file); - return true; - } - close(fd); - } - if (errno == ENOENT) { - free(file); - return true; - } - if (errno != EWOULDBLOCK) { - eerror("%s: open `%s': %s", applet, file, - strerror(errno)); - free(file); - exit(EXIT_FAILURE); - } - if (nanosleep(&interval, NULL) == -1) { - if (errno != EINTR) - goto finish; - } - if (!forever) { - timespecsub(&timeout, &interval, &timeout); - if (timeout.tv_sec <= 0) - goto finish; - 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; - } - } - } -finish: - free(file); - 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_deptypes(void) -{ - deptypes_b = rc_stringlist_new(); - rc_stringlist_add(deptypes_b, "broken"); - - deptypes_n = rc_stringlist_new(); - rc_stringlist_add(deptypes_n, "ineed"); - - deptypes_nw = rc_stringlist_new(); - rc_stringlist_add(deptypes_nw, "ineed"); - rc_stringlist_add(deptypes_nw, "iwant"); - - deptypes_nwu = rc_stringlist_new(); - rc_stringlist_add(deptypes_nwu, "ineed"); - rc_stringlist_add(deptypes_nwu, "iwant"); - rc_stringlist_add(deptypes_nwu, "iuse"); - - deptypes_nwua = rc_stringlist_new(); - rc_stringlist_add(deptypes_nwua, "ineed"); - rc_stringlist_add(deptypes_nwua, "iwant"); - rc_stringlist_add(deptypes_nwua, "iuse"); - rc_stringlist_add(deptypes_nwua, "iafter"); - - deptypes_m = rc_stringlist_new(); - rc_stringlist_add(deptypes_m, "needsme"); - - deptypes_mwua = rc_stringlist_new(); - rc_stringlist_add(deptypes_mwua, "needsme"); - rc_stringlist_add(deptypes_mwua, "wantsme"); - rc_stringlist_add(deptypes_mwua, "usesme"); - rc_stringlist_add(deptypes_mwua, "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 (!deptypes_b) - setup_deptypes(); - - services = rc_deptree_depends(deptree, deptypes_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, deptypes_n, - applet_list, runlevel, depoptions); - want_services = rc_deptree_depends(deptree, deptypes_nw, - applet_list, runlevel, depoptions); - use_services = rc_deptree_depends(deptree, deptypes_nwu, - 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, deptypes_nwua, 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(want_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 (!deptypes_m) - setup_deptypes(); - - services = rc_deptree_depends(deptree, deptypes_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, deptypes_mwua, 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; - } - } - free(list); - return allow; -} - -int main(int argc, char **argv) -{ - bool doneone = false; - bool runscript = false; - int retval, opt, depoptions = RC_DEP_TRACE; - RC_STRING *svc; - char *path = NULL; - char *lnk = NULL; - char *dir, *save = NULL, *saveLnk = NULL; - char *pidstr = NULL; - 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); - } - - applet = basename_c(argv[0]); - if (strcmp(applet, "runscript") == 0) - runscript = true; - - 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. */ - path = realpath(argv[1], NULL); - if (!path) { - fprintf(stderr, "realpath: %s\n", strerror(errno)); - exit(EXIT_FAILURE); - } - lnk = xmalloc(4096); - memset(lnk, 0, 4096); - if (readlink(argv[1], lnk, 4096-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; - xasprintf(&service, "%s/%s", dir, file); - if (stat(service, &stbuf) != 0) { - free(service); - service = xstrdup(lnk); - } - free(save); - free(saveLnk); - } - free(lnk); - 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 */ - xasprintf(&pidstr, "%d", (int) getpid()); - setenv("RC_OPENRC_PID", pidstr, 1); - /* - * RC_RUNSCRIPT_PID is deprecated, but we will keep it for a while - * for safety. - */ - 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); - memcpy(prefix, applet, ll); - memset(prefix + ll, ' ', l - ll); - memset(prefix + l, 0, 1); - eprefix(prefix); - } - - /* Ok, we are ready to go, so setup selinux if applicable */ - selinux_setup(argv); - - 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 (rc_yesno(getenv("RC_NODEPS"))) - deps = false; - - /* 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_DRYRUN"))) - dry_run = true; - 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); - - if (runscript) - ewarn("%s uses runscript, please convert to openrc-run.", service); - - /* 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, "iwant") == 0 || - strcmp(optarg, "needsme") == 0 || - strcmp(optarg, "usesme") == 0 || - strcmp(optarg, "wantsme") == 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); - tmplist = NULL; - 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; -} diff --git a/src/rc/openrc-shutdown.c b/src/rc/openrc-shutdown.c deleted file mode 100644 index 750946f5..00000000 --- a/src/rc/openrc-shutdown.c +++ /dev/null @@ -1,358 +0,0 @@ -/* - * openrc-shutdown.c - * If you are using OpenRC's provided init, this will shut down or - * reboot your system. - * - * This is based on code written by James Hammons , so - * I would like to publically thank him for his work. - */ - -/* - * Copyright 2017 The OpenRC Authors. - * See the Authors file at the top-level directory of this distribution and - * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS - * - * This file is part of OpenRC. It is subject to the license terms in - * the LICENSE file found in the top-level directory of this - * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE - * This file may not be copied, modified, propagated, or distributed - * except according to the terms contained in the LICENSE file. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "broadcast.h" -#include "einfo.h" -#include "rc.h" -#include "helpers.h" -#include "rc-misc.h" -#include "rc-sysvinit.h" -#include "rc-wtmp.h" -#include "_usage.h" - -const char *applet = NULL; -const char *extraopts = NULL; -const char getoptstring[] = "cdDfFHKpRrsw" getoptstring_COMMON; -const struct option longopts[] = { - { "cancel", no_argument, NULL, 'c'}, - { "no-write", no_argument, NULL, 'd'}, - { "dry-run", no_argument, NULL, 'D'}, - { "halt", no_argument, NULL, 'H'}, - { "kexec", no_argument, NULL, 'K'}, - { "poweroff", no_argument, NULL, 'p'}, - { "reexec", no_argument, NULL, 'R'}, - { "reboot", no_argument, NULL, 'r'}, - { "single", no_argument, NULL, 's'}, - { "write-only", no_argument, NULL, 'w'}, - longopts_COMMON -}; -const char * const longopts_help[] = { - "cancel a pending shutdown", - "do not write wtmp record", - "print actions instead of executing them", - "halt the system", - "reboot the system using kexec", - "power off the system", - "re-execute init (use after upgrading)", - "reboot the system", - "single user mode", - "write wtmp boot record and exit", - longopts_help_COMMON -}; -const char *usagestring = "" \ - "Usage: openrc-shutdown -c | --cancel\n" \ - " or: openrc-shutdown -R | --reexec\n" \ - " or: openrc-shutdown -w | --write-only\n" \ - " or: openrc-shutdown -H | --halt time\n" \ - " or: openrc-shutdown -K | --kexec time\n" \ - " or: openrc-shutdown -p | --poweroff time\n" \ - " or: openrc-shutdown -r | --reboot time\n" \ - " or: openrc-shutdown -s | --single time"; -const char *exclusive = "Select one of " - "--cancel, --halt, --kexec, --poweroff, --reexec, --reboot, --single or \n" - "--write-only"; -const char *nologin_file = RC_SYSCONFDIR"/nologin"; -const char *shutdown_pid = "/run/openrc-shutdown.pid"; - -static bool do_cancel = false; -static bool do_dryrun = false; -static bool do_halt = false; -static bool do_kexec = false; -static bool do_poweroff = false; -static bool do_reboot = false; -static bool do_reexec = false; -static bool do_single = false; -static bool do_wtmp = true; -static bool do_wtmp_only = false; - -static void cancel_shutdown(void) -{ - pid_t pid; - - pid = get_pid(applet, shutdown_pid); - if (pid <= 0) - eerrorx("%s: Unable to cancel shutdown", applet); - - if (kill(pid, SIGTERM) != -1) - einfo("%s: shutdown canceled", applet); - else - eerrorx("%s: Unable to cancel shutdown", applet); -} - -/* - * Create the nologin file. - */ -static void create_nologin(int mins) -{ - FILE *fp; - time_t t; - - time(&t); - t += 60 * mins; - - if ((fp = fopen(nologin_file, "w")) != NULL) { - fprintf(fp, "\rThe system is going down on %s\r\n", ctime(&t)); - fclose(fp); - } -} - -/* - * Send a command to our init - */ -static void send_cmd(const char *cmd) -{ - FILE *fifo; - size_t ignored; - - if (do_dryrun) { - einfo("Would send %s to init", cmd); - return; - } - if (do_wtmp && (do_halt || do_kexec || do_reboot || do_poweroff)) - log_wtmp("shutdown", "~~", 0, RUN_LVL, "~~"); - fifo = fopen(RC_INIT_FIFO, "w"); - if (!fifo) { - perror("fopen"); - return; - } - - ignored = fwrite(cmd, 1, strlen(cmd), fifo); - if (ignored != strlen(cmd)) - printf("Error writing to init fifo\n"); - fclose(fifo); -} - -/* - * sleep without being interrupted. - * The idea for this code came from sysvinit. - */ -static void sleep_no_interrupt(int seconds) -{ - struct timespec duration; - struct timespec remaining; - - duration.tv_sec = seconds; - duration.tv_nsec = 0; - - while (nanosleep(&duration, &remaining) < 0 && errno == EINTR) - duration = remaining; -} - -static void stop_shutdown(int sig) -{ - (void) sig; - unlink(nologin_file); - unlink(shutdown_pid); -einfo("Shutdown canceled"); -exit(0); -} - -int main(int argc, char **argv) -{ - char *ch = NULL; - int opt; - int cmd_count = 0; - int hour = 0; - int min = 0; - int shutdown_delay = 0; - struct sigaction sa; - struct tm *lt; - time_t tv; - bool need_warning = false; - char *msg = NULL; - char *state = NULL; - char *time_arg = NULL; - FILE *fp; - - applet = basename_c(argv[0]); - while ((opt = getopt_long(argc, argv, getoptstring, - longopts, (int *) 0)) != -1) - { - switch (opt) { - case 'c': - do_cancel = true; - cmd_count++; - break; - case 'd': - do_wtmp = false; - break; - case 'D': - do_dryrun = true; - break; - case 'H': - do_halt = true; - xasprintf(&state, "%s", "halt"); - cmd_count++; - break; - case 'K': - do_kexec = true; - xasprintf(&state, "%s", "reboot"); - cmd_count++; - break; - case 'p': - do_poweroff = true; - xasprintf(&state, "%s", "power off"); - cmd_count++; - break; - case 'R': - do_reexec = true; - cmd_count++; - break; - case 'r': - do_reboot = true; - xasprintf(&state, "%s", "reboot"); - cmd_count++; - break; - case 's': - do_single = true; - xasprintf(&state, "%s", "go down for maintenance"); - cmd_count++; - break; - case 'w': - do_wtmp_only = true; - cmd_count++; - break; - case_RC_COMMON_GETOPT - } - } - if (geteuid() != 0) - eerrorx("%s: you must be root\n", applet); - if (cmd_count != 1) { - eerror("%s: %s\n", applet, exclusive); - usage(EXIT_FAILURE); - } - - if (do_cancel) { - cancel_shutdown(); - exit(EXIT_SUCCESS); - } else if (do_reexec) { - send_cmd("reexec"); - exit(EXIT_SUCCESS); - } else if (do_wtmp_only) { - log_wtmp("shutdown", "~~", 0, RUN_LVL, "~~"); - exit(EXIT_SUCCESS); - } - - if (optind >= argc) { - eerror("%s: No shutdown time specified", applet); - usage(EXIT_FAILURE); - } - time_arg = argv[optind]; - if (*time_arg == '+') - time_arg++; - if (strcasecmp(time_arg, "now") == 0) - strcpy(time_arg, "0"); - for (ch=time_arg; *ch; ch++) - if ((*ch < '0' || *ch > '9') && *ch != ':') { - eerror("%s: invalid time %s", applet, time_arg); - usage(EXIT_FAILURE); - } - if (strchr(time_arg, ':')) { - if ((sscanf(time_arg, "%2d:%2d", &hour, &min) != 2) || - (hour > 23) || (min > 59)) { - eerror("%s: invalid time %s", applet, time_arg); - usage(EXIT_FAILURE); - } - time(&tv); - lt = localtime(&tv); - shutdown_delay = (hour * 60 + min) - (lt->tm_hour * 60 + lt->tm_min); - if (shutdown_delay < 0) - shutdown_delay += 1440; - } else { - shutdown_delay = atoi(time_arg); - } - - fp = fopen(shutdown_pid, "w"); - if (!fp) - eerrorx("%s: fopen `%s': %s", applet, shutdown_pid, strerror(errno)); - fprintf(fp, "%d\n", getpid()); - fclose(fp); - - openlog(applet, LOG_PID, LOG_DAEMON); - memset(&sa, 0, sizeof(sa)); - sa.sa_handler = stop_shutdown; - sigemptyset(&sa.sa_mask); - sigaction(SIGINT, &sa, NULL); - sigaction(SIGTERM, &sa, NULL); - while (shutdown_delay > 0) { - need_warning = false; - if (shutdown_delay > 180) - need_warning = (shutdown_delay % 60 == 0); - else if (shutdown_delay > 60) - need_warning = (shutdown_delay % 30 == 0); - else if (shutdown_delay > 10) - need_warning = (shutdown_delay % 15 == 0); - else - need_warning = true; - if (shutdown_delay <= 5) - create_nologin(shutdown_delay); - if (need_warning) { - xasprintf(&msg, "\rThe system will %s in %d minutes\r\n", - state, shutdown_delay); - broadcast(msg); - free(msg); - } - sleep_no_interrupt(60); - shutdown_delay--; - } - xasprintf(&msg, "\rThe system will %s now\r\n", state); - broadcast(msg); - syslog(LOG_NOTICE, "The system will %s now", state); - unlink(nologin_file); - unlink(shutdown_pid); - if (do_halt) { - if (exists("/run/initctl")) { - sysvinit_setenv("INIT_HALT", "HALT"); - sysvinit_runlevel('0'); - } else - send_cmd("halt"); - } else if (do_kexec) - send_cmd("kexec"); - else if (do_poweroff) { - if (exists("/run/initctl")) { - sysvinit_setenv("INIT_HALT", "POWEROFF"); - sysvinit_runlevel('0'); - } else - send_cmd("poweroff"); - } else if (do_reboot) { - if (exists("/run/initctl")) - sysvinit_runlevel('6'); - else - send_cmd("reboot"); - } else if (do_single) { - if (exists("/run/initctl")) - sysvinit_runlevel('S'); - else - send_cmd("single"); - } - return 0; -} diff --git a/src/rc/rc-abort.c b/src/rc/rc-abort.c deleted file mode 100644 index 39a22c94..00000000 --- a/src/rc/rc-abort.c +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2007-2015 The OpenRC Authors. - * See the Authors file at the top-level directory of this distribution and - * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS - * - * This file is part of OpenRC. It is subject to the license terms in - * the LICENSE file found in the top-level directory of this - * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE - * This file may not be copied, modified, propagated, or distributed - * except according to the terms contained in the LICENSE file. - */ - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "einfo.h" - -int main(void) -{ - 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; -} diff --git a/src/rc/rc-depend.c b/src/rc/rc-depend.c deleted file mode 100644 index 80754872..00000000 --- a/src/rc/rc-depend.c +++ /dev/null @@ -1,177 +0,0 @@ -/* - * rc-depend - * rc service dependency and ordering - */ - -/* - * Copyright (c) 2007-2015 The OpenRC Authors. - * See the Authors file at the top-level directory of this distribution and - * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS - * - * This file is part of OpenRC. It is subject to the license terms in - * the LICENSE file found in the top-level directory of this - * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE - * This file may not be copied, modified, propagated, or distributed - * except according to the terms contained in the LICENSE file. - */ - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "einfo.h" -#include "queue.h" -#include "rc.h" -#include "rc-misc.h" -#include "_usage.h" - -const char *applet = NULL; -const char *extraopts = NULL; -const char getoptstring[] = "aot:suTF:" getoptstring_COMMON; -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 -}; -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 -}; -const char *usagestring = NULL; - -int main(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; - - applet = basename_c(argv[0]); - 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 4b1b9189..00000000 --- a/src/rc/rc-logger.c +++ /dev/null @@ -1,314 +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-2015 The OpenRC Authors. - * See the Authors file at the top-level directory of this distribution and - * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS - * - * This file is part of OpenRC. It is subject to the license terms in - * the LICENSE file found in the top-level directory of this - * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE - * This file may not be copied, modified, propagated, or distributed - * except according to the terms contained in the LICENSE file. - */ - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if defined(__linux__) || (defined(__FreeBSD_kernel__) && defined(__GLIBC__)) \ - || defined(__GNU__) -# include -#elif defined(__NetBSD__) || defined(__OpenBSD__) -# include -#else -# include -#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 (!isprint((int) *p) && *p != '\n') - goto cont; - 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 (!strcmp(logfile, TMPLOG)) { - eerror("Cowardly refusing to concatenate a logfile into itself."); - eerrorx("Please change rc_log_path to something other than %s to get rid of this message", TMPLOG); - } - - 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; - } - } - fclose(log); - } else { - log_error = 1; - eerror("Error: fopen(%s) failed: %s", TMPLOG, strerror(errno)); - } - - 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 52ca9717..00000000 --- a/src/rc/rc-logger.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2007-2015 The OpenRC Authors. - * See the Authors file at the top-level directory of this distribution and - * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS - * - * This file is part of OpenRC. It is subject to the license terms in - * the LICENSE file found in the top-level directory of this - * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE - * This file may not be copied, modified, propagated, or distributed - * except according to the terms contained in the LICENSE file. - */ - -#ifndef RC_LOGGER_H -#define RC_LOGGER_H - -extern pid_t rc_logger_pid; -extern 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 1ca51e72..00000000 --- a/src/rc/rc-misc.c +++ /dev/null @@ -1,492 +0,0 @@ -/* - * rc-misc.c - * rc misc functions - */ - -/* - * Copyright (c) 2007-2015 The OpenRC Authors. - * See the Authors file at the top-level directory of this distribution and - * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS - * - * This file is part of OpenRC. It is subject to the license terms in - * the LICENSE file found in the top-level directory of this - * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE - * This file may not be copied, modified, propagated, or distributed - * except according to the terms contained in the LICENSE file. - */ - -#include -#include -#include - -#ifdef __linux__ -# include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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[] = { - "EERROR_QUIET", "EINFO_QUIET", - "IN_BACKGROUND", "IN_DRYRUN", "IN_HOTPLUG", - "RC_DEBUG", "RC_NODEPS", - "LANG", "LC_MESSAGES", "TERM", - "EINFO_COLOR", "EINFO_VERBOSE", - NULL -}; - -void -env_filter(void) -{ - RC_STRINGLIST *env_allow; - RC_STRINGLIST *profile; - RC_STRINGLIST *env_list; - RC_STRING *env; - char *e; - size_t i = 0; - - /* Add the user defined list of vars */ - env_allow = rc_stringlist_split(rc_conf_value("rc_env_allow"), " "); - /* - * If '*' is an entry in rc_env_allow, do nothing as we are to pass - * through all environment variables. - */ - if (rc_stringlist_find(env_allow, "*")) - return; - profile = rc_config_load(RC_PROFILE_ENV); - - /* Copy the env and work from this so we can manipulate it safely */ - env_list = rc_stringlist_new(); - while (environ && environ[i]) { - env = rc_stringlist_add(env_list, environ[i++]); - e = strchr(env->value, '='); - if (e) - *e = '\0'; - } - - TAILQ_FOREACH(env, env_list, entries) { - /* Check the whitelist */ - for (i = 0; env_whitelist[i]; i++) { - if (strcmp(env_whitelist[i], env->value) == 0) - break; - } - if (env_whitelist[i]) - continue; - - /* Check our user defined list */ - if (rc_stringlist_find(env_allow, env->value)) - continue; - - /* OK, not allowed! */ - unsetenv(env->value); - } - - /* Now add anything missing from the profile */ - TAILQ_FOREACH(env, profile, entries) { - e = strchr(env->value, '='); - *e = '\0'; - if (!getenv(env->value)) - setenv(env->value, e + 1, 1); - } - - rc_stringlist_free(env_list); - rc_stringlist_free(env_allow); - rc_stringlist_free(profile); -} - -void -env_config(void) -{ - size_t pplen = strlen(RC_PATH_PREFIX); - char *path; - char *p; - char *e; - size_t l; - struct utsname uts; - FILE *fp; - char *token; - char *np; - char *npp; - char *tok; - const char *sys = rc_sys(); - char *buffer = NULL; - size_t size = 0; - - /* Ensure our PATH is prefixed with the system locations first - for a little extra security */ - path = getenv("PATH"); - if (!path) - setenv("PATH", RC_PATH_PREFIX, 1); - else if (strncmp (RC_PATH_PREFIX, path, pplen) != 0) { - l = strlen(path) + pplen + 3; - e = p = xmalloc(sizeof(char) * l); - p += snprintf(p, l, "%s", RC_PATH_PREFIX); - - /* Now go through the env var and only add bits not in our - * PREFIX */ - while ((token = strsep(&path, ":"))) { - np = npp = xstrdup(RC_PATH_PREFIX); - while ((tok = strsep(&npp, ":"))) - if (strcmp(tok, token) == 0) - break; - if (!tok) - p += snprintf(p, l - (p - e), ":%s", token); - free (np); - } - *p++ = '\0'; - unsetenv("PATH"); - setenv("PATH", e, 1); - free(e); - } - - setenv("RC_VERSION", VERSION, 1); - setenv("RC_LIBEXECDIR", RC_LIBEXECDIR, 1); - setenv("RC_SVCDIR", RC_SVCDIR, 1); - setenv("RC_TMPDIR", RC_SVCDIR "/tmp", 1); - setenv("RC_BOOTLEVEL", RC_LEVEL_BOOT, 1); - e = rc_runlevel_get(); - setenv("RC_RUNLEVEL", e, 1); - free(e); - - if ((fp = fopen(RC_KRUNLEVEL, "r"))) { - if (getline(&buffer, &size, fp) != -1) { - l = strlen (buffer) - 1; - if (buffer[l] == '\n') - buffer[l] = 0; - setenv("RC_DEFAULTLEVEL", buffer, 1); - } - fclose(fp); - } else - setenv("RC_DEFAULTLEVEL", RC_LEVEL_DEFAULT, 1); - - free(buffer); - if (sys) - setenv("RC_SYS", sys, 1); - -#ifdef PREFIX - setenv("RC_PREFIX", RC_PREFIX, 1); -#endif - - /* Some scripts may need to take a different code path if - Linux/FreeBSD, etc - To save on calling uname, we store it in an environment variable */ - if (uname(&uts) == 0) - setenv("RC_UNAME", uts.sysname, 1); - - /* Be quiet or verbose as necessary */ - if (rc_conf_yesno("rc_quiet")) - setenv("EINFO_QUIET", "YES", 1); - if (rc_conf_yesno("rc_verbose")) - setenv("EINFO_VERBOSE", "YES", 1); - - errno = 0; - if ((!rc_conf_yesno("rc_color") && errno == 0) || - rc_conf_yesno("rc_nocolor")) - setenv("EINFO_COLOR", "NO", 1); -} - -int -signal_setup(int sig, void (*handler)(int)) -{ - struct sigaction sa; - - memset(&sa, 0, sizeof (sa)); - sigemptyset(&sa.sa_mask); - sa.sa_handler = handler; - return sigaction(sig, &sa, NULL); -} - -int -signal_setup_restart(int sig, void (*handler)(int)) -{ - struct sigaction sa; - - memset(&sa, 0, sizeof (sa)); - sigemptyset(&sa.sa_mask); - sa.sa_handler = handler; - sa.sa_flags = SA_RESTART; - return sigaction(sig, &sa, NULL); -} - -int -svc_lock(const char *applet) -{ - char *file = NULL; - int fd; - - xasprintf(&file, RC_SVCDIR "/exclusive/%s", applet); - fd = open(file, O_WRONLY | O_CREAT | O_NONBLOCK, 0664); - free(file); - if (fd == -1) - return -1; - if (flock(fd, LOCK_EX | LOCK_NB) == -1) { - eerror("Call to flock failed: %s", strerror(errno)); - close(fd); - return -1; - } - return fd; -} - -int -svc_unlock(const char *applet, int fd) -{ - char *file = NULL; - - xasprintf(&file, RC_SVCDIR "/exclusive/%s", applet); - close(fd); - unlink(file); - free(file); - return -1; -} - -pid_t -exec_service(const char *service, const char *arg) -{ - char *file, sfd[32]; - int fd; - pid_t pid = -1; - sigset_t full; - sigset_t old; - struct sigaction sa; - - fd = svc_lock(basename_c(service)); - if (fd == -1) - return -1; - - file = rc_service_resolve(service); - if (!exists(file)) { - rc_service_mark(service, RC_SERVICE_STOPPED); - svc_unlock(basename_c(service), fd); - free(file); - return 0; - } - snprintf(sfd, sizeof(sfd), "%d", fd); - - /* We need to block signals until we have forked */ - memset(&sa, 0, sizeof (sa)); - sa.sa_handler = SIG_DFL; - sigemptyset(&sa.sa_mask); - sigfillset(&full); - sigprocmask(SIG_SETMASK, &full, &old); - - if ((pid = fork()) == 0) { - /* Restore default handlers */ - sigaction(SIGCHLD, &sa, NULL); - sigaction(SIGHUP, &sa, NULL); - sigaction(SIGINT, &sa, NULL); - sigaction(SIGQUIT, &sa, NULL); - sigaction(SIGTERM, &sa, NULL); - sigaction(SIGUSR1, &sa, NULL); - sigaction(SIGWINCH, &sa, NULL); - - /* Unmask signals */ - sigprocmask(SIG_SETMASK, &old, NULL); - - /* Safe to run now */ - execl(file, file, "--lockfd", sfd, arg, (char *) NULL); - fprintf(stderr, "unable to exec `%s': %s\n", - file, strerror(errno)); - svc_unlock(basename_c(service), fd); - _exit(EXIT_FAILURE); - } - - if (pid == -1) { - fprintf(stderr, "fork: %s\n",strerror (errno)); - svc_unlock(basename_c(service), fd); - } else - fcntl(fd, F_SETFD, fcntl(fd, F_GETFD, 0) | FD_CLOEXEC); - - sigprocmask(SIG_SETMASK, &old, NULL); - free(file); - return pid; -} - -int -parse_mode(mode_t *mode, char *text) -{ - char *p; - unsigned long l; - - /* Check for a numeric mode */ - if ((*text - '0') < 8) { - l = strtoul(text, &p, 8); - if (*p || l > 07777U) { - errno = EINVAL; - return -1; - } - *mode = (mode_t) l; - return 0; - } - - /* We currently don't check g+w type stuff */ - errno = EINVAL; - return -1; -} - -int -is_writable(const char *path) -{ - if (access(path, W_OK) == 0) - return 1; - - return 0; -} - -RC_DEPTREE * _rc_deptree_load(int force, int *regen) -{ - int fd; - int retval; - int serrno = errno; - int merrno; - time_t t; - char file[PATH_MAX]; - struct stat st; - struct utimbuf ut; - FILE *fp; - - t = 0; - if (rc_deptree_update_needed(&t, file) || force != 0) { - /* Test if we have permission to update the deptree */ - fd = open(RC_DEPTREE_CACHE, O_WRONLY); - merrno = errno; - errno = serrno; - if (fd == -1 && merrno == EACCES) - return rc_deptree_load(); - close(fd); - - if (regen) - *regen = 1; - ebegin("Caching service dependencies"); - retval = rc_deptree_update() ? 0 : -1; - eend (retval, "Failed to update the dependency tree"); - - if (retval == 0) { - if (stat(RC_DEPTREE_CACHE, &st) != 0) { - eerror("stat(%s): %s", RC_DEPTREE_CACHE, strerror(errno)); - return NULL; - } - if (st.st_mtime < t) { - eerror("Clock skew detected with `%s'", file); - eerrorn("Adjusting mtime of `" RC_DEPTREE_CACHE - "' to %s", ctime(&t)); - fp = fopen(RC_DEPTREE_SKEWED, "w"); - if (fp != NULL) { - fprintf(fp, "%s\n", file); - fclose(fp); - } - ut.actime = t; - ut.modtime = t; - utime(RC_DEPTREE_CACHE, &ut); - } else { - if (exists(RC_DEPTREE_SKEWED)) - unlink(RC_DEPTREE_SKEWED); - } - } - if (force == -1 && regen != NULL) - *regen = retval; - } - return rc_deptree_load(); -} - -static const struct { - const char * const name; - RC_SERVICE bit; -} service_bits[] = { - { "service_started", RC_SERVICE_STARTED, }, - { "service_stopped", RC_SERVICE_STOPPED, }, - { "service_inactive", RC_SERVICE_INACTIVE, }, - { "service_starting", RC_SERVICE_STARTING, }, - { "service_stopping", RC_SERVICE_STOPPING, }, - { "service_hotplugged", RC_SERVICE_HOTPLUGGED, }, - { "service_wasinactive", RC_SERVICE_WASINACTIVE, }, - { "service_failed", RC_SERVICE_FAILED, }, - { "service_crashed", RC_SERVICE_CRASHED, }, -}; - -RC_SERVICE lookup_service_state(const char *service) -{ - size_t i; - for (i = 0; i < ARRAY_SIZE(service_bits); ++i) - if (!strcmp(service, service_bits[i].name)) - return service_bits[i].bit; - return 0; -} - -void from_time_t(char *time_string, time_t tv) -{ - strftime(time_string, 20, "%Y-%m-%d %H:%M:%S", localtime(&tv)); -} - -time_t to_time_t(char *timestring) -{ - int check = 0; - int year = 0; - int month = 0; - int day = 0; - int hour = 0; - int min = 0; - int sec = 0; - struct tm breakdown = {0}; - time_t result = -1; - - check = sscanf(timestring, "%4d-%2d-%2d %2d:%2d:%2d", - &year, &month, &day, &hour, &min, &sec); - if (check == 6) { - breakdown.tm_year = year - 1900; /* years since 1900 */ - breakdown.tm_mon = month - 1; - breakdown.tm_mday = day; - breakdown.tm_hour = hour; - breakdown.tm_min = min; - breakdown.tm_sec = sec; - breakdown.tm_isdst = -1; - result = mktime(&breakdown); - } - return result; -} - -pid_t get_pid(const char *applet,const char *pidfile) -{ - FILE *fp; - pid_t pid; - - if (!pidfile) - return -1; - - if ((fp = fopen(pidfile, "r")) == NULL) { - ewarnv("%s: fopen `%s': %s", applet, pidfile, strerror(errno)); - return -1; - } - - if (fscanf(fp, "%d", &pid) != 1) { - ewarnv("%s: no pid found in `%s'", applet, pidfile); - fclose(fp); - return -1; - } - - fclose(fp); - - return pid; -} diff --git a/src/rc/rc-pipes.c b/src/rc/rc-pipes.c deleted file mode 100644 index b4e60e03..00000000 --- a/src/rc/rc-pipes.c +++ /dev/null @@ -1,56 +0,0 @@ -/* - * rc-pipes.c - * Helper to handle spawning processes and connecting them to pipes. - */ - -/* - * Copyright (c) 2018 The OpenRC Authors. - * See the Authors file at the top-level directory of this distribution and - * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS - * - * This file is part of OpenRC. It is subject to the license terms in - * the LICENSE file found in the top-level directory of this - * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE - * This file may not be copied, modified, propagated, or distributed - * except according to the terms contained in the LICENSE file. - */ - -#include -#include -#include - -#include "rc-pipes.h" - -static const int pipe_read_end = 0; -static const int pipe_write_end = 1; - -/* - * Starts a command with stdin redirected from a pipe - * Returns the write end of the pipe or -1 - */ -int rc_pipe_command(char *cmd) -{ - int pfd[2]; - pid_t pid; - - if (pipe(pfd) < 0) - return -1; - - pid = fork(); - if (pid > 0) { - /* parent */ - close(pfd[pipe_read_end]); - return pfd[pipe_write_end]; - } else if (pid == 0) { - /* child */ - close(pfd[pipe_write_end]); - if (pfd[pipe_read_end] != STDIN_FILENO) { - if (dup2(pfd[pipe_read_end], STDIN_FILENO) < 0) - exit(1); - close(pfd[pipe_read_end]); - } - execl("/bin/sh", "sh", "-c", cmd, NULL); - exit(1); - } - return -1; -} diff --git a/src/rc/rc-pipes.h b/src/rc/rc-pipes.h deleted file mode 100644 index 861963b7..00000000 --- a/src/rc/rc-pipes.h +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright (c) 2018 The OpenRC Authors. - * See the Authors file at the top-level directory of this distribution and - * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS - * - * This file is part of OpenRC. It is subject to the license terms in - * the LICENSE file found in the top-level directory of this - * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE - * This file may not be copied, modified, propagated, or distributed - * except according to the terms contained in the LICENSE file. - */ - -#ifndef RC_PIPES_H -#define RC_PIPES_H - -int rc_pipe_command(char *cmd); - -#endif diff --git a/src/rc/rc-plugin.c b/src/rc/rc-plugin.c deleted file mode 100644 index 76995866..00000000 --- a/src/rc/rc-plugin.c +++ /dev/null @@ -1,245 +0,0 @@ -/* - * rc-plugin.c - * Simple plugin handler - */ - -/* - * Copyright (c) 2007-2015 The OpenRC Authors. - * See the Authors file at the top-level directory of this distribution and - * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS - * - * This file is part of OpenRC. It is subject to the license terms in - * the LICENSE file found in the top-level directory of this - * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE - * This file may not be copied, modified, propagated, or distributed - * except according to the terms contained in the LICENSE file. - */ - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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 = NULL; - void *h; - int (*fptr)(RC_HOOK, const char *); - - /* Don't load plugins if we're in one */ - if (rc_in_plugin) - return; - - TAILQ_INIT(&plugins); - - if (!(dp = opendir(RC_PLUGINDIR))) - return; - - while ((d = readdir(dp))) { - if (d->d_name[0] == '.') - continue; - - xasprintf(&file, RC_PLUGINDIR "/%s", d->d_name); - h = dlopen(file, RTLD_LAZY); - free(file); - if (h == NULL) { - eerror("dlopen: %s", dlerror()); - continue; - } - - fptr = (int (*)(RC_HOOK, const char *)) - dlfunc(h, RC_PLUGIN_HOOK); - if (fptr == NULL) { - eerror("%s: cannot find symbol `%s'", - d->d_name, RC_PLUGIN_HOOK); - dlclose(h); - } else { - plugin = xmalloc(sizeof(*plugin)); - plugin->name = xstrdup(d->d_name); - plugin->handle = h; - plugin->hook = fptr; - TAILQ_INSERT_TAIL(&plugins, plugin, entries); - } - } - closedir(dp); -} - -int -rc_waitpid(pid_t pid) -{ - int status; - - while (waitpid(pid, &status, 0) == -1) { - if (errno != EINTR) { - status = -1; - break; - } - } - return status; -} - -void -rc_plugin_run(RC_HOOK hook, const char *value) -{ - PLUGIN *plugin; - struct sigaction sa; - sigset_t empty; - sigset_t full; - sigset_t old; - int i; - int flags; - int pfd[2]; - pid_t pid; - char *buffer; - char *token; - char *p; - ssize_t nr; - int retval; - - /* Don't run plugins if we're in one */ - if (rc_in_plugin) - return; - - /* We need to block signals until we have forked */ - memset(&sa, 0, sizeof(sa)); - sa.sa_handler = SIG_DFL; - sigemptyset(&sa.sa_mask); - sigemptyset(&empty); - sigfillset(&full); - - TAILQ_FOREACH(plugin, &plugins, entries) { - /* We create a pipe so that plugins can affect our environment - * vars, which in turn influence our scripts. */ - if (pipe(pfd) == -1) { - eerror("pipe: %s", strerror(errno)); - return; - } - - /* Stop any scripts from inheriting us. - * This is actually quite important as without this, the splash - * plugin will probably hang when running in silent mode. */ - for (i = 0; i < 2; i++) - if ((flags = fcntl (pfd[i], F_GETFD, 0)) < 0 || - fcntl (pfd[i], F_SETFD, flags | FD_CLOEXEC) < 0) - eerror("fcntl: %s", strerror(errno)); - - sigprocmask(SIG_SETMASK, &full, &old); - - /* We run the plugin in a new process so we never crash - * or otherwise affected by it */ - if ((pid = fork()) == -1) { - eerror("fork: %s", strerror(errno)); - break; - } - - if (pid == 0) { - /* Restore default handlers */ - sigaction(SIGCHLD, &sa, NULL); - sigaction(SIGHUP, &sa, NULL); - sigaction(SIGINT, &sa, NULL); - sigaction(SIGQUIT, &sa, NULL); - sigaction(SIGTERM, &sa, NULL); - sigaction(SIGUSR1, &sa, NULL); - sigaction(SIGWINCH, &sa, NULL); - sigprocmask(SIG_SETMASK, &old, NULL); - - rc_in_plugin = true; - close(pfd[0]); - rc_environ_fd = fdopen(pfd[1], "w"); - retval = plugin->hook(hook, value); - fclose(rc_environ_fd); - rc_environ_fd = NULL; - - /* Just in case the plugin sets this to false */ - rc_in_plugin = true; - exit(retval); - } - - sigprocmask(SIG_SETMASK, &old, NULL); - close(pfd[1]); - buffer = xmalloc(sizeof(char) * BUFSIZ); - memset(buffer, 0, BUFSIZ); - - while ((nr = read(pfd[0], buffer, BUFSIZ)) > 0) { - p = buffer; - while (*p && p - buffer < nr) { - token = strsep(&p, "="); - if (token) { - unsetenv(token); - if (*p) { - setenv(token, p, 1); - p += strlen(p) + 1; - } else - p++; - } - } - } - - free(buffer); - close(pfd[0]); - - rc_waitpid(pid); - } -} - -void -rc_plugin_unload(void) -{ - PLUGIN *plugin = TAILQ_FIRST(&plugins); - PLUGIN *next; - - while (plugin) { - next = TAILQ_NEXT(plugin, entries); - dlclose(plugin->handle); - free(plugin->name); - free(plugin); - plugin = next; - } - TAILQ_INIT(&plugins); -} diff --git a/src/rc/rc-plugin.h b/src/rc/rc-plugin.h deleted file mode 100644 index 8422adec..00000000 --- a/src/rc/rc-plugin.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * rc-plugin.h - * Private instructions to use plugins - */ - -/* - * Copyright (c) 2007-2015 The OpenRC Authors. - * See the Authors file at the top-level directory of this distribution and - * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS - * - * This file is part of OpenRC. It is subject to the license terms in - * the LICENSE file found in the top-level directory of this - * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE - * This file may not be copied, modified, propagated, or distributed - * except according to the terms contained in the LICENSE file. - */ - -#ifndef __LIBRC_PLUGIN_H__ -#define __LIBRC_PLUGIN_H__ - -/* A simple flag to say if we're in a plugin proccess or not. - * Mainly used in atexit code. */ -extern bool rc_in_plugin; - -int rc_waitpid(pid_t pid); -void rc_plugin_load(void); -void rc_plugin_unload(void); -void rc_plugin_run(RC_HOOK, const char *value); - -/* dlfunc defines needed to avoid ISO errors. FreeBSD has this right :) */ -#if !defined(__FreeBSD__) && !defined(__DragonFly__) -struct __dlfunc_arg { - int __dlfunc_dummy; -}; - -typedef void (*dlfunc_t)(struct __dlfunc_arg); - -dlfunc_t dlfunc (void * __restrict handle, const char * __restrict symbol); -#endif - -#endif diff --git a/src/rc/rc-schedules.c b/src/rc/rc-schedules.c deleted file mode 100644 index 5938086d..00000000 --- a/src/rc/rc-schedules.c +++ /dev/null @@ -1,433 +0,0 @@ -/* - * The functions in this file control the stopping of daemons by - * start-stop-daemon and supervise-daemon. - */ - -/* - * Copyright (c) 2015 The OpenRC Authors. - * See the Authors file at the top-level directory of this distribution and - * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS - * - * This file is part of OpenRC. It is subject to the license terms in - * the LICENSE file found in the top-level directory of this - * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE - * This file may not be copied, modified, propagated, or distributed - * except according to the terms contained in the LICENSE file. - */ - -/* nano seconds */ -#define POLL_INTERVAL 20000000 -#define WAIT_PIDFILE 500000000 -#define ONE_SECOND 1000000000 -#define ONE_MS 1000000 - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "einfo.h" -#include "queue.h" -#include "rc.h" -#include "rc-misc.h" -#include "rc-schedules.h" -#include "helpers.h" - -typedef struct scheduleitem { - enum { - SC_TIMEOUT, - SC_SIGNAL, - SC_GOTO, - SC_FOREVER, - } type; - int value; - struct scheduleitem *gotoitem; - TAILQ_ENTRY(scheduleitem) entries; -} SCHEDULEITEM; - -static TAILQ_HEAD(, scheduleitem) schedule; - -void free_schedulelist(void) -{ - SCHEDULEITEM *s1 = TAILQ_FIRST(&schedule); - SCHEDULEITEM *s2; - - while (s1) { - s2 = TAILQ_NEXT(s1, entries); - free(s1); - s1 = s2; - } - TAILQ_INIT(&schedule); -} - -int parse_signal(const char *applet, const char *sig) -{ - typedef struct signalpair - { - const char *name; - int signal; - } SIGNALPAIR; - -#define signalpair_item(name) { #name, SIG##name }, - - static const SIGNALPAIR signallist[] = { - signalpair_item(HUP) - signalpair_item(INT) - signalpair_item(QUIT) - signalpair_item(ILL) - signalpair_item(TRAP) - signalpair_item(ABRT) - signalpair_item(BUS) - signalpair_item(FPE) - signalpair_item(KILL) - signalpair_item(USR1) - signalpair_item(SEGV) - signalpair_item(USR2) - signalpair_item(PIPE) - signalpair_item(ALRM) - signalpair_item(TERM) - signalpair_item(CHLD) - signalpair_item(CONT) - signalpair_item(STOP) - signalpair_item(TSTP) - signalpair_item(TTIN) - signalpair_item(TTOU) - signalpair_item(URG) - signalpair_item(XCPU) - signalpair_item(XFSZ) - signalpair_item(VTALRM) - signalpair_item(PROF) -#ifdef SIGWINCH - signalpair_item(WINCH) -#endif -#ifdef SIGIO - signalpair_item(IO) -#endif -#ifdef SIGPWR - signalpair_item(PWR) -#endif - signalpair_item(SYS) - { "NULL", 0 }, - }; - - unsigned int i = 0; - const char *s; - - if (!sig || *sig == '\0') - return -1; - - if (sscanf(sig, "%u", &i) == 1) { - if (i < NSIG) - return i; - eerrorx("%s: `%s' is not a valid signal", applet, sig); - } - - if (strncmp(sig, "SIG", 3) == 0) - s = sig + 3; - else - s = NULL; - - for (i = 0; i < ARRAY_SIZE(signallist); ++i) - if (strcmp(sig, signallist[i].name) == 0 || - (s && strcmp(s, signallist[i].name) == 0)) - return signallist[i].signal; - - eerrorx("%s: `%s' is not a valid signal", applet, sig); - /* NOTREACHED */ -} - -static SCHEDULEITEM *parse_schedule_item(const char *applet, const char *string) -{ - const char *after_hyph; - int sig; - SCHEDULEITEM *item = xmalloc(sizeof(*item)); - - item->value = 0; - item->gotoitem = NULL; - if (strcmp(string,"forever") == 0) - item->type = SC_FOREVER; - else if (isdigit((unsigned char)string[0])) { - item->type = SC_TIMEOUT; - errno = 0; - if (sscanf(string, "%d", &item->value) != 1) - eerrorx("%s: invalid timeout value in schedule `%s'", - applet, string); - } else if ((after_hyph = string + (string[0] == '-')) && - ((sig = parse_signal(applet, after_hyph)) != -1)) - { - item->type = SC_SIGNAL; - item->value = (int)sig; - } else - eerrorx("%s: invalid schedule item `%s'", applet, string); - - return item; -} - -void parse_schedule(const char *applet, const char *string, int timeout) -{ - char buffer[20]; - const char *slash; - int count = 0; - SCHEDULEITEM *repeatat = NULL; - size_t len; - SCHEDULEITEM *item; - - TAILQ_INIT(&schedule); - if (string) - for (slash = string; *slash; slash++) - if (*slash == '/') - count++; - - free_schedulelist(); - - if (count == 0) { - item = xmalloc(sizeof(*item)); - item->type = SC_SIGNAL; - item->value = timeout; - item->gotoitem = NULL; - TAILQ_INSERT_TAIL(&schedule, item, entries); - - item = xmalloc(sizeof(*item)); - item->type = SC_TIMEOUT; - item->gotoitem = NULL; - TAILQ_INSERT_TAIL(&schedule, item, entries); - if (string) { - if (sscanf(string, "%d", &item->value) != 1) - eerrorx("%s: invalid timeout in schedule", - applet); - } else - item->value = 5; - - return; - } - - while (string != NULL) { - if ((slash = strchr(string, '/'))) - len = slash - string; - else - len = strlen(string); - - if (len >= (ptrdiff_t)sizeof(buffer)) - eerrorx("%s: invalid schedule item, far too long", - applet); - - memcpy(buffer, string, len); - buffer[len] = 0; - string = slash ? slash + 1 : NULL; - - item = parse_schedule_item(applet, buffer); - TAILQ_INSERT_TAIL(&schedule, item, entries); - if (item->type == SC_FOREVER) { - if (repeatat) - eerrorx("%s: invalid schedule, `forever' " - "appears more than once", applet); - - repeatat = item; - continue; - } - } - - if (repeatat) { - item = xmalloc(sizeof(*item)); - item->type = SC_GOTO; - item->value = 0; - item->gotoitem = repeatat; - TAILQ_INSERT_TAIL(&schedule, item, entries); - } - - return; -} - -/* return number of processes killed, -1 on error */ -int do_stop(const char *applet, const char *exec, const char *const *argv, - pid_t pid, uid_t uid,int sig, bool test, bool quiet) -{ - RC_PIDLIST *pids; - RC_PID *pi; - RC_PID *np; - bool killed; - int nkilled = 0; - - if (pid > 0) - pids = rc_find_pids(NULL, NULL, 0, pid); - else - pids = rc_find_pids(exec, argv, uid, 0); - - if (!pids) - return 0; - - LIST_FOREACH_SAFE(pi, pids, entries, np) { - if (test) { - einfo("Would send signal %d to PID %d", sig, pi->pid); - nkilled++; - } else { - if (sig) { - syslog(LOG_DEBUG, "Sending signal %d to PID %d", sig, pi->pid); - if (!quiet) - ebeginv("Sending signal %d to PID %d", sig, pi->pid); - } - errno = 0; - killed = (kill(pi->pid, sig) == 0 || - errno == ESRCH ? true : false); - if (!quiet) - eendv(killed ? 0 : 1, - "%s: failed to send signal %d to PID %d: %s", - applet, sig, pi->pid, strerror(errno)); - else if (!killed) - syslog(LOG_ERR, "Failed to send signal %d to PID %d: %s", - sig, pi->pid, strerror(errno)); - if (!killed) { - nkilled = -1; - } else { - if (nkilled != -1) - nkilled++; - } - } - free(pi); - } - - free(pids); - return nkilled; -} - -int run_stop_schedule(const char *applet, - const char *exec, const char *const *argv, - pid_t pid, uid_t uid, - bool test, bool progress, bool quiet) -{ - SCHEDULEITEM *item = TAILQ_FIRST(&schedule); - int nkilled = 0; - int tkilled = 0; - int nrunning = 0; - long nloops, nsecs; - struct timespec ts; - const char *const *p; - bool progressed = false; - - if (!(pid > 0 || exec || uid || (argv && *argv))) - return 0; - - if (exec) { - einfov("Will stop %s", exec); - syslog(LOG_DEBUG, "Will stop %s", exec); - } - if (pid > 0) { - einfov("Will stop PID %d", pid); - syslog(LOG_DEBUG, "Will stop PID %d", pid); - } - if (uid) { - einfov("Will stop processes owned by UID %d", uid); - syslog(LOG_DEBUG, "Will stop processes owned by UID %d", uid); - } - if (argv && *argv) { - einfovn("Will stop processes of `"); - if (rc_yesno(getenv("EINFO_VERBOSE"))) { - for (p = argv; p && *p; p++) { - if (p != argv) - printf(" "); - printf("%s", *p); - } - printf("'\n"); - } - } - - while (item) { - switch (item->type) { - case SC_GOTO: - item = item->gotoitem; - continue; - - case SC_SIGNAL: - nrunning = 0; - nkilled = do_stop(applet, exec, argv, pid, uid, item->value, test, - quiet); - if (nkilled == 0) { - if (tkilled == 0) { - if (progressed) - printf("\n"); - eerror("%s: no matching processes found", applet); - } - return tkilled; - } - else if (nkilled == -1) - return 0; - - tkilled += nkilled; - break; - case SC_FOREVER: - case SC_TIMEOUT: - if (item->type == SC_TIMEOUT && item->value < 1) { - item = NULL; - break; - } - - ts.tv_sec = 0; - ts.tv_nsec = POLL_INTERVAL; - - for (nsecs = 0; item->type == SC_FOREVER || nsecs < item->value; nsecs++) { - for (nloops = 0; - nloops < ONE_SECOND / POLL_INTERVAL; - nloops++) - { - if ((nrunning = do_stop(applet, exec, argv, - pid, uid, 0, test, quiet)) == 0) - return 0; - - - if (nanosleep(&ts, NULL) == -1) { - if (progressed) { - printf("\n"); - progressed = false; - } - if (errno != EINTR) { - eerror("%s: nanosleep: %s", - applet, strerror(errno)); - return 0; - } - } - } - if (progress) { - printf("."); - fflush(stdout); - progressed = true; - } - } - break; - default: - if (progressed) { - printf("\n"); - progressed = false; - } - eerror("%s: invalid schedule item `%d'", - applet, item->type); - return 0; - } - - if (item) - item = TAILQ_NEXT(item, entries); - } - - if (test || (tkilled > 0 && nrunning == 0)) - return nkilled; - - if (progressed) - printf("\n"); - if (!quiet) { - if (nrunning == 1) - eerror("%s: %d process refused to stop", applet, nrunning); - else - eerror("%s: %d process(es) refused to stop", applet, nrunning); - } - - return -nrunning; -} diff --git a/src/rc/rc-schedules.h b/src/rc/rc-schedules.h deleted file mode 100644 index 5fb9e526..00000000 --- a/src/rc/rc-schedules.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2017 The OpenRC Authors. - * See the Authors file at the top-level directory of this distribution and - * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS - * - * This file is part of OpenRC. It is subject to the license terms in - * the LICENSE file found in the top-level directory of this - * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE - * This file may not be copied, modified, propagated, or distributed - * except according to the terms contained in the LICENSE file. - */ - -#ifndef __RC_SCHEDULES_H -#define __RC_SCHEDULES_H - -void free_schedulelist(void); -int parse_signal(const char *applet, const char *sig); -void parse_schedule(const char *applet, const char *string, int timeout); -int do_stop(const char *applet, const char *exec, const char *const *argv, - pid_t pid, uid_t uid,int sig, bool test, bool quiet); -int run_stop_schedule(const char *applet, - const char *exec, const char *const *argv, - pid_t pid, uid_t uid, - bool test, bool progress, bool quiet); - -#endif diff --git a/src/rc/rc-selinux.c b/src/rc/rc-selinux.c deleted file mode 100644 index 8364a5f0..00000000 --- a/src/rc/rc-selinux.c +++ /dev/null @@ -1,417 +0,0 @@ -/* - * rc-selinux.c - * SELinux helpers to get and set contexts. - */ - -/* - * Copyright (c) 2014-2015 The OpenRC Authors. - * See the Authors file at the top-level directory of this distribution and - * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS - * - * This file is part of OpenRC. It is subject to the license terms in - * the LICENSE file found in the top-level directory of this - * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE - * This file may not be copied, modified, propagated, or distributed - * except according to the terms contained in the LICENSE file. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include - -#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 INITRC_FILE "initrc_context" - -#ifdef HAVE_AUDIT -#include -#endif - -/* PAM or shadow for authentication */ -#ifdef HAVE_PAM -# define PAM_SERVICE_NAME "run_init" /* the name of this program for PAM */ -# include -# include -#else -# define PASSWORD_PROMPT "Password:" -# include -# include -# include -#endif - - -/* The handle for the fcontext lookups */ -static struct selabel_handle *hnd = NULL; - -int selinux_util_label(const char *path) -{ - int retval = 0; - int enforce; - struct stat st; - char *con; - - enforce = security_getenforce(); - if (retval < 0) - return retval; - - if (!hnd) - return (enforce) ? -1 : 0; - - retval = lstat(path, &st); - if (retval < 0) { - if (errno == ENOENT) - return 0; - return (enforce) ? -1 : 0; - } - - /* lookup the context */ - retval = selabel_lookup_raw(hnd, &con, path, st.st_mode); - if (retval < 0) { - if (errno == ENOENT) - return 0; - return (enforce) ? -1 : 0; - } - - /* apply the context */ - retval = lsetfilecon(path, con); - freecon(con); - if (retval < 0) { - if (errno == ENOENT) - return 0; - if (errno == ENOTSUP) - return 0; - return (enforce) ? -1 : 0; - } - - return 0; -} - -/* - * Open the label handle - * returns 1 on success, 0 if no selinux, negative on error - */ -int selinux_util_open(void) -{ - int retval = 0; - - retval = is_selinux_enabled(); - if (retval <= 0) - return retval; - - hnd = selabel_open(SELABEL_CTX_FILE, NULL, 0); - if (!hnd) - return -2; - - return 1; -} - -/* - * Close the label handle - * returns 1 on success, 0 if no selinux, negative on error - */ -int selinux_util_close(void) -{ - int retval = 0; - - retval = is_selinux_enabled(); - if (retval <= 0) - return retval; - - if (hnd) { - selabel_close(hnd); - hnd = NULL; - } - - return 0; -} - -/* - * This will check the users password and return 0 on success or -1 on fail - * - * We ask for the password to make sure it is intended vs run by malicious software. - * Actual authorization is covered by the policy itself. - */ -static int check_password(char *username) -{ - int ret = 1; -#ifdef HAVE_PAM - pam_handle_t *pamh; - int pam_err = 0; - const struct pam_conv pconv = { - misc_conv, - NULL - }; - - pam_err = pam_start(PAM_SERVICE_NAME, username, &pconv, &pamh); - if (pam_err != PAM_SUCCESS) { - ret = -1; - goto outpam; - } - - pam_err = pam_authenticate(pamh, PAM_DISALLOW_NULL_AUTHTOK); - if (pam_err != PAM_SUCCESS) { - ret = -1; - goto outpam; - } - - ret = 0; -outpam: - pam_end(pamh, pam_err); - pamh = NULL; - -#else /* authenticating via /etc/shadow instead */ - struct spwd *spw; - char *password; - char *attempt; - - spw = getspnam(username); - if (!spw) { - eerror("Failed to read shadow entry"); - ret = -1; - goto outshadow; - } - - attempt = getpass(PASSWORD_PROMPT); - if (!attempt) { - ret = -1; - goto outshadow; - } - - if (*spw->sp_pwdp == '\0' && *attempt == '\0') { - ret = -1; - goto outshadow; - } - - /* salt must be at least two characters long */ - if (!(spw->sp_pwdp[0] && spw->sp_pwdp[1])) { - ret = -1; - goto outshadow; - } - - /* encrypt the password attempt */ - password = crypt(attempt, spw->sp_pwdp); - - if (password && strcmp(password, spw->sp_pwdp) == 0) - ret = 0; - else - ret = -1; -outshadow: -#endif - return ret; -} - -/* Authenticates the user, returns 0 on success, 1 on fail */ -static int check_auth() -{ - struct passwd *pw; - uid_t uid; - -#ifdef HAVE_AUDIT - uid = audit_getloginuid(); - if (uid == (uid_t) -1) - uid = getuid(); -#else - uid = getuid(); -#endif - - pw = getpwuid(uid); - if (!pw) { - eerror("cannot find your entry in the passwd file."); - return (-1); - } - - printf("Authenticating %s.\n", pw->pw_name); - - /* do the actual check */ - if (check_password(pw->pw_name) == 0) { - return 0; - } - - eerrorx("Authentication failed for %s", pw->pw_name); - return 1; -} - -/* - * Read the context from the given context file. context must be free'd by the user. - */ -static int read_context_file(const char *filename, char **context) -{ - int ret = -1; - FILE *fp; - char *filepath = NULL; - char *line = NULL; - char *p; - char *p2; - size_t len = 0; - ssize_t read; - - xasprintf(&filepath, "%s/%s", selinux_contexts_path(), filename); - - fp = fopen(filepath, "r"); - if (fp == NULL) { - eerror("Failed to open context file: %s", filename); - free(filepath); - return -1; - } - - while ((read = getline(&line, &len, fp)) != -1) { - /* cut off spaces before the string */ - p = line; - while (isspace(*p) && *p != '\0') - p++; - - /* empty string, skip */ - if (*p == '\0') - continue; - - /* cut off spaces after the string */ - p2 = p; - while (!isspace(*p2) && *p2 != '\0') - p2++; - *p2 = '\0'; - - *context = xstrdup(p); - ret = 0; - break; - } - - free(line); - free(filepath); - fclose(fp); - return ret; -} - -static int read_run_init_context(char **context) -{ - int ret = -1; - RC_STRINGLIST *list; - char *value = NULL; - - list = rc_config_list(selinux_openrc_contexts_path()); - if (list == NULL) - return ret; - - value = rc_config_value(list, "run_init"); - if (value != NULL && strlen(value) > 0) { - *context = xstrdup(value); - ret = 0; - } - - rc_stringlist_free(list); - return ret; -} - -void selinux_setup(char **argv) -{ - char *new_context = NULL; - char *curr_context = NULL; - context_t curr_con; - char *curr_t = NULL; - char *run_init_t = NULL; - - /* Return, if selinux is disabled. */ - if (is_selinux_enabled() < 1) { - return; - } - - if (read_run_init_context(&run_init_t) != 0) { - /* assume a reasonable default, rather than bailing out */ - run_init_t = xstrdup("run_init_t"); - ewarn("Assuming SELinux run_init type is %s", run_init_t); - } - - /* Get our current context. */ - if (getcon(&curr_context) < 0) { - if (errno == ENOENT) { - /* should only hit this if proc is not mounted. this - * happens on Gentoo right after init starts, when - * the init script processing starts. - */ - goto out; - } else { - perror("getcon"); - exit(1); - } - } - - /* extract the type from the context */ - curr_con = context_new(curr_context); - if (!curr_con) { - free(curr_context); - goto out; - } - - curr_t = xstrdup(context_type_get(curr_con)); - if (!curr_t) { - context_free(curr_con); - free(curr_context); - goto out; - } - - /* dont need them anymore so free() now */ - context_free(curr_con); - free(curr_context); - - /* if we are not in the run_init domain, we should not do anything */ - if (strncmp(run_init_t, curr_t, strlen(run_init_t)) != 0) { - goto out; - } - - free(curr_t); - free(run_init_t); - - if (check_auth() != 0) { - eerrorx("Authentication failed."); - } - - /* Get the context for the script to be run in. */ - if (read_context_file(INITRC_FILE, &new_context) != 0) { - /* assume a reasonable default, rather than bailing out */ - new_context = xstrdup("system_u:system_r:initrc_t"); - ewarn("Assuming SELinux initrc context is %s", new_context); - } - - /* Set the new context */ - if (setexeccon(new_context) < 0) { - eerrorx("Could not set SELinux exec context to %s.", new_context); - } - - free(new_context); - - /* - * exec will recycle ptys so try and use open_init_pty if it exists - * which will open the pty with initrc_devpts_t, if it doesnt exist, - * fall back to plain exec - */ - if (!access("/usr/sbin/open_init_pty", X_OK)) { - if (execvp("/usr/sbin/open_init_pty", argv)) { - perror("execvp"); - exit(-1); - } - } else if (execvp(argv[1], argv + 1)) { - perror("execvp"); - exit(-1); - } - -out: - free(run_init_t); - free(curr_t); -} diff --git a/src/rc/rc-selinux.h b/src/rc/rc-selinux.h deleted file mode 100644 index 627c87bc..00000000 --- a/src/rc/rc-selinux.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2014-2015 The OpenRC Authors. - * See the Authors file at the top-level directory of this distribution and - * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS - * - * This file is part of OpenRC. It is subject to the license terms in - * the LICENSE file found in the top-level directory of this - * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE - * This file may not be copied, modified, propagated, or distributed - * except according to the terms contained in the LICENSE file. - */ - -#ifndef RC_SELINUX_UTIL_H -#define RC_SELINUX_UTIL_H - -#ifdef HAVE_SELINUX - -int selinux_util_open(void); -int selinux_util_label(const char *path); -int selinux_util_close(void); - -void selinux_setup(char **argv); - -#else - -/* always return false for selinux_util_open() */ -#define selinux_util_open() (0) -#define selinux_util_label(x) do { } while (0) -#define selinux_util_close() do { } while (0) - -#define selinux_setup(x) do { } while (0) - -#endif - - -#endif diff --git a/src/rc/rc-service.c b/src/rc/rc-service.c deleted file mode 100644 index 44651cc6..00000000 --- a/src/rc/rc-service.c +++ /dev/null @@ -1,171 +0,0 @@ -/* - * rc-service.c - * Finds all OpenRC services - */ - -/* - * Copyright (c) 2008-2015 The OpenRC Authors. - * See the Authors file at the top-level directory of this distribution and - * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS - * - * This file is part of OpenRC. It is subject to the license terms in - * the LICENSE file found in the top-level directory of this - * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE - * This file may not be copied, modified, propagated, or distributed - * except according to the terms contained in the LICENSE file. - */ - -#include -#include -#include -#include -#include - -#include "einfo.h" -#include "queue.h" -#include "rc.h" -#include "rc-misc.h" -#include "_usage.h" - -const char *applet = NULL; -const char *extraopts = NULL; -const char getoptstring[] = "cdDe:ilr:INsSZ" getoptstring_COMMON; -const struct option longopts[] = { - { "debug", 0, NULL, 'd' }, - { "nodeps", 0, NULL, 'D' }, - { "exists", 1, NULL, 'e' }, - { "ifcrashed", 0, NULL, 'c' }, - { "ifexists", 0, NULL, 'i' }, - { "ifinactive", 0, NULL, 'I' }, - { "ifnotstarted", 0, NULL, 'N' }, - { "ifstarted", 0, NULL, 's' }, - { "ifstopped", 0, NULL, 'S' }, - { "list", 0, NULL, 'l' }, - { "resolve", 1, NULL, 'r' }, - { "dry-run", 0, NULL, 'Z' }, - longopts_COMMON -}; -const char * const longopts_help[] = { - "set xtrace when running the command", - "ignore dependencies", - "tests if the service exists or not", - "if the service is crashed run the command", - "if the service exists run the command", - "if the service is inactive run the command", - "if the service is not started run the command", - "if the service is started run the command", - "if the service is stopped run the command", - "list all available services", - "resolve the service name to an init script", - "dry run (show what would happen)", - longopts_help_COMMON -}; -const char *usagestring = "" \ - "Usage: rc-service [options] [-i] ...\n" \ - " or: rc-service [options] -e \n" \ - " or: rc-service [options] -l\n" \ - " or: rc-service [options] -r "; - -int main(int argc, char **argv) -{ - int opt; - char *service; - RC_STRINGLIST *list; - RC_STRING *s; - RC_SERVICE state; - bool if_crashed = false; - bool if_exists = false; - bool if_inactive = false; - bool if_notstarted = false; - bool if_started = false; - bool if_stopped = false; - - applet = basename_c(argv[0]); - /* 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 'd': - setenv("RC_DEBUG", "yes", 1); - break; - case 'D': - setenv("RC_NODEPS", "yes", 1); - break; - case 'e': - service = rc_service_resolve(optarg); - opt = service ? EXIT_SUCCESS : EXIT_FAILURE; - free(service); - return opt; - /* NOTREACHED */ - case 'c': - if_crashed = true; - break; - case 'i': - if_exists = true; - break; - case 'I': - if_inactive = true; - break; - case 'N': - if_notstarted = 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); - rc_stringlist_free(list); - return EXIT_SUCCESS; - /* NOTREACHED */ - case 'r': - service = rc_service_resolve(optarg); - if (service == NULL) - return EXIT_FAILURE; - printf("%s\n", service); - free(service); - return EXIT_SUCCESS; - /* NOTREACHED */ - case 's': - if_started = true; - break; - case 'S': - if_stopped = true; - break; - case 'Z': - setenv("IN_DRYRUN", "yes", 1); - break; - - 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); - } - state = rc_service_state(*argv); - if (if_crashed && !(rc_service_daemons_crashed(*argv) && errno != EACCES)) - return 0; - if (if_inactive && !(state & RC_SERVICE_INACTIVE)) - return 0; - if (if_notstarted && (state & RC_SERVICE_STARTED)) - return 0; - if (if_started && !(state & RC_SERVICE_STARTED)) - return 0; - if (if_stopped && !(state & RC_SERVICE_STOPPED)) - return 0; - *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 7eb152ec..00000000 --- a/src/rc/rc-status.c +++ /dev/null @@ -1,465 +0,0 @@ -/* - * rc-status.c - * Display the status of the services in runlevels - */ - -/* - * Copyright (c) 2007-2015 The OpenRC Authors. - * See the Authors file at the top-level directory of this distribution and - * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS - * - * This file is part of OpenRC. It is subject to the license terms in - * the LICENSE file found in the top-level directory of this - * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE - * This file may not be copied, modified, propagated, or distributed - * except according to the terms contained in the LICENSE file. - */ - -#include -#include -#include -#include -#include -#include - -#include "einfo.h" -#include "queue.h" -#include "rc.h" -#include "rc-misc.h" -#include "_usage.h" - -enum format_t { - FORMAT_DEFAULT, - FORMAT_INI, -}; - -const char *applet = NULL; -const char *extraopts = NULL; -const char getoptstring[] = "acf:lmrsSu" getoptstring_COMMON; -const struct option longopts[] = { - {"all", 0, NULL, 'a'}, - {"crashed", 0, NULL, 'c'}, - {"format", 1, NULL, 'f'}, - {"list", 0, NULL, 'l'}, - {"manual", 0, NULL, 'm'}, - {"runlevel", 0, NULL, 'r'}, - {"servicelist", 0, NULL, 's'}, - {"supervised", 0, NULL, 'S'}, - {"unused", 0, NULL, 'u'}, - longopts_COMMON -}; -const char * const longopts_help[] = { - "Show services from all run levels", - "Show crashed services", - "format status to be parsable (currently arg must be ini)", - "Show list of run levels", - "Show manually started services", - "Show the name of the current runlevel", - "Show service list", - "show supervised services", - "Show services not assigned to any runlevel", - longopts_help_COMMON -}; -const char *usagestring = "" \ - "Usage: rc-status [options] -f ini ...\n" \ - " or: rc-status [options] [-a | -c | -l | -m | -r | -s | -u]"; - -static RC_DEPTREE *deptree; -static RC_STRINGLIST *types; - -static RC_STRINGLIST *levels, *services, *tmp, *alist; -static RC_STRINGLIST *sservices, *nservices, *needsme; - -static void print_level(const char *prefix, const char *level, - enum format_t format) -{ - switch (format) { - case FORMAT_DEFAULT: - 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); - break; - case FORMAT_INI: - printf("%s", "["); - if (prefix) - printf("%s ", prefix); - printf("%s]\n", level); - break; - } -} - -static char *get_uptime(const char *service) -{ - RC_SERVICE state = rc_service_state(service); - char *start_count; - char *start_time_string; - time_t start_time; - int64_t diff_days; - int64_t diff_hours; - int64_t diff_mins; - int64_t diff_secs; - char *uptime = NULL; - - if (state & RC_SERVICE_STARTED) { - start_count = rc_service_value_get(service, "start_count"); - start_time_string = rc_service_value_get(service, "start_time"); - if (start_count && start_time_string) { - start_time = to_time_t(start_time_string); - diff_secs = (int64_t) difftime(time(NULL), start_time); - diff_days = diff_secs / 86400; - diff_secs = diff_secs % 86400; - diff_hours = diff_secs / 3600; - diff_secs = diff_secs % 3600; - diff_mins = diff_secs / 60; - diff_secs = diff_secs % 60; - if (diff_days > 0) - xasprintf(&uptime, - "%"PRId64" day(s) %02"PRId64":%02"PRId64":%02"PRId64" (%s)", - diff_days, diff_hours, diff_mins, diff_secs, - start_count); - else - xasprintf(&uptime, - "%02"PRId64":%02"PRId64":%02"PRId64" (%s)", - diff_hours, diff_mins, diff_secs, start_count); - } - } - return uptime; -} - -static void print_service(const char *service, enum format_t format) -{ - char *status = NULL; - char *uptime = NULL; - char *child_pid = NULL; - char *start_time = NULL; - int cols; - const char *c = ecolor(ECOLOR_GOOD); - RC_SERVICE state = rc_service_state(service); - ECOLOR color = ECOLOR_BAD; - - if (state & RC_SERVICE_STOPPING) - xasprintf(&status, "stopping "); - else if (state & RC_SERVICE_STARTING) { - xasprintf(&status, "starting "); - color = ECOLOR_WARN; - } else if (state & RC_SERVICE_INACTIVE) { - xasprintf(&status, "inactive "); - color = ECOLOR_WARN; - } else if (state & RC_SERVICE_STARTED) { - errno = 0; - if (rc_service_daemons_crashed(service) && errno != EACCES) - { - child_pid = rc_service_value_get(service, "child_pid"); - start_time = rc_service_value_get(service, "start_time"); - if (start_time && child_pid) - xasprintf(&status, " unsupervised "); - else - xasprintf(&status, " crashed "); - free(child_pid); - free(start_time); - } else { - uptime = get_uptime(service); - if (uptime) { - xasprintf(&status, " started %s", uptime); - free(uptime); - } else - xasprintf(&status, " started "); - color = ECOLOR_GOOD; - } - } else if (state & RC_SERVICE_SCHEDULED) { - xasprintf(&status, "scheduled"); - color = ECOLOR_WARN; - } else if (state & RC_SERVICE_FAILED) { - xasprintf(&status, "failed"); - color = ECOLOR_WARN; - } else - xasprintf(&status, " stopped "); - - errno = 0; - switch (format) { - case FORMAT_DEFAULT: - cols = printf(" %s", service); - if (c && *c && isatty(fileno(stdout))) - printf("\n"); - ebracket(cols, color, status); - break; - case FORMAT_INI: - printf("%s = %s\n", service, status); - break; - } - free(status); -} - -static void print_services(const char *runlevel, RC_STRINGLIST *svcs, - enum format_t format) -{ - 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, format); - 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, format); - } - rc_stringlist_free(l); -} - -static void print_stacked_services(const char *runlevel, enum format_t format) -{ - 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, format); - servicelist = rc_services_in_runlevel(stackedlevel->value); - print_services(stackedlevel->value, servicelist, format); - rc_stringlist_free(servicelist); - } - rc_stringlist_free(stackedlevels); - stackedlevels = NULL; -} - -int main(int argc, char **argv) -{ - RC_SERVICE state; - RC_STRING *s, *l, *t, *level; - enum format_t format = FORMAT_DEFAULT; - bool levels_given = false; - bool show_all = false; - char *p, *runlevel = NULL; - int opt, retval = 0; - - applet = basename_c(argv[0]); - while ((opt = getopt_long(argc, argv, getoptstring, longopts, - (int *) 0)) != -1) - switch (opt) { - case 'a': - show_all = true; - 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 'f': - if (strcasecmp(optarg, "ini") == 0) { - format = FORMAT_INI; - setenv("EINFO_QUIET", "YES", 1); - } else - eerrorx("%s: invalid argument to --format switch\n", applet); - break; - case 'l': - levels = rc_runlevel_list(); - TAILQ_FOREACH(l, levels, entries) - printf("%s\n", l->value); - goto exit; - case 'm': - 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; - } - } - TAILQ_FOREACH_SAFE(s, services, entries, t) - if (rc_service_state(s->value) & - (RC_SERVICE_STOPPED | RC_SERVICE_HOTPLUGGED)) { - TAILQ_REMOVE(services, s, entries); - free(s->value); - free(s); - } - print_services(NULL, services, FORMAT_DEFAULT); - goto exit; - case 'r': - runlevel = rc_runlevel_get(); - printf("%s\n", runlevel); - goto exit; - /* NOTREACHED */ - case 'S': - services = rc_services_in_state(RC_SERVICE_STARTED); - TAILQ_FOREACH_SAFE(s, services, entries, t) - if (!rc_service_value_get(s->value, "child_pid")) - TAILQ_REMOVE(services, s, entries); - print_services(NULL, services, FORMAT_DEFAULT); - goto exit; - /* NOTREACHED */ - case 's': - services = rc_services_in_runlevel(NULL); - print_services(NULL, services, FORMAT_DEFAULT); - 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, FORMAT_DEFAULT); - 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])) { - levels_given = true; - 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, format); - services = rc_services_in_runlevel(l->value); - print_services(l->value, services, format); - print_stacked_services(l->value, format); - rc_stringlist_free(nservices); - nservices = NULL; - rc_stringlist_free(services); - services = NULL; - } - - if (show_all || !levels_given) { - /* Show hotplugged services */ - print_level("Dynamic", "hotplugged", format); - services = rc_services_in_state(RC_SERVICE_HOTPLUGGED); - print_services(NULL, services, format); - rc_stringlist_free(services); - services = NULL; - - /* Show manually started and unassigned depended services */ - if (show_all) { - 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) { - state = rc_service_state(s->value); - if ((rc_stringlist_find(sservices, s->value) || - (state & ( RC_SERVICE_STOPPED | RC_SERVICE_HOTPLUGGED)))) { - if (!(state & RC_SERVICE_FAILED)) { - TAILQ_REMOVE(services, s, entries); - free(s->value); - free(s); - } - } - } - needsme = rc_stringlist_new(); - rc_stringlist_add(needsme, "needsme"); - rc_stringlist_add(needsme, "wantsme"); - 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/wanted", format); - print_services(NULL, nservices, format); - print_level("Dynamic", "manual", format); - print_services(NULL, services, format); - } - -exit: - free(runlevel); - 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); - - return retval; -} diff --git a/src/rc/rc-sysvinit.c b/src/rc/rc-sysvinit.c deleted file mode 100644 index 8d258b63..00000000 --- a/src/rc/rc-sysvinit.c +++ /dev/null @@ -1,102 +0,0 @@ -/* - * rc-sysvinit.c - * Helper to send a runlevel change to sysvinit - */ - -/* - * Copyright (c) 2019 The OpenRC Authors. - * See the Authors file at the top-level directory of this distribution and - * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS - * - * This file is part of OpenRC. It is subject to the license terms in - * the LICENSE file found in the top-level directory of this - * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE - * This file may not be copied, modified, propagated, or distributed - * except according to the terms contained in the LICENSE file. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "einfo.h" -#include "rc-sysvinit.h" - -static void sysvinit_send_cmd(struct init_request *request) -{ - int fd; - char *p; - size_t bytes; - ssize_t r; - - fd = open("/run/initctl", O_WRONLY|O_NONBLOCK|O_CLOEXEC|O_NOCTTY); - if (fd < 0) { - if (errno != ENOENT) - eerror("Failed to open initctl fifo: %s", strerror(errno)); - return; - } - p = (char *) request; - bytes = sizeof(*request); - do { - r = write(fd, p, bytes); - if (r < 0) { - if ((errno == EAGAIN) || (errno == EINTR)) - continue; - eerror("Failed to write to /run/initctl: %s", strerror(errno)); - return; - } - p += r; - bytes -= r; - } while (bytes > 0); -} - -void sysvinit_runlevel(char rl) -{ - struct init_request request; - - if (!rl) - return; - - request = (struct init_request) { - .magic = INIT_MAGIC, - .sleeptime = 0, - .cmd = INIT_CMD_RUNLVL, - .runlevel = rl, - }; - sysvinit_send_cmd(&request); - return; -} - -/* - * Set environment variables in the init process. - */ -void sysvinit_setenv(const char *name, const char *value) -{ - struct init_request request; - size_t nl; - size_t vl; - - memset(&request, 0, sizeof(request)); - request.magic = INIT_MAGIC; - request.cmd = INIT_CMD_SETENV; - nl = strlen(name); - if (value) - vl = strlen(value); -else - vl = 0; - - if (nl + vl + 3 >= (int)sizeof(request.i.data)) - return; - - memcpy(request.i.data, name, nl); - if (value) { - request.i.data[nl] = '='; - memcpy(request.i.data + nl + 1, value, vl); - } - sysvinit_send_cmd(&request); - return; -} diff --git a/src/rc/rc-sysvinit.h b/src/rc/rc-sysvinit.h deleted file mode 100644 index 6142cdd6..00000000 --- a/src/rc/rc-sysvinit.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * rc-sysvinit.h - Interface to communicate with sysvinit via /run/initctl. - */ - -/* - * Copyright (c) 2019 The OpenRC Authors. - * See the Authors file at the top-level directory of this distribution and - * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS - * - * This file is part of OpenRC. It is subject to the license terms in - * the LICENSE file found in the top-level directory of this - * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE - * This file may not be copied, modified, propagated, or distributed - * except according to the terms contained in the LICENSE file. - */ - -#ifndef _RC_SYSVINIT_H -#define _RC_SYSVINIT_H - -/* - * The #defines and structures below are taken from initreq.h in - * sysvinit and must be used by any program wishing to communicate with - * it. - */ - -#define INIT_MAGIC 0x03091969 -#define INIT_CMD_START 0 -#define INIT_CMD_RUNLVL 1 -#define INIT_CMD_POWERFAIL 2 -#define INIT_CMD_POWERFAILNOW 3 -#define INIT_CMD_POWEROK 4 -#define INIT_CMD_BSD 5 -#define INIT_CMD_SETENV 6 -#define INIT_CMD_UNSETENV 7 - -/* - * This is what BSD 4.4 uses when talking to init. - * Linux doesn't use this right now. - */ -struct init_request_bsd { - char gen_id[8]; /* Beats me.. telnetd uses "fe" */ - char tty_id[16]; /* Tty name minus /dev/tty */ - char host[64]; /* Hostname */ - char term_type[16]; /* Terminal type */ - int signal; /* Signal to send */ - int pid; /* Process to send to */ - char exec_name[128]; /* Program to execute */ - char reserved[128]; /* For future expansion. */ -}; - -/* - * Because of legacy interfaces, "runlevel" and "sleeptime" - * aren't in a seperate struct in the union. - * - * The weird sizes are because init expects the whole - * struct to be 384 bytes. - */ -struct init_request { - int magic; /* Magic number */ - int cmd; /* What kind of request */ - int runlevel; /* Runlevel to change to */ - int sleeptime; /* Time between TERM and KILL */ - union { - struct init_request_bsd bsd; - char data[368]; - } i; -}; - -void sysvinit_runlevel(char rl); -void sysvinit_setenv(const char *name, const char *value); - -#endif diff --git a/src/rc/rc-update.c b/src/rc/rc-update.c deleted file mode 100644 index d6dbf240..00000000 --- a/src/rc/rc-update.c +++ /dev/null @@ -1,353 +0,0 @@ -/* - * rc-update - * Manage init scripts and runlevels - */ - -/* - * Copyright (c) 2007-2015 The OpenRC Authors. - * See the Authors file at the top-level directory of this distribution and - * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS - * - * This file is part of OpenRC. It is subject to the license terms in - * the LICENSE file found in the top-level directory of this - * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE - * This file may not be copied, modified, propagated, or distributed - * except according to the terms contained in the LICENSE file. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "einfo.h" -#include "queue.h" -#include "rc.h" -#include "rc-misc.h" -#include "_usage.h" - -const char *applet = NULL; -const char *extraopts = NULL; -const char *usagestring = "" \ - "Usage: rc-update [options] add [...]\n" \ - " or: rc-update [options] del [...]\n" \ - " or: rc-update [options] [show [...]]"; -const char getoptstring[] = "asu" getoptstring_COMMON; -const struct option longopts[] = { - { "all", 0, NULL, 'a' }, - { "stack", 0, NULL, 's' }, - { "update", 0, NULL, 'u' }, - longopts_COMMON -}; -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 -}; - -/* 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 executable", - 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 = NULL; - 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); - buffer = xmalloc(l+1); - memset (buffer, ' ', l); - buffer[l] = 0; - rc_stringlist_add (in, buffer); - free(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); -} - -#define DOADD (1 << 1) -#define DODELETE (1 << 2) -#define DOSHOW (1 << 3) - -int main(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; - - applet = basename_c(argv[0]); - 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-wtmp.c b/src/rc/rc-wtmp.c deleted file mode 100644 index 42e228d0..00000000 --- a/src/rc/rc-wtmp.c +++ /dev/null @@ -1,51 +0,0 @@ -/* - * rc-wtmp.c - * This file contains routines to deal with the wtmp file. - */ - -/* - * Copyright 2017 The OpenRC Authors. - * See the Authors file at the top-level directory of this distribution and - * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS - * - * This file is part of OpenRC. It is subject to the license terms in - * the LICENSE file found in the top-level directory of this - * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE - * This file may not be copied, modified, propagated, or distributed - * except according to the terms contained in the LICENSE file. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "rc-wtmp.h" - -void log_wtmp(const char *user, const char *id, pid_t pid, int type, - const char *line) -{ - struct timeval tv; - struct utmp utmp; - struct utsname uname_buf; - - memset(&utmp, 0, sizeof(utmp)); - gettimeofday(&tv, NULL); - utmp.ut_tv.tv_sec = tv.tv_sec; - utmp.ut_tv.tv_usec = tv.tv_usec; - utmp.ut_pid = pid; - utmp.ut_type = type; - strncpy(utmp.ut_name, user, sizeof(utmp.ut_name)); - strncpy(utmp.ut_id , id , sizeof(utmp.ut_id )); - strncpy(utmp.ut_line, line, sizeof(utmp.ut_line)); - - /* Put the OS version in place of the hostname */ - if (uname(&uname_buf) == 0) - strncpy(utmp.ut_host, uname_buf.release, sizeof(utmp.ut_host)); - - updwtmp(WTMP_FILE, &utmp); -} diff --git a/src/rc/rc.c b/src/rc/rc.c deleted file mode 100644 index 53c75bb6..00000000 --- a/src/rc/rc.c +++ /dev/null @@ -1,1118 +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-2015 The OpenRC Authors. - * See the Authors file at the top-level directory of this distribution and - * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS - * - * This file is part of OpenRC. It is subject to the license terms in - * the LICENSE file found in the top-level directory of this - * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE - * This file may not be copied, modified, propagated, or distributed - * except according to the terms contained in the LICENSE file. - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "einfo.h" -#include "queue.h" -#include "rc.h" -#include "rc-logger.h" -#include "rc-misc.h" -#include "rc-plugin.h" - -#include "version.h" -#include "_usage.h" - -const char *extraopts = NULL; -const char getoptstring[] = "a:no:s:S" getoptstring_COMMON; -const struct option longopts[] = { - { "no-stop", 0, NULL, 'n' }, - { "override", 1, NULL, 'o' }, - { "service", 1, NULL, 's' }, - { "sys", 0, NULL, 'S' }, - longopts_COMMON -}; -const char * const longopts_help[] = { - "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 -}; -const char *usagestring = "" \ - "Usage: openrc [options] []"; - -#define INITSH RC_LIBEXECDIR "/sh/init.sh" -#define INITEARLYSH RC_LIBEXECDIR "/sh/init-early.sh" - -#define INTERACTIVE RC_SVCDIR "/interactive" - -#define DEVBOOT "/dev/.rcboot" - -const char *applet = NULL; -static RC_STRINGLIST *main_hotplugged_services; -static RC_STRINGLIST *main_stop_services; -static RC_STRINGLIST *main_start_services; -static RC_STRINGLIST *main_types_nw; -static RC_STRINGLIST *main_types_nwua; -static RC_DEPTREE *main_deptree; -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; - 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; - - xasprintf(&path, RC_SVCDIR "/failed/%s", d->d_name); - if (unlink(path)) - eerror("%s: unlink `%s': %s", - applet, path, strerror(errno)); - free(path); - } - closedir(dp); - } -} - -static void -cleanup(void) -{ - RC_PID *p1 = LIST_FIRST(&service_pids); - RC_PID *p2; - - 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(); - } - - while (p1) { - p2 = LIST_NEXT(p1, entries); - free(p1); - p1 = p2; - } - - rc_stringlist_free(main_hotplugged_services); - rc_stringlist_free(main_stop_services); - rc_stringlist_free(main_start_services); - rc_stringlist_free(main_types_nw); - rc_stringlist_free(main_types_nwua); - rc_deptree_free(main_deptree); - free(runlevel); -} - -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 systems cannot really drop to shells */ - if (sys && strcmp(sys, RC_SYS_VSERVER) == 0) - { - execlp("halt", "halt", "-f", (char *) NULL); - eerrorx("%s: unable to exec `halt -f': %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 char *get_krunlevel(void) -{ - char *buffer = NULL; - FILE *fp; - size_t i = 0; - - if (!exists(RC_KRUNLEVEL)) - return NULL; - if (!(fp = fopen(RC_KRUNLEVEL, "r"))) { - eerror("fopen `%s': %s", RC_KRUNLEVEL, strerror(errno)); - return NULL; - } - - if (getline(&buffer, &i, fp) != -1) { - i = strlen(buffer); - if (buffer[i - 1] == '\n') - buffer[i - 1] = 0; - } - fclose(fp); - return buffer; -} - -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 = NULL; - 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) - xasprintf(&signame, "SIGINT"); - /* FALLTHROUGH */ - case SIGTERM: - if (!signame) - xasprintf(&signame, "SIGTERM"); - /* FALLTHROUGH */ - case SIGQUIT: - if (!signame) - xasprintf(&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); - /* force an update of the dependency tree */ - if ((main_deptree = _rc_deptree_load(1, NULL)) == NULL) - eerrorx("failed to load deptree"); -} - -static bool -runlevel_config(const char *service, const char *level) -{ - char *init = rc_service_resolve(service); - char *conf, *dir; - bool retval; - - dir = dirname(init); - dir = dirname(init); - xasprintf(&conf, "%s/conf.d/%s.%s", dir, service, level); - retval = exists(conf); - free(conf); - free(init); - return retval; -} - -static void -do_stop_services(RC_STRINGLIST *types_nw, 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_nw) { - types_nw = rc_stringlist_new(); - rc_stringlist_add(types_nw, "needsme"); - rc_stringlist_add(types_nw, "wantsme"); - } - - 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_nw, - 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) { - 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); - if (pid == -1) - break; - /* 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 - -int main(int argc, char **argv) -{ - const char *bootlevel = NULL; - char *newlevel = NULL; - const char *systype = NULL; - RC_STRINGLIST *deporder = NULL; - RC_STRINGLIST *tmplist; - RC_STRING *service; - bool going_down = false; - int depoptions = RC_DEP_STRICT | RC_DEP_TRACE; - char *krunlevel = NULL; - char *pidstr = NULL; - 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"); - - 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 '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': - systype = rc_sys(); - if (systype) - printf("%s\n", systype); - exit(EXIT_SUCCESS); - /* NOTREACHED */ - case_RC_COMMON_GETOPT - } - } - - if (strcmp(applet, "rc") == 0) - ewarn("rc is deprecated, please use openrc instead."); - 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 */ - xasprintf(&pidstr, "%d", getpid()); - setenv("RC_PID", pidstr, 1); - free(pidstr); - - /* 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)) - { - krunlevel = get_krunlevel(); - if (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 ((main_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. - */ - main_stop_services = rc_services_in_state(RC_SERVICE_STARTED); - tmplist = rc_services_in_state(RC_SERVICE_INACTIVE); - TAILQ_CONCAT(main_stop_services, tmplist, entries); - free(tmplist); - tmplist = rc_services_in_state(RC_SERVICE_STARTING); - TAILQ_CONCAT(main_stop_services, tmplist, entries); - free(tmplist); - if (main_stop_services) - rc_stringlist_sort(&main_stop_services); - - main_types_nwua = rc_stringlist_new(); - rc_stringlist_add(main_types_nwua, "ineed"); - rc_stringlist_add(main_types_nwua, "iwant"); - rc_stringlist_add(main_types_nwua, "iuse"); - rc_stringlist_add(main_types_nwua, "iafter"); - - if (main_stop_services) { - tmplist = rc_deptree_depends(main_deptree, main_types_nwua, main_stop_services, - runlevel, depoptions | RC_DEP_STOP); - rc_stringlist_free(main_stop_services); - main_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. - */ - main_hotplugged_services = rc_services_in_state(RC_SERVICE_HOTPLUGGED); - main_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(main_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(main_start_services, tmplist, entries); - free(tmplist); - } - if (main_hotplugged_services) { - TAILQ_FOREACH(service, main_hotplugged_services, - entries) - rc_stringlist_addu(main_start_services, - service->value); - } - } - } - - parallel = rc_conf_yesno("rc_parallel"); - - /* Now stop the services that shouldn't be running */ - if (main_stop_services && !nostop) - do_stop_services(main_types_nw, main_start_services, main_stop_services, main_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 (main_hotplugged_services) - TAILQ_FOREACH(service, main_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 (main_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(main_deptree, main_types_nwua, 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/seedrng.c b/src/rc/seedrng.c deleted file mode 100644 index 95fb947d..00000000 --- a/src/rc/seedrng.c +++ /dev/null @@ -1,522 +0,0 @@ -/* - * seedrng.c - * Seed kernel RNG from seed file, based on code from: - * https://git.zx2c4.com/seedrng/about/ - */ - -/* - * Copyright (c) 2022 The OpenRC Authors. - * See the Authors file at the top-level directory of this distribution and - * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS - * - * This file is part of OpenRC. It is subject to the license terms in - * the LICENSE file found in the top-level directory of this - * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE - * This file may not be copied, modified, propagated, or distributed - * except according to the terms contained in the LICENSE file. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "rc.h" -#include "einfo.h" -#include "helpers.h" -#include "_usage.h" - -#ifndef GRND_INSECURE -#define GRND_INSECURE 0x0004 /* Apparently some headers don't ship with this yet. */ -#endif - -/* Use long option value that is out of range for 8 bit getopt values. - * The exact enum value is internal and can freely change, so we keep the - * options sorted. - */ -enum long_opts { - /* This has to come first so following values stay in the 0x100+ range. */ - LONGOPT_BASE = 0x100, - LONGOPT_LOCK_FILE, - LONGOPT_SEED_DIR, - LONGOPT_SKIP_CREDIT, -}; - -const char *applet = NULL; -const char *extraopts =NULL; -const char getoptstring[] = getoptstring_COMMON; -const struct option longopts[] = { - { "lock-file", 1, NULL, LONGOPT_LOCK_FILE }, - { "seed-dir", 1, NULL, LONGOPT_SEED_DIR }, - { "skip-credit", 0, NULL, LONGOPT_SKIP_CREDIT }, - longopts_COMMON -}; -const char * const longopts_help[] = { - "lock file", - "directory for seed files", - "skip credit", - longopts_help_COMMON -}; -const char *usagestring = NULL; - -static char *lock_file = NULL; -static char *seed_dir = NULL; -static char *creditable_seed = NULL; -static char *non_creditable_seed = NULL; - -enum blake2s_lengths { - BLAKE2S_BLOCK_LEN = 64, - BLAKE2S_HASH_LEN = 32, - BLAKE2S_KEY_LEN = 32 -}; - -enum seedrng_lengths { - MAX_SEED_LEN = 512, - MIN_SEED_LEN = BLAKE2S_HASH_LEN -}; - -struct blake2s_state { - uint32_t h[8]; - uint32_t t[2]; - uint32_t f[2]; - uint8_t buf[BLAKE2S_BLOCK_LEN]; - unsigned int buflen; - unsigned int outlen; -}; - -#define le32_to_cpup(a) le32toh(*(a)) -#define cpu_to_le32(a) htole32(a) -#ifndef ARRAY_SIZE -#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) -#endif -#ifndef DIV_ROUND_UP -#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d)) -#endif - -static inline void cpu_to_le32_array(uint32_t *buf, unsigned int words) -{ - while (words--) { - *buf = cpu_to_le32(*buf); - ++buf; - } -} - -static inline void le32_to_cpu_array(uint32_t *buf, unsigned int words) -{ - while (words--) { - *buf = le32_to_cpup(buf); - ++buf; - } -} - -static inline uint32_t ror32(uint32_t word, unsigned int shift) -{ - return (word >> (shift & 31)) | (word << ((-shift) & 31)); -} - -static const uint32_t blake2s_iv[8] = { - 0x6A09E667UL, 0xBB67AE85UL, 0x3C6EF372UL, 0xA54FF53AUL, - 0x510E527FUL, 0x9B05688CUL, 0x1F83D9ABUL, 0x5BE0CD19UL -}; - -static const uint8_t blake2s_sigma[10][16] = { - { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, - { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 }, - { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 }, - { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 }, - { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 }, - { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 }, - { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 }, - { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 }, - { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 }, - { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0 }, -}; - -static void blake2s_set_lastblock(struct blake2s_state *state) -{ - state->f[0] = -1; -} - -static void blake2s_increment_counter(struct blake2s_state *state, const uint32_t inc) -{ - state->t[0] += inc; - state->t[1] += (state->t[0] < inc); -} - -static void blake2s_init_param(struct blake2s_state *state, const uint32_t param) -{ - int i; - - memset(state, 0, sizeof(*state)); - for (i = 0; i < 8; ++i) - state->h[i] = blake2s_iv[i]; - state->h[0] ^= param; -} - -static void blake2s_init(struct blake2s_state *state, const size_t outlen) -{ - blake2s_init_param(state, 0x01010000 | outlen); - state->outlen = outlen; -} - -static void blake2s_compress(struct blake2s_state *state, const uint8_t *block, size_t nblocks, const uint32_t inc) -{ - uint32_t m[16]; - uint32_t v[16]; - int i; - - while (nblocks > 0) { - blake2s_increment_counter(state, inc); - memcpy(m, block, BLAKE2S_BLOCK_LEN); - le32_to_cpu_array(m, ARRAY_SIZE(m)); - memcpy(v, state->h, 32); - v[ 8] = blake2s_iv[0]; - v[ 9] = blake2s_iv[1]; - v[10] = blake2s_iv[2]; - v[11] = blake2s_iv[3]; - v[12] = blake2s_iv[4] ^ state->t[0]; - v[13] = blake2s_iv[5] ^ state->t[1]; - v[14] = blake2s_iv[6] ^ state->f[0]; - v[15] = blake2s_iv[7] ^ state->f[1]; - -#define G(r, i, a, b, c, d) do { \ - a += b + m[blake2s_sigma[r][2 * i + 0]]; \ - d = ror32(d ^ a, 16); \ - c += d; \ - b = ror32(b ^ c, 12); \ - a += b + m[blake2s_sigma[r][2 * i + 1]]; \ - d = ror32(d ^ a, 8); \ - c += d; \ - b = ror32(b ^ c, 7); \ -} while (0) - -#define ROUND(r) do { \ - G(r, 0, v[0], v[ 4], v[ 8], v[12]); \ - G(r, 1, v[1], v[ 5], v[ 9], v[13]); \ - G(r, 2, v[2], v[ 6], v[10], v[14]); \ - G(r, 3, v[3], v[ 7], v[11], v[15]); \ - G(r, 4, v[0], v[ 5], v[10], v[15]); \ - G(r, 5, v[1], v[ 6], v[11], v[12]); \ - G(r, 6, v[2], v[ 7], v[ 8], v[13]); \ - G(r, 7, v[3], v[ 4], v[ 9], v[14]); \ -} while (0) - ROUND(0); - ROUND(1); - ROUND(2); - ROUND(3); - ROUND(4); - ROUND(5); - ROUND(6); - ROUND(7); - ROUND(8); - ROUND(9); - -#undef G -#undef ROUND - - for (i = 0; i < 8; ++i) - state->h[i] ^= v[i] ^ v[i + 8]; - - block += BLAKE2S_BLOCK_LEN; - --nblocks; - } -} - -static void blake2s_update(struct blake2s_state *state, const void *inp, size_t inlen) -{ - const size_t fill = BLAKE2S_BLOCK_LEN - state->buflen; - const uint8_t *in = inp; - - if (!inlen) - return; - if (inlen > fill) { - memcpy(state->buf + state->buflen, in, fill); - blake2s_compress(state, state->buf, 1, BLAKE2S_BLOCK_LEN); - state->buflen = 0; - in += fill; - inlen -= fill; - } - if (inlen > BLAKE2S_BLOCK_LEN) { - const size_t nblocks = DIV_ROUND_UP(inlen, BLAKE2S_BLOCK_LEN); - blake2s_compress(state, in, nblocks - 1, BLAKE2S_BLOCK_LEN); - in += BLAKE2S_BLOCK_LEN * (nblocks - 1); - inlen -= BLAKE2S_BLOCK_LEN * (nblocks - 1); - } - memcpy(state->buf + state->buflen, in, inlen); - state->buflen += inlen; -} - -static void blake2s_final(struct blake2s_state *state, uint8_t *out) -{ - blake2s_set_lastblock(state); - memset(state->buf + state->buflen, 0, BLAKE2S_BLOCK_LEN - state->buflen); - blake2s_compress(state, state->buf, 1, state->buflen); - cpu_to_le32_array(state->h, ARRAY_SIZE(state->h)); - memcpy(out, state->h, state->outlen); -} - -static size_t determine_optimal_seed_len(void) -{ - size_t ret = 0; - char poolsize_str[11] = { 0 }; - int fd = open("/proc/sys/kernel/random/poolsize", O_RDONLY); - - if (fd < 0 || read(fd, poolsize_str, sizeof(poolsize_str) - 1) < 0) { - ewarn("%s: Unable to determine pool size, falling back to %u bits: %s", - applet, MIN_SEED_LEN * 8, strerror(errno)); - ret = MIN_SEED_LEN; - } else - ret = DIV_ROUND_UP(strtoul(poolsize_str, NULL, 10), 8); - if (fd >= 0) - close(fd); - if (ret < MIN_SEED_LEN) - ret = MIN_SEED_LEN; - else if (ret > MAX_SEED_LEN) - ret = MAX_SEED_LEN; - return ret; -} - -static int read_new_seed(uint8_t *seed, size_t len, bool *is_creditable) -{ - ssize_t ret; - int urandom_fd; - - *is_creditable = false; - ret = getrandom(seed, len, GRND_NONBLOCK); - if (ret == (ssize_t)len) { - *is_creditable = true; - return 0; - } - if (ret == -1 && errno == ENOSYS) { - struct pollfd random_fd = { - .fd = open("/dev/random", O_RDONLY), - .events = POLLIN - }; - if (random_fd.fd < 0) - return -errno; - *is_creditable = poll(&random_fd, 1, 0) == 1; - close(random_fd.fd); - } else if (getrandom(seed, len, GRND_INSECURE) == (ssize_t)len) - return 0; - urandom_fd = open("/dev/urandom", O_RDONLY); - if (urandom_fd < 0) - return -errno; - ret = read(urandom_fd, seed, len); - if (ret == (ssize_t)len) - ret = 0; - else - ret = -errno ? -errno : -EIO; - close(urandom_fd); - return ret; -} - -static int seed_rng(uint8_t *seed, size_t len, bool credit) -{ - struct { - int entropy_count; - int buf_size; - uint8_t buffer[MAX_SEED_LEN]; - } req = { - .entropy_count = credit ? len * 8 : 0, - .buf_size = len - }; - int random_fd, ret; - - if (len > sizeof(req.buffer)) - return -EFBIG; - memcpy(req.buffer, seed, len); - - random_fd = open("/dev/random", O_RDWR); - if (random_fd < 0) - return -errno; - ret = ioctl(random_fd, RNDADDENTROPY, &req); - if (ret) - ret = -errno ? -errno : -EIO; - close(random_fd); - return ret; -} - -static int seed_from_file_if_exists(const char *filename, bool credit, struct blake2s_state *hash) -{ - uint8_t seed[MAX_SEED_LEN]; - ssize_t seed_len; - int fd, dfd, ret = 0; - - fd = open(filename, O_RDONLY); - if (fd < 0 && errno == ENOENT) - return 0; - else if (fd < 0) { - ret = -errno; - eerror("%s: Unable to open seed file: %s", applet, strerror(errno)); - return ret; - } - dfd = open(seed_dir, O_DIRECTORY | O_RDONLY); - if (dfd < 0) { - ret = -errno; - close(fd); - eerror("%s: Unable to open seed directory: %s", applet, strerror(errno)); - return ret; - } - seed_len = read(fd, seed, sizeof(seed)); - if (seed_len < 0) { - ret = -errno; - eerror("%s: Unable to read seed file: %s", applet, strerror(errno)); - } - close(fd); - if (ret) { - close(dfd); - return ret; - } - if ((unlink(filename) < 0 || fsync(dfd) < 0) && seed_len) { - ret = -errno; - eerror("%s: Unable to remove seed after reading, so not seeding: %s", - applet, strerror(errno)); - } - close(dfd); - if (ret) - return ret; - if (!seed_len) - return 0; - - blake2s_update(hash, &seed_len, sizeof(seed_len)); - blake2s_update(hash, seed, seed_len); - - einfo("Seeding %zd bits %s crediting", seed_len * 8, credit ? "and" : "without"); - ret = seed_rng(seed, seed_len, credit); - if (ret < 0) - eerror("%s: Unable to seed: %s", applet, strerror(-ret)); - return ret; -} - -static void populate_global_paths(void) -{ - if (!lock_file) - lock_file = xstrdup("/var/run/seedrng.lock"); - if (!seed_dir) - seed_dir = xstrdup("/var/lib/seedrng"); - xasprintf(&creditable_seed, "%s/seed.credit", seed_dir); - xasprintf(&non_creditable_seed, "%s/seed.no-credit", seed_dir); -} - -static void cleanup(void) -{ - free(lock_file); - free(seed_dir); - free(creditable_seed); - free(non_creditable_seed); -} - -int main(int argc, char **argv) -{ - int opt; - static const char seedrng_prefix[] = "SeedRNG v1 Old+New Prefix"; - static const char seedrng_failure[] = "SeedRNG v1 No New Seed Failure"; - int ret, fd = -1, lock, program_ret = 0; - uint8_t new_seed[MAX_SEED_LEN]; - size_t new_seed_len; - bool new_seed_creditable; - struct timespec realtime = { 0 }, boottime = { 0 }; - struct blake2s_state hash; - bool skip_credit = false; - - atexit(cleanup); - applet = basename_c(argv[0]); - if (geteuid()) - eerrorx("%s: superuser access is required", applet); - umask(0077); - - while ((opt = getopt_long(argc, argv, getoptstring, - longopts, (int *) 0)) != -1) - { - switch (opt) { - case LONGOPT_LOCK_FILE: - if (!lock_file) - lock_file = xstrdup(optarg); - break; - case LONGOPT_SEED_DIR: - if (!seed_dir) - seed_dir = xstrdup(optarg); - break; - case LONGOPT_SKIP_CREDIT: - skip_credit = true; - break; - case_RC_COMMON_GETOPT - } - } - populate_global_paths(); - blake2s_init(&hash, BLAKE2S_HASH_LEN); - blake2s_update(&hash, seedrng_prefix, strlen(seedrng_prefix)); - clock_gettime(CLOCK_REALTIME, &realtime); - clock_gettime(CLOCK_BOOTTIME, &boottime); - blake2s_update(&hash, &realtime, sizeof(realtime)); - blake2s_update(&hash, &boottime, sizeof(boottime)); - - if (mkdir(seed_dir, 0700) < 0 && errno != EEXIST) - eerrorx("%s: Unable to create \"%s\" directory: %s", - applet, seed_dir, strerror(errno)); - - lock = open(lock_file, O_WRONLY | O_CREAT, 0000); - if (lock < 0 || flock(lock, LOCK_EX) < 0) { - eerror("%s: Unable to open lock file: %s", applet, strerror(errno)); - program_ret = 1; - goto out; - } - - ret = seed_from_file_if_exists(non_creditable_seed, false, &hash); - if (ret < 0) - program_ret |= 1 << 1; - ret = seed_from_file_if_exists(creditable_seed, !skip_credit, &hash); - if (ret < 0) - program_ret |= 1 << 2; - - new_seed_len = determine_optimal_seed_len(); - ret = read_new_seed(new_seed, new_seed_len, &new_seed_creditable); - if (ret < 0) { - eerror("%s: Unable to read new seed: %s", applet, strerror(-ret)); - new_seed_len = BLAKE2S_HASH_LEN; - strncpy((char *)new_seed, seedrng_failure, new_seed_len); - program_ret |= 1 << 3; - } - blake2s_update(&hash, &new_seed_len, sizeof(new_seed_len)); - blake2s_update(&hash, new_seed, new_seed_len); - blake2s_final(&hash, new_seed + new_seed_len - BLAKE2S_HASH_LEN); - - einfo("Saving %zu bits of %s seed for next boot", new_seed_len * 8, new_seed_creditable ? "creditable" : "non-creditable"); - fd = open(non_creditable_seed, O_WRONLY | O_CREAT | O_TRUNC, 0400); - if (fd < 0) { - eerror("%s: Unable to open seed file for writing: %s", - applet, strerror(errno)); - program_ret |= 1 << 4; - goto out; - } - if (write(fd, new_seed, new_seed_len) != (ssize_t)new_seed_len || fsync(fd) < 0) { - eerror("%s: Unable to write seed file: %s", applet, strerror(errno)); - program_ret |= 1 << 5; - goto out; - } - if (new_seed_creditable && rename(non_creditable_seed, creditable_seed) < 0) { - ewarn("%s: Unable to make new seed creditable: %s", - applet, strerror(errno)); - program_ret |= 1 << 6; - } -out: - if (fd >= 0) - close(fd); - if (lock >= 0) - close(lock); - return program_ret; -} diff --git a/src/rc/shell_var.c b/src/rc/shell_var.c deleted file mode 100644 index 70750245..00000000 --- a/src/rc/shell_var.c +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2007-2015 The OpenRC Authors. - * See the Authors file at the top-level directory of this distribution and - * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS - * - * This file is part of OpenRC. It is subject to the license terms in - * the LICENSE file found in the top-level directory of this - * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE - * This file may not be copied, modified, propagated, or distributed - * except according to the terms contained in the LICENSE file. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -int main(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; -} diff --git a/src/rc/start-stop-daemon.c b/src/rc/start-stop-daemon.c deleted file mode 100644 index db97f3b3..00000000 --- a/src/rc/start-stop-daemon.c +++ /dev/null @@ -1,1205 +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-2015 The OpenRC Authors. - * See the Authors file at the top-level directory of this distribution and - * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS - * - * This file is part of OpenRC. It is subject to the license terms in - * the LICENSE file found in the top-level directory of this - * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE - * This file may not be copied, modified, propagated, or distributed - * except according to the terms contained in the LICENSE file. - */ - -#define ONE_MS 1000000 - -#ifdef __linux__ -/* For extra SCHED_* defines. */ -# define _GNU_SOURCE -#endif - -#include -#include -#include -#include -#include -#include -#include - -#ifdef __linux__ -#include /* For io priority */ -#include /* For prctl */ -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef HAVE_PAM -#include - -/* We are not supporting authentication conversations */ -static struct pam_conv conv = { NULL, NULL}; -#endif - -#ifdef HAVE_CAP -#include -#endif - -#include - -#include "einfo.h" -#include "queue.h" -#include "rc.h" -#include "rc-misc.h" -#include "rc-pipes.h" -#include "rc-schedules.h" -#include "_usage.h" -#include "helpers.h" - -/* Use long option value that is out of range for 8 bit getopt values. - * The exact enum value is internal and can freely change, so we keep the - * options sorted. - */ -enum { - /* This has to come first so following values stay in the 0x100+ range. */ - LONGOPT_BASE = 0x100, - - LONGOPT_CAPABILITIES, - LONGOPT_OOM_SCORE_ADJ, - LONGOPT_NO_NEW_PRIVS, - LONGOPT_SCHEDULER, - LONGOPT_SCHEDULER_PRIO, - LONGOPT_SECBITS, -}; - -const char *applet = NULL; -const char *extraopts = NULL; -const char getoptstring[] = "I:KN:PR:Sa:bc:d:e:g:ik:mn:op:s:tu:r:w:x:1:2:3:4:" \ - getoptstring_COMMON; -const struct option longopts[] = { - { "capabilities", 1, NULL, LONGOPT_CAPABILITIES}, - { "secbits", 1, NULL, LONGOPT_SECBITS}, - { "no-new-privs", 0, NULL, LONGOPT_NO_NEW_PRIVS}, - { "ionice", 1, NULL, 'I'}, - { "stop", 0, NULL, 'K'}, - { "nicelevel", 1, NULL, 'N'}, - { "oom-score-adj",1, NULL, LONGOPT_OOM_SCORE_ADJ}, - { "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'}, - { "stdout-logger",1, NULL, '3'}, - { "stderr-logger",1, NULL, '4'}, - { "progress", 0, NULL, 'P'}, - { "scheduler", 1, NULL, LONGOPT_SCHEDULER}, - { "scheduler-priority", 1, NULL, LONGOPT_SCHEDULER_PRIO}, - longopts_COMMON -}; -const char * const longopts_help[] = { - "Set the inheritable, ambient and bounding capabilities", - "Set the security-bits for the program", - "Set the No New Privs flag for the program", - "Set an ionice class:data when starting", - "Stop daemon", - "Set a nicelevel when starting", - "Set OOM score adjustment 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", - "Redirect stdout to process", - "Redirect stderr to process", - "Print dots each second while waiting", - "Set process scheduler", - "Set process scheduler priority", - longopts_help_COMMON -}; -const char *usagestring = NULL; - -static char **nav; - -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 _unused, - int who _unused, - int ioprio _unused) -{ -#ifdef SYS_ioprio_set - return syscall(SYS_ioprio_set, which, who, ioprio); -#else - return 0; -#endif -} -#endif - -static void -cleanup(void) -{ - free(changeuser); - free(nav); - free_schedulelist(); -} - -static void -handle_signal(int sig) -{ - int status; - int serrno = errno; - char *signame = NULL; - - switch (sig) { - case SIGINT: - if (!signame) - xasprintf(&signame, "SIGINT"); - /* FALLTHROUGH */ - case SIGTERM: - if (!signame) - xasprintf(&signame, "SIGTERM"); - /* FALLTHROUGH */ - case SIGQUIT: - if (!signame) - xasprintf(&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); - } - - /* free signame */ - free(signame); - - /* Restore errno */ - errno = serrno; -} - -static char * -expand_home(const char *home, const char *path) -{ - char *opath, *ppath, *p, *nh; - 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); - } - - xasprintf(&nh, "%s%s", home, ppath); - free(opath); - return nh; -} - -int main(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; - size_t size = 0; - 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 = INT_MIN, ionicec = -1, ioniced = 0; - int oom_score_adj = INT_MIN; - 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; - char *stderr_process = NULL; - char *stdout_process = NULL; - int stdin_fd; - int stdout_fd; - int stderr_fd; - pid_t pid, spid; - RC_PIDLIST *pids; - 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 = NULL; - struct passwd *pw; - struct group *gr; - char *line = NULL; - FILE *fp; - size_t len; - mode_t numask = 022; - char **margv; - unsigned int start_wait = 0; - const char *scheduler = NULL; - int sched_prio = -1; -#ifdef HAVE_CAP - cap_iab_t cap_iab = NULL; - unsigned secbits = 0; -#endif -#ifdef PR_SET_NO_NEW_PRIVS - bool no_new_privs = false; -#endif - - applet = basename_c(argv[0]); - atexit(cleanup); - - 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); - if ((tmp = getenv("SSD_IONICELEVEL"))) { - int n = sscanf(tmp, "%d:%d", &ionicec, &ioniced); - if (n != 1 && n != 2) - eerror("%s: invalid ionice level `%s' (SSD_IONICELEVEL)", - applet, tmp); - if (ionicec == 0) - ioniced = 0; - else if (ionicec == 3) - ioniced = 7; - ionicec <<= 13; /* class shift */ - } - if ((tmp = getenv("SSD_OOM_SCORE_ADJ"))) - if (sscanf(tmp, "%d", &oom_score_adj) != 1) - eerror("%s: invalid oom_score_adj `%s' (SSD_OOM_SCORE_ADJ)", - 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 LONGOPT_CAPABILITIES: -#ifdef HAVE_CAP - cap_iab = cap_iab_from_text(optarg); - if (cap_iab == NULL) - eerrorx("Could not parse iab: %s", strerror(errno)); -#else - eerrorx("Capabilities support not enabled"); -#endif - break; - - case LONGOPT_SECBITS: -#ifdef HAVE_CAP - if (*optarg == '\0') - eerrorx("Secbits are empty"); - - tmp = NULL; - secbits = strtoul(optarg, &tmp, 0); - if (*tmp != '\0') - eerrorx("Could not parse secbits: invalid char %c", *tmp); -#else - eerrorx("Capabilities support not enabled"); -#endif - break; - - case LONGOPT_NO_NEW_PRIVS: -#ifdef PR_SET_NO_NEW_PRIVS - no_new_privs = true; -#else - eerrorx("The No New Privs flag is only supported by Linux (since 3.5)"); -#endif - break; - - 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 LONGOPT_OOM_SCORE_ADJ: /* --oom-score-adj */ - if (sscanf(optarg, "%d", &oom_score_adj) != 1) - eerrorx("%s: invalid oom-score-adj `%s'", - applet, optarg); - break; - - case 'P': /* --progress */ - progress = true; - break; - - case 'R': /* --retry | */ - retry = optarg; - break; - - case 'S': /* --start */ - start = true; - break; - - case 'b': /* --background */ - background = true; - break; - - case 'c': /* --chuid | */ - /* DEPRECATED */ - ewarn("WARNING: -c/--chuid is deprecated and will be removed in the future, please use -u/--user instead"); - /* falls through */ - case 'u': /* --user | */ - { - char dummy[2]; - p = optarg; - tmp = strsep(&p, ":"); - changeuser = xstrdup(tmp); - if (sscanf(tmp, "%d%1s", &tid, dummy) != 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%1s", &tid, dummy) != 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 | */ - 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 */ - 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 */ - pidfile = optarg; - break; - - case 's': /* --signal */ - sig = parse_signal(applet, optarg); - break; - - case 't': /* --test */ - test = true; - break; - - case 'r': /* --chroot /new/root */ - ch_root = optarg; - break; - - case 'a': /* --startas */ - /* 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, "%u", &start_wait) != 1) - eerrorx("%s: `%s' not a number", - applet, optarg); - break; - case 'x': /* --exec */ - 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 '3': /* --stdout-logger "command to run for stdout logging" */ - stdout_process = optarg; - break; - - case '4': /* --stderr-logger "command to run for stderr logging" */ - stderr_process = optarg; - break; - - case LONGOPT_SCHEDULER: /* --scheduler "Process scheduler policy" */ - scheduler = optarg; - break; - - case LONGOPT_SCHEDULER_PRIO: /* --scheduler-priority "Process scheduler priority" */ - sscanf(optarg, "%d", &sched_prio); - 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; - ++argc; - } else if (exec) { - *--argv = exec; - ++argc; - }; - - 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); - if (stdout_process || stderr_process) - eerrorx("%s: --stdout-logger and --stderr-logger are only relevant" - " with --start", applet); - if (start_wait) - ewarn("using --wait with --stop has no effect," - " use --retry instead"); - } 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); - if ((stdout_process || stderr_process) && !background) - eerrorx("%s: --stdout-logger and --stderr-logger are only relevant" - " with --background", applet); - if (redirect_stdout && stdout_process) - eerrorx("%s: do not use --stdout and --stdout-logger together", - applet); - if (redirect_stderr && stderr_process) - eerrorx("%s: do not use --stderr and --stderr-logger together", - 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) - xasprintf(&exec_file, "%s/%s", ch_root, exec); - else - xasprintf(&exec_file, "%s", exec); - } else { - /* Something in $PATH */ - p = tmp = xstrdup(getenv("PATH")); - exec_file = NULL; - while ((token = strsep(&p, ":"))) { - if (ch_root) - xasprintf(&exec_file, "%s/%s/%s", ch_root, token, exec); - else - xasprintf(&exec_file, "%s/%s", token, exec); - if (exec_file && exists(exec_file)) - break; - free(exec_file); - exec_file = NULL; - } - free(tmp); - } - } - if (start && !exists(exec_file)) { - eerror("%s: %s does not exist", applet, - exec_file ? exec_file : exec); - free(exec_file); - exit(EXIT_FAILURE); - } - if (start && retry) - ewarn("using --retry with --start has no effect," - " use --wait instead"); - - /* 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) { - line = NULL; - if (getline(&line, &size, fp) == -1) - eerrorx("%s: %s", applet, strerror(errno)); - p = line; - 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, " "); - free(exec_file); - xasprintf(&exec_file, "%s", token); - 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] = NULL; - } - } - } - margv = nav ? nav : argv; - - if (stop || sig != -1) { - if (sig == -1) - sig = SIGTERM; - if (!stop) - oknodo = true; - if (retry) - parse_schedule(applet, retry, sig); - else if (test || oknodo) - parse_schedule(applet, "0", sig); - else - parse_schedule(applet, NULL, sig); - if (pidfile) { - pid = get_pid(applet, pidfile); - if (pid == -1 && errno != ENOENT) - exit(EXIT_FAILURE); - } else { - pid = 0; - } - i = run_stop_schedule(applet, exec, (const char *const *)margv, - pid, uid, test, progress, false); - - 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(applet, pidfile); - else - pid = 0; - - if (pid) - pids = rc_find_pids(NULL, NULL, 0, pid); - else - pids = rc_find_pids(exec, (const char * const *) argv, uid, 0); - if (pids) - eerrorx("%s: %s is already running", applet, exec); - - free(pids); - 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 != INT_MIN) { - if (setpriority(PRIO_PROCESS, mypid, nicelevel) == -1) - eerrorx("%s: setpriority %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 (oom_score_adj != INT_MIN) { - fp = fopen("/proc/self/oom_score_adj", "w"); - if (!fp) - eerrorx("%s: oom_score_adj %d: %s", applet, - oom_score_adj, strerror(errno)); - fprintf(fp, "%d\n", oom_score_adj); - fclose(fp); - } - - 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); -#ifdef HAVE_CAP - if (uid && cap_setuid(uid)) -#else - if (uid && setuid(uid)) -#endif - eerrorx ("%s: unable to set userid to %d", - applet, uid); - - /* Close any fd's to the passwd database */ - endpwent(); - -#ifdef HAVE_CAP - if (cap_iab != NULL) { - i = cap_iab_set_proc(cap_iab); - - if (cap_free(cap_iab) != 0) - eerrorx("Could not releasable memory: %s", strerror(errno)); - - if (i != 0) - eerrorx("Could not set iab: %s", strerror(errno)); - } - - if (secbits != 0) { - if (cap_set_secbits(secbits) < 0) - eerrorx("Could not set securebits to 0x%x: %s", secbits, strerror(errno)); - } -#endif - -#ifdef PR_SET_NO_NEW_PRIVS - if (no_new_privs) { - if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1) - eerrorx("Could not set No New Privs flag: %s", strerror(errno)); - } -#endif - - -#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=", 11) != 0 && - strncmp(env->value, "RC_SVCNAME=", 11) != 0) || - strncmp(env->value, "SSD_NICELEVEL=", 14) == 0 || - strncmp(env->value, "SSD_IONICELEVEL=", 16) == 0 || - strncmp(env->value, "SSD_OOM_SCORE_ADJ=", 18) == 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); - } - - stdin_fd = devnull_fd; - 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 | S_IRGRP | S_IWGRP)) == -1) - eerrorx("%s: unable to open the logfile" - " for stdout `%s': %s", - applet, redirect_stdout, strerror(errno)); - }else if (stdout_process) { - stdout_fd = rc_pipe_command(stdout_process); - if (stdout_fd == -1) - eerrorx("%s: unable to open the logging process" - " for stdout `%s': %s", - applet, stdout_process, strerror(errno)); - } - if (redirect_stderr) { - if ((stderr_fd = open(redirect_stderr, - O_WRONLY | O_CREAT | O_APPEND, - S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP)) == -1) - eerrorx("%s: unable to open the logfile" - " for stderr `%s': %s", - applet, redirect_stderr, strerror(errno)); - }else if (stderr_process) { - stderr_fd = rc_pipe_command(stderr_process); - if (stderr_fd == -1) - eerrorx("%s: unable to open the logging process" - " for stderr `%s': %s", - applet, stderr_process, strerror(errno)); - } - - if (background) - dup2(stdin_fd, STDIN_FILENO); - if (background || redirect_stdout || stdout_process - || rc_yesno(getenv("EINFO_QUIET"))) - dup2(stdout_fd, STDOUT_FILENO); - if (background || redirect_stderr || stderr_process - || rc_yesno(getenv("EINFO_QUIET"))) - dup2(stderr_fd, STDERR_FILENO); - - for (i = getdtablesize() - 1; i >= 3; --i) - close(i); - - if (scheduler != NULL) { - int scheduler_index; - struct sched_param sched = {.sched_priority = sched_prio}; - if (strcmp(scheduler, "fifo") == 0) - scheduler_index = SCHED_FIFO; - else if (strcmp(scheduler, "rr") == 0) - scheduler_index = SCHED_RR; - else if (strcmp(scheduler, "other") == 0) - scheduler_index = SCHED_OTHER; -#ifdef SCHED_BATCH - else if (strcmp(scheduler, "batch") == 0) - scheduler_index = SCHED_BATCH; -#endif -#ifdef SCHED_IDLE - else if (strcmp(scheduler, "idle") == 0) - scheduler_index = SCHED_IDLE; -#endif - else if (sscanf(scheduler, "%d", &scheduler_index) != 1) - eerrorx("Unknown scheduler: %s", scheduler); - - if (sched_prio == -1) - sched.sched_priority = sched_get_priority_min(scheduler_index); - - if (sched_setscheduler(mypid, scheduler_index, &sched)) - eerrorx("Failed to set scheduler: %s", strerror(errno)); - } else if (sched_prio != -1) { - const struct sched_param sched = {.sched_priority = sched_prio}; - if (sched_setparam(mypid, &sched)) - eerrorx("Failed to set scheduler parameters: %s", strerror(errno)); - } - - 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: nanosleep: %s", - applet, strerror(errno)); - return 0; - } - } - if (background) { - if (kill(pid, 0) == 0) - alive = true; - } else { - if (pidfile) { - pid = get_pid(applet, pidfile); - if (pid == -1) { - eerrorx("%s: did not " - "create a valid" - " pid in `%s'", - applet, pidfile); - } - } else - pid = 0; - if (do_stop(applet, exec, (const char *const *)margv, - pid, uid, 0, test, false) > 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/supervise-daemon.c b/src/rc/supervise-daemon.c deleted file mode 100644 index 93deedcd..00000000 --- a/src/rc/supervise-daemon.c +++ /dev/null @@ -1,1250 +0,0 @@ -/* - * supervise-daemon - * This is a supervisor for daemons. - * It will start a deamon and make sure it restarts if it crashes. - */ - -/* - * Copyright (c) 2016 The OpenRC Authors. - * See the Authors file at the top-level directory of this distribution and - * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS - * - * This file is part of OpenRC. It is subject to the license terms in - * the LICENSE file found in the top-level directory of this - * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE - * This file may not be copied, modified, propagated, or distributed - * except according to the terms contained in the LICENSE file. - */ - -/* nano seconds */ -#define POLL_INTERVAL 20000000 -#define WAIT_PIDFILE 500000000 -#define ONE_SECOND 1000000000 -#define ONE_MS 1000000 - -#include -#include -#include -#include -#include -#include -#include - -#ifdef __linux__ -#include /* For io priority */ -#include /* For prctl */ -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef HAVE_PAM -#include - -/* We are not supporting authentication conversations */ -static struct pam_conv conv = { NULL, NULL}; -#endif - -#ifdef HAVE_CAP -#include -#endif - -#include "einfo.h" -#include "queue.h" -#include "rc.h" -#include "rc-misc.h" -#include "rc-plugin.h" -#include "rc-schedules.h" -#include "_usage.h" -#include "helpers.h" - -/* Use long option value that is out of range for 8 bit getopt values. - * The exact enum value is internal and can freely change, so we keep the - * options sorted. - */ -enum { - /* This has to come first so following values stay in the 0x100+ range. */ - LONGOPT_BASE = 0x100, - - LONGOPT_CAPABILITIES, - LONGOPT_OOM_SCORE_ADJ, - LONGOPT_NO_NEW_PRIVS, - LONGOPT_SECBITS, -}; - -const char *applet = NULL; -const char *extraopts = NULL; -const char getoptstring[] = "A:a:D:d:e:g:H:I:Kk:m:N:p:R:r:s:Su:1:2:3" \ - getoptstring_COMMON; -const struct option longopts[] = { - { "healthcheck-timer", 1, NULL, 'a'}, - { "healthcheck-delay", 1, NULL, 'A'}, - { "capabilities", 1, NULL, LONGOPT_CAPABILITIES}, - { "secbits", 1, NULL, LONGOPT_SECBITS}, - { "no-new-privs", 0, NULL, LONGOPT_NO_NEW_PRIVS}, - { "respawn-delay", 1, NULL, 'D'}, - { "chdir", 1, NULL, 'd'}, - { "env", 1, NULL, 'e'}, - { "group", 1, NULL, 'g'}, - { "ionice", 1, NULL, 'I'}, - { "stop", 0, NULL, 'K'}, - { "umask", 1, NULL, 'k'}, - { "respawn-max", 1, NULL, 'm'}, - { "nicelevel", 1, NULL, 'N'}, - { "oom-score-adj",1, NULL, LONGOPT_OOM_SCORE_ADJ}, - { "pidfile", 1, NULL, 'p'}, - { "respawn-period", 1, NULL, 'P'}, - { "retry", 1, NULL, 'R'}, - { "chroot", 1, NULL, 'r'}, - { "signal", 1, NULL, 's'}, - { "start", 0, NULL, 'S'}, - { "user", 1, NULL, 'u'}, - { "stdout", 1, NULL, '1'}, - { "stderr", 1, NULL, '2'}, - { "reexec", 0, NULL, '3'}, - longopts_COMMON -}; -const char * const longopts_help[] = { - "set an initial health check delay", - "set a health check timer", - "Set the inheritable, ambient and bounding capabilities", - "Set the security-bits for the program", - "Set the No New Privs flag for the program", - "Set a respawn delay", - "Change the PWD", - "Set an environment string", - "Change the process group", - "Set an ionice class:data when starting", - "Stop daemon", - "Set the umask for the daemon", - "set maximum number of respawn attempts", - "Set a nicelevel when starting", - "Set OOM score adjustment when starting", - "Match pid found in this file", - "Set respawn time period", - "Retry schedule to use when stopping", - "Chroot to this directory", - "Send a signal to the daemon", - "Start daemon", - "Change the process user", - "Redirect stdout to file", - "Redirect stderr to file", - "reexec (used internally)", - longopts_help_COMMON -}; -const char *usagestring = NULL; - -static int healthcheckdelay = 0; -static int healthchecktimer = 0; -static volatile sig_atomic_t do_healthcheck = 0; -static volatile sig_atomic_t exiting = 0; -static int nicelevel = INT_MIN; -static int ionicec = -1; -static int ioniced = 0; -static int oom_score_adj = INT_MIN; -static char *changeuser, *ch_root, *ch_dir; -static uid_t uid = 0; -static gid_t gid = 0; -static int devnull_fd = -1; -static int stdin_fd; -static int stdout_fd; -static int stderr_fd; -static char *redirect_stderr = NULL; -static char *redirect_stdout = NULL; -#ifdef TIOCNOTTY -static int tty_fd = -1; -#endif -static pid_t child_pid; -static int respawn_count = 0; -static int respawn_delay = 0; -static int respawn_max = 10; -static int respawn_period = 0; -static char *fifopath = NULL; -static int fifo_fd = 0; -static char *pidfile = NULL; -static char *svcname = NULL; -static bool verbose = false; -#ifdef HAVE_CAP -static cap_iab_t cap_iab = NULL; -static unsigned secbits = 0; -#endif -#ifdef PR_SET_NO_NEW_PRIVS -static bool no_new_privs = false; -#endif - -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 _unused, int who _unused, - int ioprio _unused) -{ -#ifdef SYS_ioprio_set - return syscall(SYS_ioprio_set, which, who, ioprio); -#else - return 0; -#endif -} -#endif - -static void cleanup(void) -{ - free(changeuser); -} - -static void re_exec_supervisor(void) -{ - syslog(LOG_WARNING, "Re-executing for %s", svcname); - execlp("supervise-daemon", "supervise-daemon", svcname, "--reexec", - (char *) NULL); - syslog(LOG_ERR, "Unable to execute supervise-daemon: %s", - strerror(errno)); - exit(EXIT_FAILURE); -} - -static void handle_signal(int sig) -{ - int serrno = errno; - pid_t pid; - - switch (sig) { - case SIGALRM: - do_healthcheck = 1; - break; - case SIGCHLD: - if (exiting) - while (waitpid((pid_t)(-1), NULL, WNOHANG) > 0) {} - else { - while ((pid = waitpid((pid_t)(-1), NULL, WNOHANG|WNOWAIT)) > 0) { - if (pid == child_pid) - break; - pid = waitpid(pid, NULL, WNOHANG); - } - } - break; - case SIGTERM: - exiting = 1; - break; - default: - syslog(LOG_WARNING, "caught signal %d", sig); - re_exec_supervisor(); - } - /* Restore errno */ - errno = serrno; -} - -static char * expand_home(const char *home, const char *path) -{ - char *opath, *ppath, *p, *nh; - 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); - } - - xasprintf(&nh, "%s%s", home, ppath); - free(opath); - return nh; -} - -static char *make_cmdline(char **argv) -{ - char **c; - char *cmdline = NULL; - size_t len = 0; - - for (c = argv; c && *c; c++) - len += (strlen(*c) + 1); - cmdline = xmalloc(len+1); - memset(cmdline, 0, len+1); - for (c = argv; c && *c; c++) { - strcat(cmdline, *c); - strcat(cmdline, " "); - } - return cmdline; -} - -static pid_t exec_command(const char *cmd) -{ - char *file; - pid_t pid = -1; - sigset_t full; - sigset_t old; - struct sigaction sa; - - file = rc_service_resolve(svcname); - if (!exists(file)) { - free(file); - return 0; - } - - /* 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 == 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, cmd, (char *) NULL); - syslog(LOG_ERR, "unable to exec `%s': %s\n", - file, strerror(errno)); - _exit(EXIT_FAILURE); - } - - if (pid == -1) - syslog(LOG_ERR, "fork: %s\n",strerror (errno)); - - sigprocmask(SIG_SETMASK, &old, NULL); - free(file); - return pid; -} - -static void child_process(char *exec, char **argv) -{ - RC_STRINGLIST *env_list; - RC_STRING *env; - int i; - char *p; - char *token; - size_t len; - char *newpath; - char *np; - char *cmdline = NULL; - time_t start_time; - char start_count_string[20]; - char start_time_string[20]; - FILE *fp; - -#ifdef HAVE_PAM - pam_handle_t *pamh = NULL; - int pamr; - const char *const *pamenv = NULL; -#endif - - setsid(); - - if (svcname) { - start_time = time(NULL); - from_time_t(start_time_string, start_time); - rc_service_value_set(svcname, "start_time", start_time_string); - sprintf(start_count_string, "%i", respawn_count); - rc_service_value_set(svcname, "start_count", start_count_string); - sprintf(start_count_string, "%d", getpid()); - rc_service_value_set(svcname, "child_pid", start_count_string); - } - - if (nicelevel != INT_MIN) { - if (setpriority(PRIO_PROCESS, getpid(), nicelevel) == -1) - eerrorx("%s: setpriority %d: %s", applet, nicelevel, - strerror(errno)); - } - - if (ionicec != -1 && ioprio_set(1, getpid(), ionicec | ioniced) == -1) - eerrorx("%s: ioprio_set %d %d: %s", applet, ionicec, ioniced, - strerror(errno)); - - if (oom_score_adj != INT_MIN) { - fp = fopen("/proc/self/oom_score_adj", "w"); - if (!fp) - eerrorx("%s: oom_score_adj %d: %s", applet, - oom_score_adj, strerror(errno)); - fprintf(fp, "%d\n", oom_score_adj); - fclose(fp); - } - - 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)); - -#ifdef HAVE_PAM - if (changeuser != NULL) { - pamr = pam_start("supervise-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); -#ifdef HAVE_CAP - if (uid && cap_setuid(uid)) -#else - if (uid && setuid(uid)) -#endif - eerrorx ("%s: unable to set userid to %d", applet, uid); - - /* Close any fd's to the passwd database */ - endpwent(); - -#ifdef HAVE_CAP - if (cap_iab != NULL) { - i = cap_iab_set_proc(cap_iab); - - if (cap_free(cap_iab) != 0) - eerrorx("Could not releasable memory: %s", strerror(errno)); - - if (i != 0) - eerrorx("Could not set iab: %s", strerror(errno)); - } - - if (secbits != 0) { - if (cap_set_secbits(secbits) < 0) - eerrorx("Could not set securebits to 0x%x: %s", secbits, strerror(errno)); - } -#endif - -#ifdef PR_SET_NO_NEW_PRIVS - if (no_new_privs) { - if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1) - eerrorx("Could not set No New Privs flag: %s", strerror(errno)); - } -#endif - - /* remove the controlling tty */ -#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=", 11) != 0 && - strncmp(env->value, "RC_SVCNAME=", 11) != 0) || - strncmp(env->value, "SSD_NICELEVEL=", 14) == 0 || - strncmp(env->value, "SSD_IONICELEVEL=", 16) == 0 || - strncmp(env->value, "SSD_OOM_SCORE_ADJ=", 18) == 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); - } - - stdin_fd = devnull_fd; - 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)); - } - - dup2(stdin_fd, STDIN_FILENO); - if (redirect_stdout || rc_yesno(getenv("EINFO_QUIET"))) - dup2(stdout_fd, STDOUT_FILENO); - if (redirect_stderr || rc_yesno(getenv("EINFO_QUIET"))) - dup2(stderr_fd, STDERR_FILENO); - - for (i = getdtablesize() - 1; i >= 3; --i) - fcntl(i, F_SETFD, FD_CLOEXEC); - cmdline = make_cmdline(argv); - syslog(LOG_INFO, "Child command line: %s", cmdline); - free(cmdline); - 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)); -} - -static void supervisor(char *exec, char **argv) -{ - FILE *fp; - char buf[2048]; - char cmd[2048]; - int count; - int failing; - int health_status; - int healthcheck_respawn; - int i; - int nkilled; - int sig_send; - pid_t health_pid; - pid_t wait_pid; - sigset_t old_signals; - sigset_t signals; - struct sigaction sa; - struct timespec ts; - time_t respawn_now= 0; - time_t first_spawn= 0; - - /* block all signals we do not handle */ - sigfillset(&signals); - sigdelset(&signals, SIGALRM); - sigdelset(&signals, SIGCHLD); - sigdelset(&signals, SIGTERM); - sigprocmask(SIG_SETMASK, &signals, &old_signals); - - /* install signal handler */ - memset(&sa, 0, sizeof(sa)); - sa.sa_handler = handle_signal; - sigaction(SIGALRM, &sa, NULL); - sigaction(SIGCHLD, &sa, NULL); - sigaction(SIGTERM, &sa, NULL); - - fp = fopen(pidfile, "w"); - if (!fp) - eerrorx("%s: fopen `%s': %s", applet, pidfile, strerror(errno)); - fprintf(fp, "%d\n", getpid()); - fclose(fp); - - if (svcname) - rc_service_daemon_set(svcname, exec, (const char * const *) argv, - pidfile, true); - - /* remove the controlling tty */ -#ifdef TIOCNOTTY - ioctl(tty_fd, TIOCNOTTY, 0); - close(tty_fd); -#endif - - /* - * Supervisor main loop - */ - if (healthcheckdelay) - alarm(healthcheckdelay); - else if (healthchecktimer) - alarm(healthchecktimer); - failing = 0; - while (!exiting) { - healthcheck_respawn = 0; - fifo_fd = open(fifopath, O_RDONLY); - if (fifo_fd > 0) { - memset(buf, 0, sizeof(buf)); - count = read(fifo_fd, buf, sizeof(buf) - 1); - close(fifo_fd); - if (count != -1) - buf[count] = 0; - if (count == 0) - continue; - if (verbose) - syslog(LOG_DEBUG, "Received %s from fifo", buf); - if (strncasecmp(buf, "sig", 3) == 0) { - if ((sscanf(buf, "%s %d", cmd, &sig_send) == 2) - && (sig_send >= 0 && sig_send < NSIG)) { - syslog(LOG_INFO, "Sending signal %d to %d", sig_send, - child_pid); - if (kill(child_pid, sig_send) == -1) - syslog(LOG_ERR, "Unable to send signal %d to %d", - sig_send, child_pid); - } - } - continue; - } - if (do_healthcheck) { - do_healthcheck = 0; - alarm(0); - if (verbose) - syslog(LOG_DEBUG, "running health check for %s", svcname); - health_pid = exec_command("healthcheck"); - health_status = rc_waitpid(health_pid); - if (WIFEXITED(health_status) && WEXITSTATUS(health_status) == 0) - alarm(healthchecktimer); - else { - syslog(LOG_WARNING, "health check for %s failed", svcname); - health_pid = exec_command("unhealthy"); - rc_waitpid(health_pid); - syslog(LOG_INFO, "stopping %s, pid %d", exec, child_pid); - nkilled = run_stop_schedule(applet, NULL, NULL, child_pid, 0, - false, false, true); - if (nkilled < 0) - syslog(LOG_INFO, "Unable to kill %d: %s", - child_pid, strerror(errno)); - else - healthcheck_respawn = 1; - } - } - if (exiting) { - alarm(0); - syslog(LOG_INFO, "stopping %s, pid %d", exec, child_pid); - nkilled = run_stop_schedule(applet, NULL, NULL, child_pid, 0, - false, false, true); - if (nkilled > 0) - syslog(LOG_INFO, "killed %d processes", nkilled); - continue; - } - wait_pid = waitpid(child_pid, &i, WNOHANG); - if (wait_pid == child_pid) { - if (WIFEXITED(i)) - syslog(LOG_WARNING, "%s, pid %d, exited with return code %d", - exec, child_pid, WEXITSTATUS(i)); - else if (WIFSIGNALED(i)) - syslog(LOG_WARNING, "%s, pid %d, terminated by signal %d", - exec, child_pid, WTERMSIG(i)); - } - if (wait_pid == child_pid || healthcheck_respawn) { - do_healthcheck = 0; - healthcheck_respawn = 0; - alarm(0); - respawn_now = time(NULL); - if (first_spawn == 0) - first_spawn = respawn_now; - if ((respawn_period > 0) - && (respawn_now - first_spawn > respawn_period)) { - respawn_count = 0; - first_spawn = 0; - } else - respawn_count++; - if (respawn_max > 0 && respawn_count > respawn_max) { - syslog(LOG_WARNING, "respawned \"%s\" too many times, exiting", - exec); - exiting = 1; - failing = 1; - continue; - } - ts.tv_sec = respawn_delay; - ts.tv_nsec = 0; - nanosleep(&ts, NULL); - if (exiting) - continue; - child_pid = fork(); - if (child_pid == -1) { - syslog(LOG_ERR, "%s: fork: %s", applet, strerror(errno)); - exit(EXIT_FAILURE); - } - if (child_pid == 0) { - sigprocmask(SIG_SETMASK, &old_signals, NULL); - memset(&sa, 0, sizeof(sa)); - sa.sa_handler = SIG_DFL; - sigaction(SIGALRM, &sa, NULL); - sigaction(SIGCHLD, &sa, NULL); - sigaction(SIGTERM, &sa, NULL); - child_process(exec, argv); - } - if (healthcheckdelay) - alarm(healthcheckdelay); - else if (healthchecktimer) - alarm(healthchecktimer); - } - } - - if (svcname) { - rc_service_daemon_set(svcname, exec, (const char *const *)argv, - pidfile, false); - rc_service_value_set(svcname, "child_pid", NULL); - rc_service_mark(svcname, RC_SERVICE_STOPPED); - if (failing) - rc_service_mark(svcname, RC_SERVICE_FAILED); - } - if (pidfile && exists(pidfile)) - unlink(pidfile); - if (fifopath && exists(fifopath)) - unlink(fifopath); - exit(EXIT_SUCCESS); -} - -int main(int argc, char **argv) -{ - int opt; - char **c; - int x; - bool start = false; - bool stop = false; - bool reexec = false; - bool sendsig = false; - char *exec = NULL; - char *retry = NULL; - int sig = SIGTERM; - char *home = NULL; - int tid = 0; - pid_t pid; - char *tmp; - char *p; - char *token; - int i; - int n; - char *exec_file = NULL; - char *varbuf = NULL; - struct timespec ts; - struct passwd *pw; - struct group *gr; - FILE *fp; - mode_t numask = 022; - int child_argc = 0; - char **child_argv = NULL; - char *str = NULL; - char *cmdline = NULL; - - applet = basename_c(argv[0]); - atexit(cleanup); - svcname = getenv("RC_SVCNAME"); - if (!svcname) - eerrorx("%s: The RC_SVCNAME environment variable is not set", applet); - openlog(applet, LOG_PID, LOG_DAEMON); - - if (argc <= 1 || strcmp(argv[1], svcname)) - eerrorx("%s: the first argument is %s and must be %s", - applet, argv[1], svcname); - - if ((tmp = getenv("SSD_NICELEVEL"))) - if (sscanf(tmp, "%d", &nicelevel) != 1) - eerror("%s: invalid nice level `%s' (SSD_NICELEVEL)", - applet, tmp); - if ((tmp = getenv("SSD_IONICELEVEL"))) { - int n = sscanf(tmp, "%d:%d", &ionicec, &ioniced); - if (n != 1 && n != 2) - eerror("%s: invalid ionice level `%s' (SSD_IONICELEVEL)", - applet, tmp); - if (ionicec == 0) - ioniced = 0; - else if (ionicec == 3) - ioniced = 7; - ionicec <<= 13; /* class shift */ - } - if ((tmp = getenv("SSD_OOM_SCORE_ADJ"))) - if (sscanf(tmp, "%d", &oom_score_adj) != 1) - eerror("%s: invalid oom_score_adj `%s' (SSD_OOM_SCORE_ADJ)", - 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; - } - } - } - - cmdline = make_cmdline(argv); - if (svcname) { - argc--; - argv++; - } - while ((opt = getopt_long(argc, argv, getoptstring, longopts, - (int *) 0)) != -1) - switch (opt) { - case 'a': /* --healthcheck-timer