Compare commits

..

76 Commits
0.29 ... 0.34.6

Author SHA1 Message Date
William Hubbs
b98e83ee91 Update ChangeLog 2017-11-07 16:01:08 -06:00
William Hubbs
d59197fafe start-stop-daemon: do not use do_stop to verify whether a daemon is running
X-Gentoo-Bug: 636574
X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=636574
2017-11-07 15:38:11 -06:00
William Hubbs
b66c86c9ee version 0.34.6 2017-11-07 15:37:43 -06:00
William Hubbs
f7eb236f6f Update ChangeLog 2017-10-30 18:23:40 -05:00
William Hubbs
1936d73eb1 fix issue with --reexec call 2017-10-30 18:23:11 -05:00
William Hubbs
58872fc090 version 0.34.5 2017-10-30 18:23:00 -05:00
William Hubbs
fc35eb90ca Update ChangeLog 2017-10-27 19:28:31 -05:00
William Hubbs
b18be3f970 supervise-daemon: use RC_SVCNAME as the first argument to the daemon
This makes ps show which service the supervisor is monitoring.
2017-10-27 19:25:40 -05:00
William Hubbs
027438f775 supervise-daemon: fix logging for reexec and the child command line 2017-10-27 17:36:56 -05:00
William Hubbs
93e159ae85 log as supervise-daemon not the service 2017-10-27 15:32:26 -05:00
William Hubbs
bcfcf50562 version 0.34.4 2017-10-27 10:46:46 -05:00
William Hubbs
d0097cc10f Update ChangeLog 2017-10-26 14:16:36 -05:00
William Hubbs
eecf868e3c supervise-daemon: clarify a log message 2017-10-26 14:15:40 -05:00
William Hubbs
a5cd486a7f Update ChangeLog 2017-10-26 14:03:55 -05:00
William Hubbs
6e6b4ac5fa supervise-daemon: log the command line we run to spawn the child process 2017-10-26 14:01:07 -05:00
William Hubbs
558ff4d5fb supervise-daemon: log with the service name instead of "supervise-daemon" 2017-10-26 14:01:07 -05:00
William Hubbs
490f855aef implement "unsupervised" status
The unsupervised status is to be used when a supervisor of a supervised
service dies but leaves the service daemon itself running.
2017-10-26 13:18:16 -05:00
William Hubbs
d1491e201d supervise-daemon: remove child_pid from saved options during shutdown
This allows us to detect when the supervisor dies unexpectedly because
in that case child_pid will still exist.
2017-10-26 13:18:16 -05:00
William Hubbs
3231af9375 rc_service_value_set: remove the option if NULL is the value
This allows the equivalent of "unsetting" a value for a service.
2017-10-26 13:18:16 -05:00
William Hubbs
a5758e7aef supervise-daemon.sh: fix status function with no namespaces 2017-10-26 13:18:16 -05:00
Patrick McLean
61a9393ce1 cgroups_cleanup: clean up shutdown signaling
- do not sleep for the full 90 seconds if processes are dead
- re-arrange the order of signals we attempt to send to the processes
2017-10-26 13:18:16 -05:00
William Hubbs
161d22cb07 version 0.34.3 2017-10-26 13:17:54 -05:00
William Hubbs
3a96ca1c96 Update ChangeLog 2017-10-25 15:15:53 -05:00
William Hubbs
934530914b add "unsupervised" status and return code 64 to supervise-daemon status function
This is to be used if the service is being supervised and the
supervisor is somehow killed.

Currently, this is very linux specific, but I will expand to other
platforms, patches are welcome.
2017-10-25 15:14:33 -05:00
William Hubbs
b717625cd2 version 0.34.2 2017-10-25 15:13:09 -05:00
William Hubbs
b475396134 Update ChangeLog 2017-10-24 17:02:45 -05:00
William Hubbs
e7b1d898ca supervise-daemon: fix build issue for >=glibc-2.26
X-Gentoo-Bug: 635334
X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=635334
2017-10-24 17:02:14 -05:00
William Hubbs
5cd09a6f44 version 0.34.1 2017-10-24 17:00:57 -05:00
William Hubbs
f3c70bf5b5 Update ChangeLog 2017-10-24 10:42:11 -05:00
William Hubbs
f5acc66db7 rc_find_pids: ignore pids that are not in our pid namespace
X-Gentoo-Bug: 634634
X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=634634
2017-10-24 10:37:37 -05:00
William Hubbs
fdce4769f2 supervise-daemon: multiple fixes
- Harden against dying by handling all signals that would terminate the
program and adding --reexec support
- factor the supervisor into its own function
- fix test for whether we are already running
2017-10-24 10:26:18 -05:00
William Hubbs
35b1996704 supervise-daemon: elevate some log messages to warnings
Prior to this change, we were logging unexpected terminations of daemons
we were supervising at the info level. This change moves the logs to
warnings.
2017-10-18 18:07:50 -05:00
William Hubbs
3c8e7ed255 version 0.34 2017-10-13 16:10:57 -05:00
William Hubbs
acaed1f910 Update ChangeLog 2017-10-13 11:29:49 -05:00
William Hubbs
91109e31d8 update news 2017-10-12 18:54:17 -05:00
William Hubbs
2b6eeea01d man: remove service(8) man page 2017-10-12 18:47:36 -05:00
William Hubbs
a15de23e57 typo fix 2017-10-10 10:56:27 -05:00
William Hubbs
efa9ba485d init.d/sysfs.in: fix reference to RC_LIBEXECDIR
The sysfs init script referred to @LIBEXECDIR@ before this change, but
it is better to refer to RC_LIBEXECDIR so that we get rid of a sed
substitution.
2017-10-09 15:54:06 -05:00
d4ddd72701 add option to make agetty startup quiet
This fixes #150
2017-10-06 14:43:59 -05:00
William Hubbs
1e9af2cd42 fix compiler warning 2017-10-05 18:31:07 -05:00
William Hubbs
3c05db74f6 remove service binary
The service binary was just a synonym for rc-service, so use rc-service
instead of service. If you want a "service" binary, it should be
something that can determine which service manager you are running and
run the appropriate service manager commands.
2017-10-02 18:09:09 -05:00
William Hubbs
edc54b0377 version 0.33 2017-10-02 16:58:59 -05:00
William Hubbs
8e53a3fa8a Update ChangeLog 2017-10-02 11:21:29 -05:00
William Hubbs
7f3b413111 use printf consistently in cgroups handling
This makes the cgroups handling consistent between cgroups v1 and v2.
Also, it fixes #167.
2017-09-29 12:51:12 -05:00
William Hubbs
1ccba05658 sh/rc-functions.sh: add need_if_exists convenience function 2017-09-22 17:22:50 -05:00
William Hubbs
c46adf1434 man/openrc-run.8: Clarify the explanation of the need dependency 2017-09-22 16:24:20 -05:00
William Hubbs
1cac8b080c ignore sigchld when shutting down the supervised process
We need to do this to skip the zombie state for the child process since
we are not easily able to wait() for it.
2017-09-18 16:59:18 -05:00
William Hubbs
b58194ef63 typo fix 2017-09-18 13:30:56 -05:00
William Hubbs
b28c0d6f66 typo fix 2017-09-18 13:03:34 -05:00
William Hubbs
3cf19b0f30 supervise-daemon: code cleanup
Clean up the process for killing an active supervisor when stopping.
2017-09-18 12:25:37 -05:00
William Hubbs
0eb47b9af3 initialize the stop schedule 2017-09-18 10:36:17 -05:00
William Hubbs
4ab60ff109 rc-schedules.c: pass the correct pid to rc_find_pids
This is for #163.
2017-09-17 14:20:05 -05:00
Jason Zaman
db4a578273 selinux: fix const qualifier warning
rc-selinux.c: In function ‘selinux_setup’:
rc-selinux.c:361:9: warning: assignment discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
  curr_t = context_type_get(curr_con);
         ^
2017-09-17 11:41:24 -05:00
Jason Zaman
b1c3422f45 selinux: use openrc contexts path to get contexts
The minimum requirement for libselinux is now >=2.6
The refpolicy and the gentoo policy contain the
contexts since version 2.20170204-r4
2017-09-17 11:40:07 -05:00
William Hubbs
3fafd7a76e sysfs: fix cgroup hybrid mode
In hybrid mode, we should not try to mount cgroup2 if it is not
available in the kernel.

This fixes #164.
2017-09-16 17:02:52 -05:00
William Hubbs
cd5722aca5 cgroup2_find_path: use legacy mode if cgroup2 is not in the kernel
This is related to #164.
2017-09-16 16:49:22 -05:00
William Hubbs
dcb4a4d261 version 0.32 2017-09-16 13:24:52 -05:00
William Hubbs
e312e56997 Update ChangeLog 2017-09-15 15:33:24 -05:00
William Hubbs
2f60a959b4 update news file 2017-09-15 14:25:33 -05:00
William Hubbs
25b45a5a23 cgroup_cleanup: try to remove the cgroup version 2 cgroup
If we were able to kill all the processes in the cgroup, it should be
removed.
2017-09-15 14:22:34 -05:00
William Hubbs
4651b8c7e9 rc-cgroup.sh: cgroup_cleanup fix error handling
cgroup_cleanup should warn if it is unable to clean up all processes in
the control group, but it will always return success.
2017-09-15 13:42:50 -05:00
William Hubbs
50608b54ed rc-cgroup.sh: fix signal names
The "SIG" prefix on signal names passed to kill -s isn't portable.
2017-09-15 13:28:15 -05:00
William Hubbs
b0a077a35f add quiet switch to do_stop in src-schedules.c
This allows supervise-daemon to run this code without attempting to
print some status messages used by start-stop-daemon.
2017-09-14 18:24:39 -05:00
William Hubbs
6a5ca2ab36 make the procedure for killing child processes of services configurable 2017-09-14 16:17:20 -05:00
William Hubbs
2b0345165e Make cgroup_cleanup send only one sigterm and sigkill
Instead of looping and sending multiple signals to child processes in
cgroup_cleanup, we send sigterm followed by sleeping one second then
sigkill.

This brings us more in line with systemd's "control group" killmode
setting.

Also, this commit includes several shellcheck cleanups.
2017-09-14 10:55:06 -05:00
William Hubbs
8885580986 rc-cgroup.sh: move cgroup_cleanup to the end of the file 2017-09-14 10:44:52 -05:00
William Hubbs
6d7713a758 guide.md: clarify cgroups documentation
Update the documentation to reflect cgroups version 2 support.
Also, add a section on dealing with orphaned service processes.

This fixes #94.
2017-09-14 10:39:38 -05:00
William Hubbs
457f928e79 add support for control groups version 2
This is for #94.
2017-09-14 10:38:10 -05:00
William Hubbs
a71a461e45 version 0.31 2017-09-07 11:28:09 -05:00
William Hubbs
a09b8af3f9 Update ChangeLog 2017-09-06 17:31:01 -05:00
William Hubbs
382efdbfcb add quiet parameter to run_stop_schedule 2017-09-06 17:22:35 -05:00
William Hubbs
17b5cc78d3 add retry option to supervise-daemon
The --retry option for supervise-daemon defines how the supervisor will
attempt to stop the child process it is monitoring. It is defined when
the supervisor is started since stopping the supervisor just sends a
signal to the active supervisor.

This fixes #160.
2017-09-06 17:22:21 -05:00
William Hubbs
36a0ab9054 make run_stop_schedule accept a pid instead of a pid file 2017-08-25 11:36:45 -05:00
William Hubbs
27c2bd997d version 0.30 2017-08-24 11:44:32 -05:00
William Hubbs
d7938f54f2 start-stop-daemon: move --retry processing code to a shared module
This was part of start-stop-daemon; however, it needs to be shared in
order to be used by supervise-daemon.
2017-08-24 11:34:18 -05:00
William Hubbs
cfbe9c2ede move get_pid function to a shared file 2017-08-23 14:36:50 -05:00
28 changed files with 1876 additions and 1263 deletions

1045
ChangeLog

File diff suppressed because it is too large Load Diff

View File

@@ -1,3 +1,3 @@
NAME= openrc NAME= openrc
VERSION= 0.29 VERSION= 0.34.6
PKG= ${NAME}-${VERSION} PKG= ${NAME}-${VERSION}

15
NEWS.md
View File

@@ -3,6 +3,21 @@
This file will contain a list of notable changes for each release. Note This file will contain a list of notable changes for each release. Note
the information in this file is in reverse order. the information in this file is in reverse order.
## OpenRC 0.33
This version removes the "service" binary which was just a copy of
"rc-service" provided for compatibility.
If you still need the "service" binary, as opposed to "rc-service", it is
recommended that you use something like Debian's init-system-helpers.
Otherwise, just use "rc-service" in place of "service".
## OpenRC 0.31
This version adds support for Control Groups version 2, which is
considered stable as of Linux-4.13. Please see /etc/rc.conf for
documentation on how to configure control groups.
## OpenRC-0.28 ## OpenRC-0.28
This version mounts efivars read only due to concerns about changes in This version mounts efivars read only due to concerns about changes in

View File

@@ -1,3 +1,6 @@
# make agetty quiet
#quiet="yes"
# Set the baud rate of the terminal line # Set the baud rate of the terminal line
#baud="" #baud=""

View File

@@ -191,13 +191,43 @@ rc_tty_number=12
############################################################################## ##############################################################################
# LINUX CGROUPS RESOURCE MANAGEMENT # LINUX CGROUPS RESOURCE MANAGEMENT
# If you have cgroups turned on in your kernel, this switch controls # This sets the mode used to mount cgroups.
# whether or not a group for each controller is mounted under # "hybrid" mounts cgroups version 2 on /sys/fs/cgroup/unified and
# /sys/fs/cgroup. # cgroups version 1 on /sys/fs/cgroup.
# None of the other options in this section work if this is set to "NO". # "legacy" mounts cgroups version 1 on /sys/fs/cgroup
# "unified" mounts cgroups version 2 on /sys/fs/cgroup
#rc_cgroup_mode="hybrid"
# This is a list of controllers which should be enabled for cgroups version 2.
# If hybrid mode is being used, controllers listed here will not be
# available for cgroups version 1.
# This is a global setting.
#rc_cgroup_controllers=""
# This variable contains the cgroups version 2 settings for your services.
# If this is set in this file, the settings will apply to all services.
# If you want different settings for each service, place the settings in
# /etc/conf.d/foo for service foo.
# The format is to specify the setting and value followed by a newline.
# Multiple settings and values can be specified.
# For example, you would use this to set the maximum memory and maximum
# number of pids for a service.
#rc_cgroup_settings="
#memory.max 10485760
#pids.max max
#"
#
# For more information about the adjustments that can be made with
# cgroups version 2, see Documentation/cgroups-v2.txt in the linux kernel
# source tree.
#rc_cgroup_settings=""
# This switch controls whether or not cgroups version 1 controllers are
# individually mounted under
# /sys/fs/cgroup in hybrid or legacy mode.
#rc_controller_cgroups="YES" #rc_controller_cgroups="YES"
# The following settings allow you to set up values for the cgroup # The following settings allow you to set up values for the cgroups version 1
# controllers for your services. # controllers for your services.
# They can be set in this file;, however, if you do this, the settings # They can be set in this file;, however, if you do this, the settings
# will apply to all of your services. # will apply to all of your services.
@@ -211,8 +241,9 @@ rc_tty_number=12
# cpu.shares 512 # cpu.shares 512
# " # "
# #
#For more information about the adjustments that can be made with # For more information about the adjustments that can be made with
#cgroups, see Documentation/cgroups/* in the linux kernel source tree. # cgroups version 1, see Documentation/cgroups-v1/* in the linux kernel
# source tree.
# Set the blkio controller settings for this service. # Set the blkio controller settings for this service.
#rc_cgroup_blkio="" #rc_cgroup_blkio=""
@@ -246,10 +277,33 @@ rc_tty_number=12
# Set this to YES if you want all of the processes in a service's cgroup # Set this to YES if you want all of the processes in a service's cgroup
# killed when the service is stopped or restarted. # killed when the service is stopped or restarted.
# This should not be set globally because it kills all of the service's # Be aware that setting this to yes means all of a service's
# child processes, and most of the time this is undesirable. Please set # child processes will be killed. Keep this in mind if you set this to
# it in /etc/conf.d/<service>. # yes here instead of for the individual services in
# /etc/conf.d/<service>.
# To perform this cleanup manually for a stopped service, you can # To perform this cleanup manually for a stopped service, you can
# execute cgroup_cleanup with /etc/init.d/<service> cgroup_cleanup or # execute cgroup_cleanup with /etc/init.d/<service> cgroup_cleanup or
# rc-service <service> cgroup_cleanup. # rc-service <service> cgroup_cleanup.
# The process followed in this cleanup is the following:
# 1. send stopsig (sigterm if it isn't set) to all processes left in the
# cgroup immediately followed by sigcont.
# 2. Send sighup to all processes in the cgroup if rc_send_sighup is
# yes.
# 3. delay for rc_timeout_stopsec seconds.
# 4. send sigkill to all processes in the cgroup unless disabled by
# setting rc_send_sigkill to no.
# rc_cgroup_cleanup="NO" # rc_cgroup_cleanup="NO"
# If this is yes, we will send sighup to the processes in the cgroup
# immediately after stopsig and sigcont.
#rc_send_sighup="NO"
# This is the amount of time in seconds that we delay after sending sigcont
# and optionally sighup, before we optionally send sigkill to all
# processes in the # cgroup.
# The default is 90 seconds.
#rc_timeout_stopsec="90"
# If this is set to no, we do not send sigkill to all processes in the
# cgroup.
#rc_send_sigkill="YES"

View File

@@ -53,9 +53,6 @@ Calling `openrc` without any arguments will try to reset all services so
that the current runlevel is satisfied; if you manually started apache it will be that the current runlevel is satisfied; if you manually started apache it will be
stopped, and if squid died but is in the current runlevel it'll be restarted. stopped, and if squid died but is in the current runlevel it'll be restarted.
There is a `service` helper that emulates the syntax seen on e.g. older Redhat
and Ubuntu (`service nginx start` etc.)
# Runlevels # Runlevels
OpenRC has a concept of runlevels, similar to what sysvinit historically OpenRC has a concept of runlevels, similar to what sysvinit historically
@@ -241,17 +238,36 @@ messages to a file), and a few others.
# ulimit and CGroups # ulimit and CGroups
Setting `ulimit` and `nice` values per service can be done through the `rc_ulimit` Setting `ulimit` and `nice` values per service can be done through the
variable. `rc_ulimit` variable.
Under Linux, OpenRC can optionally use CGroups for process management. Under Linux, OpenRC can use cgroups for process management as well. Once
By default each service script's processes are migrated to their own CGroup. the kernel is configured appropriately, the `rc_cgroup_mode` setting in
/etc/rc.conf should be used to control whether cgroups version one,,
two, or both are used. The default is to use both if they are available.
By changing certain settings in the service's `conf.d` file limits can be
enforced per service. These settings are documented in detail in the
default /etc/rc.conf under `LINUX CGROUPS RESOURCE MANAGEMENT`.
# Dealing with Orphaned Processes
It is possible to get into a state where there are orphaned processes
running which were part of a service. For example, if you are monitoring
a service with supervise-daemon and supervise-daemon dies for an unknown
reason. The way to deal with this will be different for each system.
On Linux systems with cgroups enabled, the cgroup_cleanup command is
added to all services. You can run it manually, when the service is
stopped, by using:
```
# rc-service someservice cgroup_cleanup
```
The `rc_cgroup_cleanup` setting can be changed to yes to make this
happen automatically when the service is stopped.
By changing certain values in the `conf.d` file limits can be enforced per
service. It is easy to find orphan processes of a service that persist after
`stop()`, but by default these will NOT be terminated.
To change this add `rc_cgroup_cleanup="yes"` in the `conf.d` files for services
where you desire this functionality.
# Caching # Caching

View File

@@ -16,6 +16,7 @@ term_type="${term_type:-linux}"
command=/sbin/agetty command=/sbin/agetty
command_args_foreground="${agetty_options} ${port} ${baud} ${term_type}" command_args_foreground="${agetty_options} ${port} ${baud} ${term_type}"
pidfile="/run/${RC_SVCNAME}.pid" pidfile="/run/${RC_SVCNAME}.pid"
export EINFO_QUIET="${quiet:-yes}"
depend() { depend() {
after local after local

View File

@@ -1,4 +1,4 @@
S#!@BINDIR@/openrc-run #!@SBINDIR@/openrc-run
# Copyright (c) 2009-2015 The OpenRC Authors. # Copyright (c) 2009-2015 The OpenRC Authors.
# See the Authors file at the top-level directory of this distribution and # See the Authors file at the top-level directory of this distribution and
# https://github.com/OpenRC/openrc/blob/master/AUTHORS # https://github.com/OpenRC/openrc/blob/master/AUTHORS

View File

@@ -107,39 +107,114 @@ mount_misc()
fi fi
} }
mount_cgroups() cgroup1_base()
{ {
# set up kernel support for cgroups grep -qw cgroup /proc/filesystems || return 0
if [ -d /sys/fs/cgroup ] && ! mountinfo -q /sys/fs/cgroup; then if ! mountinfo -q /sys/fs/cgroup; then
if grep -qs cgroup /proc/filesystems; then ebegin "Mounting cgroup filesystem"
ebegin "Mounting cgroup filesystem" local opts="${sysfs_opts},mode=755,size=${rc_cgroupsize:-10m}"
local opts="${sysfs_opts},mode=755,size=${rc_cgroupsize:-10m}" mount -n -t tmpfs -o "${opts}" cgroup_root /sys/fs/cgroup
mount -n -t tmpfs -o ${opts} cgroup_root /sys/fs/cgroup eend $?
eend $?
fi
fi fi
mountinfo -q /sys/fs/cgroup || return 0
if ! mountinfo -q /sys/fs/cgroup/openrc; then if ! mountinfo -q /sys/fs/cgroup/openrc; then
local agent="@LIBEXECDIR@/sh/cgroup-release-agent.sh" local agent="${RC_LIBEXECDIR}/sh/cgroup-release-agent.sh"
mkdir /sys/fs/cgroup/openrc mkdir /sys/fs/cgroup/openrc
mount -n -t cgroup \ mount -n -t cgroup \
-o none,${sysfs_opts},name=openrc,release_agent="$agent" \ -o none,${sysfs_opts},name=openrc,release_agent="$agent" \
openrc /sys/fs/cgroup/openrc openrc /sys/fs/cgroup/openrc
printf 1 > /sys/fs/cgroup/openrc/notify_on_release printf 1 > /sys/fs/cgroup/openrc/notify_on_release
fi fi
return 0
}
yesno ${rc_controller_cgroups:-YES} && [ -e /proc/cgroups ] || return 0 cgroup1_controllers()
while read name hier groups enabled rest; do {
yesno "${rc_controller_cgroups:-YES}" && [ -e /proc/cgroups ] || return 0
while read -r name _ _ enabled rest; do
case "${enabled}" in case "${enabled}" in
1) mountinfo -q /sys/fs/cgroup/${name} && continue 1) mountinfo -q "/sys/fs/cgroup/${name}" && continue
mkdir /sys/fs/cgroup/${name} local x
mount -n -t cgroup -o ${sysfs_opts},${name} \ for x in $rc_cgroup_controllers; do
${name} /sys/fs/cgroup/${name} [ "${name}" = "blkio" ] && [ "${x}" = "io" ] &&
continue 2
[ "${name}" = "${x}" ] &&
continue 2
done
mkdir "/sys/fs/cgroup/${name}"
mount -n -t cgroup -o "${sysfs_opts},${name}" \
"${name}" "/sys/fs/cgroup/${name}"
;; ;;
esac esac
done < /proc/cgroups done < /proc/cgroups
return 0
}
cgroup2_base()
{
local base
base="$(cgroup2_find_path)"
mkdir -p "${base}"
mount -t cgroup2 none -o "${sysfs_opts},nsdelegate" "${base}" 2> /dev/null ||
mount -t cgroup2 none -o "${sysfs_opts}" "${base}"
return 0
}
cgroup2_controllers()
{
local active cgroup_path x y
cgroup_path="$(cgroup2_find_path)"
[ -z "${cgroup_path}" ] && return 0
[ -e "${cgroup_path}/cgroup.controllers" ] &&
read -r active < "${cgroup_path}/cgroup.controllers"
for x in ${rc_cgroup_controllers}; do
for y in ${active}; do
[ "$x" = "$y" ] &&
[ -e "${cgroup_path}/cgroup.subtree_control" ]&&
echo "+${x}" > "${cgroup_path}/cgroup.subtree_control"
done
done
return 0
}
cgroups_hybrid()
{
grep -qw cgroup /proc/filesystems || return 0
cgroup1_base
if grep -qw cgroup2 /proc/filesystems; then
cgroup2_base
cgroup2_controllers
fi
cgroup1_controllers
return 0
}
cgroups_legacy()
{
grep -qw cgroup /proc/filesystems || return 0
cgroup1_base
cgroup1_controllers
return 0
}
cgroups_unified()
{
cgroup2_base
cgroup2_controllers
return 0
}
mount_cgroups()
{
# set up kernel support for cgroups
if [ -d /sys/fs/cgroup ]; then
case "${rc_cgroup_mode:-hybrid}" in
hybrid) cgroups_hybrid ;;
legacy) cgroups_legacy ;;
unified) cgroups_unified ;;
esac
fi
return 0
} }
restorecon_sys() restorecon_sys()

View File

@@ -6,7 +6,7 @@ MAN3= einfo.3 \
rc_config.3 rc_deptree.3 rc_find_pids.3 rc_plugin_hook.3 \ rc_config.3 rc_deptree.3 rc_find_pids.3 rc_plugin_hook.3 \
rc_runlevel.3 rc_service.3 rc_stringlist.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 \ MAN8= rc-service.8 rc-status.8 rc-update.8 openrc.8 openrc-run.8 \
service.8 start-stop-daemon.8 supervise-daemon.8 start-stop-daemon.8 supervise-daemon.8
ifeq (${OS},Linux) ifeq (${OS},Linux)
MAN8 += rc-sstat.8 openrc-init.8 openrc-shutdown.8 MAN8 += rc-sstat.8 openrc-init.8 openrc-shutdown.8

View File

@@ -217,8 +217,10 @@ that dependency type to the function, or prefix the names with ! to
remove them from the dependencies. remove them from the dependencies.
.Bl -tag -width "RC_DEFAULTLEVEL" .Bl -tag -width "RC_DEFAULTLEVEL"
.It Ic need .It Ic need
The service will refuse to start until needed services have started and it The service will attempt to start any services it needs regardless of
will refuse to stop until any services that need it have stopped. whether they have been added to the runlevel. It will refuse to start
until all services it needs have started, and it will refuse to stop until all
services that need it have stopped.
.It Ic use .It Ic use
The service will attempt to start any services it uses that have been added The service will attempt to start any services it uses that have been added
to the runlevel. to the runlevel.

View File

@@ -1 +0,0 @@
.so rc-service.8

View File

@@ -36,6 +36,8 @@
.Ar pidfile .Ar pidfile
.Fl P , -respawn-period .Fl P , -respawn-period
.Ar seconds .Ar seconds
.Fl R , -retry
.Ar arg
.Fl r , -chroot .Fl r , -chroot
.Ar chrootpath .Ar chrootpath
.Fl u , -user .Fl u , -user
@@ -115,6 +117,9 @@ Modifies the scheduling priority of the daemon.
.It Fl P , -respawn-period Ar seconds .It Fl P , -respawn-period Ar seconds
Sets the length of a respawn period. The default is 10 seconds. See the Sets the length of a respawn period. The default is 10 seconds. See the
description of --respawn-max for more information. description of --respawn-max for more information.
.It Fl R , -retry Ar timeout | Ar signal Ns / Ns Ar timeout
The retry specification can be either a timeout in seconds or multiple
signal/timeout pairs (like SIGTERM/5).
.It Fl r , -chroot Ar path .It Fl r , -chroot Ar path
chroot to this directory before starting the daemon. All other paths, such chroot to this directory before starting the daemon. All other paths, such
as the path to the daemon, chdir and pidfile, should be relative to the chroot. as the path to the daemon, chdir and pidfile, should be relative to the chroot.

View File

@@ -258,8 +258,7 @@ for _cmd; do
[ -n "${rc_ulimit:-$RC_ULIMIT}" ] && \ [ -n "${rc_ulimit:-$RC_ULIMIT}" ] && \
ulimit ${rc_ulimit:-$RC_ULIMIT} ulimit ${rc_ulimit:-$RC_ULIMIT}
# Apply cgroups settings if defined # Apply cgroups settings if defined
if [ "$(command -v cgroup_add_service)" = \ if [ "$(command -v cgroup_add_service)" = "cgroup_add_service" ]
"cgroup_add_service" ]
then then
if [ -d /sys/fs/cgroup -a ! -w /sys/fs/cgroup ]; then if [ -d /sys/fs/cgroup -a ! -w /sys/fs/cgroup ]; then
eerror "No permission to apply cgroup settings" eerror "No permission to apply cgroup settings"
@@ -268,9 +267,11 @@ for _cmd; do
cgroup_add_service /sys/fs/cgroup/openrc cgroup_add_service /sys/fs/cgroup/openrc
cgroup_add_service /sys/fs/cgroup/systemd/system cgroup_add_service /sys/fs/cgroup/systemd/system
fi fi
[ "$(command -v cgroup_set_limits)" = \ [ "$(command -v cgroup_set_limits)" = "cgroup_set_limits" ] &&
"cgroup_set_limits" ] && \ cgroup_set_limits
cgroup_set_limits [ "$(command -v cgroup2_set_limits)" = "cgroup2_set_limits" ] &&
[ "$_cmd" = start ] &&
cgroup2_set_limits
break break
fi fi
done done
@@ -364,10 +365,14 @@ while [ -n "$1" ]; do
then then
"$1"_post || exit $? "$1"_post || exit $?
fi fi
[ "$(command -v cgroup_cleanup)" = "cgroup_cleanup" -a \ [ "$(command -v cgroup_cleanup)" = "cgroup_cleanup" ] &&
"$1" = "stop" ] && \ [ "$1" = "stop" ] &&
yesno "${rc_cgroup_cleanup}" && \ yesno "${rc_cgroup_cleanup}" && \
cgroup_cleanup cgroup_cleanup
if [ "$(command -v cgroup2_remove)" = "cgroup2_remove" ]; then
[ "$1" = stop ] || [ -z "${command}" ] &&
cgroup2_remove
fi
shift shift
continue 2 continue 2
else else

View File

@@ -14,46 +14,56 @@ description_cgroup_cleanup="Kill all processes in the cgroup"
cgroup_find_path() cgroup_find_path()
{ {
local OIFS n name dir result local OIFS name dir result
[ -n "$1" ] || return 0 [ -n "$1" ] || return 0
OIFS="$IFS" OIFS="$IFS"
IFS=":" IFS=":"
while read n name dir; do while read -r _ name dir; do
[ "$name" = "$1" ] && result="$dir" [ "$name" = "$1" ] && result="$dir"
done < /proc/1/cgroup done < /proc/1/cgroup
IFS="$OIFS" IFS="$OIFS"
echo $result printf "%s" "${result}"
} }
cgroup_get_pids() cgroup_get_pids()
{ {
local p local cgroup_procs p pids
pids= cgroup_procs="$(cgroup2_find_path)"
while read p; do [ -n "${cgroup_procs}" ] &&
[ $p -eq $$ ] || pids="${pids} ${p}" cgroup_procs="${cgroup_procs}/${RC_SVCNAME}/cgroup.procs" ||
done < /sys/fs/cgroup/openrc/${RC_SVCNAME}/tasks cgroup_procs="/sys/fs/cgroup/openrc/${RC_SVCNAME}/tasks"
[ -n "$pids" ] [ -f "${cgroup_procs}" ] || return 0
while read -r p; do
[ "$p" -eq $$ ] || pids="${pids} ${p}"
done < "${cgroup_procs}"
printf "%s" "${pids}"
return 0
} }
cgroup_running() cgroup_running()
{ {
[ -d "/sys/fs/cgroup/openrc/${RC_SVCNAME}" ] [ -d "/sys/fs/cgroup/unified/${RC_SVCNAME}" ] ||
[ -d "/sys/fs/cgroup/${RC_SVCNAME}" ] ||
[ -d "/sys/fs/cgroup/openrc/${RC_SVCNAME}" ]
} }
cgroup_set_values() cgroup_set_values()
{ {
[ -n "$1" -a -n "$2" -a -d "/sys/fs/cgroup/$1" ] || return 0 [ -n "$1" ] && [ -n "$2" ] && [ -d "/sys/fs/cgroup/$1" ] || return 0
local controller="$1" h=$(cgroup_find_path "$1") local controller h
controller="$1"
h=$(cgroup_find_path "$1")
cgroup="/sys/fs/cgroup/${1}${h}openrc_${RC_SVCNAME}" cgroup="/sys/fs/cgroup/${1}${h}openrc_${RC_SVCNAME}"
[ -d "$cgroup" ] || mkdir -p "$cgroup" [ -d "$cgroup" ] || mkdir -p "$cgroup"
set -- $2 set -- $2
local name val local name val
while [ -n "$1" -a "$controller" != "cpuacct" ]; do while [ -n "$1" ] && [ "$controller" != "cpuacct" ]; do
case "$1" in case "$1" in
$controller.*) $controller.*)
if [ -n "$name" -a -w "$cgroup/$name" -a -n "$val" ]; then if [ -n "${name}" ] && [ -w "${cgroup}/${name}" ] &&
[ -n "${val}" ]; then
veinfo "$RC_SVCNAME: Setting $cgroup/$name to $val" veinfo "$RC_SVCNAME: Setting $cgroup/$name to $val"
printf "%s" "$val" > "$cgroup/$name" printf "%s" "$val" > "$cgroup/$name"
fi fi
@@ -68,7 +78,7 @@ cgroup_set_values()
esac esac
shift shift
done done
if [ -n "$name" -a -w "$cgroup/$name" -a -n "$val" ]; then if [ -n "${name}" ] && [ -w "${cgroup}/${name}" ] && [ -n "${val}" ]; then
veinfo "$RC_SVCNAME: Setting $cgroup/$name to $val" veinfo "$RC_SVCNAME: Setting $cgroup/$name to $val"
printf "%s" "$val" > "$cgroup/$name" printf "%s" "$val" > "$cgroup/$name"
fi fi
@@ -134,21 +144,84 @@ cgroup_set_limits()
return 0 return 0
} }
cgroup2_find_path()
{
if grep -qw cgroup2 /proc/filesystems; then
case "${rc_cgroup_mode:-hybrid}" in
hybrid) printf "/sys/fs/cgroup/unified" ;;
unified) printf "/sys/fs/cgroup" ;;
esac
fi
return 0
}
cgroup2_remove()
{
local cgroup_path rc_cgroup_path
cgroup_path="$(cgroup2_find_path)"
[ -z "${cgroup_path}" ] && return 0
rc_cgroup_path="${cgroup_path}/${RC_SVCNAME}"
[ ! -d "${rc_cgroup_path}" ] ||
[ ! -e "${rc_cgroup_path}"/cgroup.events ] &&
return 0
grep -qx "$$" "${rc_cgroup_path}/cgroup.procs" &&
printf "%d" 0 > "${cgroup_path}/cgroup.procs"
local key populated vvalue
while read -r key value; do
case "${key}" in
populated) populated=${value} ;;
*) ;;
esac
done < "${rc_cgroup_path}/cgroup.events"
[ "${populated}" = 1 ] && return 0
rmdir "${rc_cgroup_path}"
return 0
}
cgroup2_set_limits()
{
local cgroup_path
cgroup_path="$(cgroup2_find_path)"
[ -d "${cgroup_path}" ] || return 0
rc_cgroup_path="${cgroup_path}/${RC_SVCNAME}"
local OIFS="$IFS"
IFS="
"
[ ! -d "${rc_cgroup_path}" ] && mkdir "${rc_cgroup_path}"
printf "%d" 0 > "${rc_cgroup_path}/cgroup.procs"
echo "${rc_cgroup_settings}" | while IFS="$OIFS" read -r key value; do
[ -z "${key}" ] || [ -z "${value}" ] && continue
[ ! -e "${rc_cgroup_path}/${key}" ] && continue
veinfo "${RC_SVCNAME}: cgroups: ${key} ${value}"
printf "%s" "${value}" > "${rc_cgroup_path}/${key}"
done
IFS="$OIFS"
return 0
}
cgroup_cleanup() cgroup_cleanup()
{ {
cgroup_running || return 0 cgroup_running || return 0
ebegin "starting cgroups cleanup" ebegin "starting cgroups cleanup"
for sig in TERM QUIT INT; do local pids loops=0
cgroup_get_pids || { eend 0 "finished" ; return 0 ; } pids="$(cgroup_get_pids)"
for i in 0 1; do if [ -n "${pids}" ]; then
kill -s $sig $pids kill -s CONT ${pids} 2> /dev/null
for j in 0 1 2; do kill -s "${stopsig:-TERM}" ${pids} 2> /dev/null
cgroup_get_pids || { eend 0 "finished" ; return 0 ; } yesno "${rc_send_sighup:-no}" &&
sleep 1 kill -s HUP ${pids} 2> /dev/null
done kill -s "${stopsig:-TERM}" ${pids} 2> /dev/null
done 2>/dev/null while [ -n "$(cgroup_get_pids)" ] &&
done [ "${loops}" -lt "${rc_timeout_stopsec:-90}" ]; do
cgroup_get_pids || { eend 0 "finished" ; return 0; } loops=$((loops+1))
kill -9 $pids sleep 1
eend $(cgroup_running && echo 1 || echo 0) "fail to stop all processes" done
pids="$(cgroup_get_pids)"
[ -n "${pids}" ] && yesno "${rc_send_sigkill:-yes}" &&
kill -s KILL ${pids} 2> /dev/null
fi
cgroup2_remove
[ -z "$(cgroup_get_pids)" ]
eend $? "Unable to stop all processes"
return 0
} }

View File

@@ -119,6 +119,13 @@ get_bootparam_value()
echo $result echo $result
} }
need_if_exists()
{
for x; do
rc-service --exists "${x}" && need "${x}"
done
}
# Called from openrc-run.sh or gendepends.sh # Called from openrc-run.sh or gendepends.sh
_get_containers() { _get_containers() {
local c local c

View File

@@ -22,7 +22,8 @@ supervise_start()
# The eval call is necessary for cases like: # The eval call is necessary for cases like:
# command_args="this \"is a\" test" # command_args="this \"is a\" test"
# to work properly. # to work properly.
eval supervise-daemon --start \ eval supervise-daemon "${RC_SVCNAME}" --start \
${retry:+--retry} $retry \
${chroot:+--chroot} $chroot \ ${chroot:+--chroot} $chroot \
${pidfile:+--pidfile} $pidfile \ ${pidfile:+--pidfile} $pidfile \
${respawn_delay:+--respawn-delay} $respawn_delay \ ${respawn_delay:+--respawn-delay} $respawn_delay \
@@ -48,14 +49,48 @@ supervise_stop()
pidfile="${startpidfile:-$pidfile}" pidfile="${startpidfile:-$pidfile}"
[ -n "$pidfile" ] || return 0 [ -n "$pidfile" ] || return 0
ebegin "Stopping ${name:-$RC_SVCNAME}" ebegin "Stopping ${name:-$RC_SVCNAME}"
supervise-daemon --stop \ supervise-daemon "${RC_SVCNAME}" --stop \
${pidfile:+--pidfile} $chroot$pidfile \ ${pidfile:+--pidfile} $chroot$pidfile \
${stopsig:+--signal} $stopsig ${stopsig:+--signal} $stopsig
eend $? "Failed to stop ${name:-$RC_SVCNAME}" eend $? "Failed to stop ${name:-$RC_SVCNAME}"
} }
_check_supervised()
{
local child_pid start_time
child_pid="$(service_get_value "child_pid")"
start_time="$(service_get_value "start_time")"
if [ -n "${child_pid}" ] && [ -n "${start_time}" ]; then
return 1
fi
return 0
}
supervise_status() supervise_status()
{ {
_status if service_stopping; then
ewarn "status: stopping"
return 4
elif service_starting; then
ewarn "status: starting"
return 8
elif service_inactive; then
ewarn "status: inactive"
return 16
elif service_started; then
if service_crashed; then
if ! _check_supervised; then
eerror "status: unsupervised"
return 64
fi
eerror "status: crashed"
return 32
fi
einfo "status: started"
return 0
else
einfo "status: stopped"
return 3
fi
} }

View File

@@ -47,6 +47,7 @@ bool rc_conf_yesno(const char *var);
void env_filter(void); void env_filter(void);
void env_config(void); void env_config(void);
int signal_setup(int sig, void (*handler)(int)); int signal_setup(int sig, void (*handler)(int));
int signal_setup_restart(int sig, void (*handler)(int));
int svc_lock(const char *); int svc_lock(const char *);
int svc_unlock(const char *, int); int svc_unlock(const char *, int);
pid_t exec_service(const char *, const char *); pid_t exec_service(const char *, const char *);
@@ -71,5 +72,6 @@ bool _rc_can_find_pids(void);
RC_SERVICE lookup_service_state(const char *service); RC_SERVICE lookup_service_state(const char *service);
void from_time_t(char *time_string, time_t tv); void from_time_t(char *time_string, time_t tv);
time_t to_time_t(char *timestring); time_t to_time_t(char *timestring);
pid_t get_pid(const char *applet, const char *pidfile);
#endif #endif

View File

@@ -80,9 +80,12 @@ rc_find_pids(const char *exec, const char *const *argv, uid_t uid, pid_t pid)
DIR *procdir; DIR *procdir;
struct dirent *entry; struct dirent *entry;
FILE *fp; FILE *fp;
int rc;
bool container_pid = false; bool container_pid = false;
bool openvz_host = false; bool openvz_host = false;
char *line = NULL; char *line = NULL;
char my_ns[30];
char proc_ns[30];
size_t len = 0; size_t len = 0;
pid_t p; pid_t p;
char buffer[PATH_MAX]; char buffer[PATH_MAX];
@@ -131,6 +134,14 @@ rc_find_pids(const char *exec, const char *const *argv, uid_t uid, pid_t pid)
} }
} }
memset(my_ns, 0, sizeof(my_ns));
memset(proc_ns, 0, sizeof(proc_ns));
if (exists("/proc/self/ns/pid")) {
rc = readlink("/proc/self/ns/pid", my_ns, sizeof(my_ns));
if (rc <= 0)
my_ns[0] = '\0';
}
while ((entry = readdir(procdir)) != NULL) { while ((entry = readdir(procdir)) != NULL) {
if (sscanf(entry->d_name, "%d", &p) != 1) if (sscanf(entry->d_name, "%d", &p) != 1)
continue; continue;
@@ -138,6 +149,14 @@ rc_find_pids(const char *exec, const char *const *argv, uid_t uid, pid_t pid)
continue; continue;
if (pid != 0 && pid != p) if (pid != 0 && pid != p)
continue; continue;
snprintf(buffer, sizeof(buffer), "/proc/%d/ns/pid", p);
if (exists(buffer)) {
rc = readlink(buffer, proc_ns, sizeof(proc_ns));
if (rc <= 0)
proc_ns[0] = '\0';
}
if (strcmp(my_ns, proc_ns))
continue;
if (uid) { if (uid) {
snprintf(buffer, sizeof(buffer), "/proc/%d", p); snprintf(buffer, sizeof(buffer), "/proc/%d", p);
if (stat(buffer, &sb) != 0 || sb.st_uid != uid) if (stat(buffer, &sb) != 0 || sb.st_uid != uid)

View File

@@ -894,12 +894,15 @@ rc_service_value_set(const char *service, const char *option,
return false; return false;
snprintf(p, sizeof(file) - (p - file), "/%s", option); snprintf(p, sizeof(file) - (p - file), "/%s", option);
if (!(fp = fopen(file, "w"))) if (value) {
return false; if (!(fp = fopen(file, "w")))
if (value) return false;
fprintf(fp, "%s", value); fprintf(fp, "%s", value);
fclose(fp); fclose(fp);
return true; } else {
unlink(file);
}
return true;
} }
librc_hidden_def(rc_service_value_set) librc_hidden_def(rc_service_value_set)

View File

@@ -24,7 +24,7 @@ SBINDIR= ${PREFIX}/sbin
LINKDIR= ${LIBEXECDIR} LINKDIR= ${LIBEXECDIR}
BINPROGS= rc-status BINPROGS= rc-status
SBINPROGS = openrc openrc-run rc rc-service rc-update runscript service \ SBINPROGS = openrc openrc-run rc rc-service rc-update runscript \
start-stop-daemon supervise-daemon start-stop-daemon supervise-daemon
RC_BINPROGS= einfon einfo ewarnn ewarn eerrorn eerror ebegin eend ewend \ RC_BINPROGS= einfon einfo ewarnn ewarn eerrorn eerror ebegin eend ewend \
eindent eoutdent esyslog eval_ecolors ewaitfile \ eindent eoutdent esyslog eval_ecolors ewaitfile \
@@ -150,16 +150,16 @@ rc-depend: rc-depend.o _usage.o rc-misc.o
rc-status: rc-status.o _usage.o rc-misc.o rc-status: rc-status.o _usage.o rc-misc.o
${CC} ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ $^ ${LDADD} ${CC} ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ $^ ${LDADD}
rc-service service: rc-service.o _usage.o rc-misc.o rc-service: rc-service.o _usage.o rc-misc.o
${CC} ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ $^ ${LDADD} ${CC} ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ $^ ${LDADD}
rc-update: rc-update.o _usage.o rc-misc.o rc-update: rc-update.o _usage.o rc-misc.o
${CC} ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ $^ ${LDADD} ${CC} ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ $^ ${LDADD}
start-stop-daemon: start-stop-daemon.o _usage.o rc-misc.o start-stop-daemon: start-stop-daemon.o _usage.o rc-misc.o rc-schedules.o
${CC} ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ $^ ${LDADD} ${CC} ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ $^ ${LDADD}
supervise-daemon: supervise-daemon.o _usage.o rc-misc.o supervise-daemon: supervise-daemon.o _usage.o rc-misc.o rc-schedules.o
${CC} ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ $^ ${LDADD} ${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 service_get_value service_set_value get_options save_options: do_value.o rc-misc.o

View File

@@ -217,6 +217,18 @@ signal_setup(int sig, void (*handler)(int))
return sigaction(sig, &sa, NULL); 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 int
svc_lock(const char *applet) svc_lock(const char *applet)
{ {
@@ -474,3 +486,27 @@ time_t to_time_t(char *timestring)
} }
return result; 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;
}

419
src/rc/rc-schedules.c Normal file
View File

@@ -0,0 +1,419 @@
/*
* 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/master/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/master/LICENSE
* This file may not be copied, modified, propagated, or distributed
* except according to the terms contained in the LICENSE file.
*/
/* nano seconds */
#define POLL_INTERVAL 20000000
#define WAIT_PIDFILE 500000000
#define ONE_SECOND 1000000000
#define ONE_MS 1000000
#include <ctype.h>
#include <errno.h>
#include <signal.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "einfo.h"
#include "queue.h"
#include "rc.h"
#include "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 (!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));
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 (exec)
einfov("Will stop %s", exec);
if (pid > 0)
einfov("Will stop PID %d", pid);
if (uid)
einfov("Will stop processes owned by UID %d", uid);
if (argv && *argv) {
einfovn("Will stop processes of `");
if (rc_yesno(getenv("EINFO_VERBOSE"))) {
for (p = argv; p && *p; p++) {
if (p != argv)
printf(" ");
printf("%s", *p);
}
printf("'\n");
}
}
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_TIMEOUT:
if (item->value < 1) {
item = NULL;
break;
}
ts.tv_sec = 0;
ts.tv_nsec = POLL_INTERVAL;
for (nsecs = 0; nsecs < item->value; nsecs++) {
for (nloops = 0;
nloops < ONE_SECOND / POLL_INTERVAL;
nloops++)
{
if ((nrunning = do_stop(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: caught an"
" interrupt", applet);
else {
eerror("%s: nanosleep: %s",
applet, strerror(errno));
return 0;
}
}
}
if (progress) {
printf(".");
fflush(stdout);
progressed = true;
}
}
break;
default:
if (progressed) {
printf("\n");
progressed = false;
}
eerror("%s: invalid schedule item `%d'",
applet, item->type);
return 0;
}
if (item)
item = TAILQ_NEXT(item, entries);
}
if (test || (tkilled > 0 && nrunning == 0))
return nkilled;
if (progressed)
printf("\n");
if (! 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;
}

26
src/rc/rc-schedules.h Normal file
View File

@@ -0,0 +1,26 @@
/*
* Copyright (c) 2017 The OpenRC Authors.
* See the Authors file at the top-level directory of this distribution and
* https://github.com/OpenRC/openrc/blob/master/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/master/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

View File

@@ -39,7 +39,6 @@
#include "rc-selinux.h" #include "rc-selinux.h"
/* the context files for selinux */ /* the context files for selinux */
#define RUN_INIT_FILE "run_init_type"
#define INITRC_FILE "initrc_context" #define INITRC_FILE "initrc_context"
#ifdef HAVE_AUDIT #ifdef HAVE_AUDIT
@@ -299,6 +298,26 @@ static int read_context_file(const char *filename, char **context)
return ret; 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) void selinux_setup(char **argv)
{ {
char *new_context = NULL; char *new_context = NULL;
@@ -312,7 +331,7 @@ void selinux_setup(char **argv)
return; return;
} }
if (read_context_file(RUN_INIT_FILE, &run_init_t) != 0) { if (read_run_init_context(&run_init_t) != 0) {
/* assume a reasonable default, rather than bailing out */ /* assume a reasonable default, rather than bailing out */
run_init_t = xstrdup("run_init_t"); run_init_t = xstrdup("run_init_t");
ewarn("Assuming SELinux run_init type is %s", run_init_t); ewarn("Assuming SELinux run_init type is %s", run_init_t);
@@ -339,14 +358,13 @@ void selinux_setup(char **argv)
goto out; goto out;
} }
curr_t = context_type_get(curr_con); curr_t = xstrdup(context_type_get(curr_con));
if (!curr_t) { if (!curr_t) {
context_free(curr_con); context_free(curr_con);
free(curr_context); free(curr_context);
goto out; goto out;
} }
curr_t = xstrdup(curr_t);
/* dont need them anymore so free() now */ /* dont need them anymore so free() now */
context_free(curr_con); context_free(curr_con);
free(curr_context); free(curr_context);

View File

@@ -128,6 +128,8 @@ print_service(const char *service)
{ {
char status[60]; char status[60];
char uptime [40]; char uptime [40];
char *child_pid = NULL;
char *start_time = NULL;
int cols = printf(" %s", service); int cols = printf(" %s", service);
const char *c = ecolor(ECOLOR_GOOD); const char *c = ecolor(ECOLOR_GOOD);
RC_SERVICE state = rc_service_state(service); RC_SERVICE state = rc_service_state(service);
@@ -147,7 +149,14 @@ print_service(const char *service)
rc_service_daemons_crashed(service) && rc_service_daemons_crashed(service) &&
errno != EACCES) errno != EACCES)
{ {
snprintf(status, sizeof(status), " crashed "); child_pid = rc_service_value_get(service, "child_pid");
start_time = rc_service_value_get(service, "start_time");
if (start_time && child_pid)
snprintf(status, sizeof(status), " unsupervised ");
else
snprintf(status, sizeof(status), " crashed ");
free(child_pid);
free(start_time);
} else { } else {
get_uptime(service, uptime, 40); get_uptime(service, uptime, 40);
snprintf(status, sizeof(status), " started %s", uptime); snprintf(status, sizeof(status), " started %s", uptime);

View File

@@ -19,10 +19,6 @@
* except according to the terms contained in the LICENSE file. * 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 #define ONE_MS 1000000
#include <sys/types.h> #include <sys/types.h>
@@ -63,6 +59,7 @@ static struct pam_conv conv = { NULL, NULL};
#include "queue.h" #include "queue.h"
#include "rc.h" #include "rc.h"
#include "rc-misc.h" #include "rc-misc.h"
#include "rc-schedules.h"
#include "_usage.h" #include "_usage.h"
#include "helpers.h" #include "helpers.h"
@@ -130,20 +127,6 @@ const char * const longopts_help[] = {
}; };
const char *usagestring = NULL; const char *usagestring = NULL;
typedef struct scheduleitem
{
enum
{
SC_TIMEOUT,
SC_SIGNAL,
SC_GOTO,
SC_FOREVER
} type;
int value;
struct scheduleitem *gotoitem;
TAILQ_ENTRY(scheduleitem) entries;
} SCHEDULEITEM;
TAILQ_HEAD(, scheduleitem) schedule;
static char **nav; static char **nav;
static char *changeuser, *ch_root, *ch_dir; static char *changeuser, *ch_root, *ch_dir;
@@ -166,20 +149,6 @@ static inline int ioprio_set(int which _unused,
} }
#endif #endif
static void
free_schedulelist(void)
{
SCHEDULEITEM *s1 = TAILQ_FIRST(&schedule);
SCHEDULEITEM *s2;
while (s1) {
s2 = TAILQ_NEXT(s1, entries);
free(s1);
s1 = s2;
}
TAILQ_INIT(&schedule);
}
static void static void
cleanup(void) cleanup(void)
{ {
@@ -188,385 +157,6 @@ cleanup(void)
free_schedulelist(); free_schedulelist();
} }
static int
parse_signal(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 *string)
{
const char *after_hyph;
int sig;
SCHEDULEITEM *item = xmalloc(sizeof(*item));
item->value = 0;
item->gotoitem = NULL;
if (strcmp(string,"forever") == 0)
item->type = SC_FOREVER;
else if (isdigit((unsigned char)string[0])) {
item->type = SC_TIMEOUT;
errno = 0;
if (sscanf(string, "%d", &item->value) != 1)
eerrorx("%s: invalid timeout value in schedule `%s'",
applet, string);
} else if ((after_hyph = string + (string[0] == '-')) &&
((sig = parse_signal(after_hyph)) != -1))
{
item->type = SC_SIGNAL;
item->value = (int)sig;
} else
eerrorx("%s: invalid schedule item `%s'", applet, string);
return item;
}
static void
parse_schedule(const char *string, int timeout)
{
char buffer[20];
const char *slash;
int count = 0;
SCHEDULEITEM *repeatat = NULL;
size_t len;
SCHEDULEITEM *item;
if (string)
for (slash = string; *slash; slash++)
if (*slash == '/')
count++;
free_schedulelist();
if (count == 0) {
item = xmalloc(sizeof(*item));
item->type = SC_SIGNAL;
item->value = timeout;
item->gotoitem = NULL;
TAILQ_INSERT_TAIL(&schedule, item, entries);
item = xmalloc(sizeof(*item));
item->type = SC_TIMEOUT;
item->gotoitem = NULL;
TAILQ_INSERT_TAIL(&schedule, item, entries);
if (string) {
if (sscanf(string, "%d", &item->value) != 1)
eerrorx("%s: invalid timeout in schedule",
applet);
} else
item->value = 5;
return;
}
while (string != NULL) {
if ((slash = strchr(string, '/')))
len = slash - string;
else
len = strlen(string);
if (len >= (ptrdiff_t)sizeof(buffer))
eerrorx("%s: invalid schedule item, far too long",
applet);
memcpy(buffer, string, len);
buffer[len] = 0;
string = slash ? slash + 1 : NULL;
item = parse_schedule_item(buffer);
TAILQ_INSERT_TAIL(&schedule, item, entries);
if (item->type == SC_FOREVER) {
if (repeatat)
eerrorx("%s: invalid schedule, `forever' "
"appears more than once", applet);
repeatat = item;
continue;
}
}
if (repeatat) {
item = xmalloc(sizeof(*item));
item->type = SC_GOTO;
item->value = 0;
item->gotoitem = repeatat;
TAILQ_INSERT_TAIL(&schedule, item, entries);
}
return;
}
static pid_t
get_pid(const char *pidfile)
{
FILE *fp;
pid_t pid;
if (! pidfile)
return -1;
if ((fp = fopen(pidfile, "r")) == NULL) {
ewarnv("%s: fopen `%s': %s", applet, pidfile, strerror(errno));
return -1;
}
if (fscanf(fp, "%d", &pid) != 1) {
ewarnv("%s: no pid found in `%s'", applet, pidfile);
fclose(fp);
return -1;
}
fclose(fp);
return pid;
}
/* return number of processed killed, -1 on error */
static int
do_stop(const char *exec, const char *const *argv,
pid_t pid, uid_t uid,int sig, bool test)
{
RC_PIDLIST *pids;
RC_PID *pi;
RC_PID *np;
bool killed;
int nkilled = 0;
if (pid)
pids = rc_find_pids(NULL, NULL, 0, pid);
else
pids = rc_find_pids(exec, argv, uid, pid);
if (!pids)
return 0;
LIST_FOREACH_SAFE(pi, pids, entries, np) {
if (test) {
einfo("Would send signal %d to PID %d", sig, pi->pid);
nkilled++;
} else {
ebeginv("Sending signal %d to PID %d", sig, pi->pid);
errno = 0;
killed = (kill(pi->pid, sig) == 0 ||
errno == ESRCH ? true : false);
eendv(killed ? 0 : 1,
"%s: failed to send signal %d to PID %d: %s",
applet, sig, pi->pid, strerror(errno));
if (!killed) {
nkilled = -1;
} else {
if (nkilled != -1)
nkilled++;
}
}
free(pi);
}
free(pids);
return nkilled;
}
static int
run_stop_schedule(const char *exec, const char *const *argv,
const char *pidfile, uid_t uid,
bool test, bool progress)
{
SCHEDULEITEM *item = TAILQ_FIRST(&schedule);
int nkilled = 0;
int tkilled = 0;
int nrunning = 0;
long nloops, nsecs;
struct timespec ts;
pid_t pid = 0;
const char *const *p;
bool progressed = false;
if (exec)
einfov("Will stop %s", exec);
if (pidfile)
einfov("Will stop PID in pidfile `%s'", pidfile);
if (uid)
einfov("Will stop processes owned by UID %d", uid);
if (argv && *argv) {
einfovn("Will stop processes of `");
if (rc_yesno(getenv("EINFO_VERBOSE"))) {
for (p = argv; p && *p; p++) {
if (p != argv)
printf(" ");
printf("%s", *p);
}
printf("'\n");
}
}
if (pidfile) {
pid = get_pid(pidfile);
if (pid == -1)
return 0;
}
while (item) {
switch (item->type) {
case SC_GOTO:
item = item->gotoitem;
continue;
case SC_SIGNAL:
nrunning = 0;
nkilled = do_stop(exec, argv, pid, uid, item->value, test);
if (nkilled == 0) {
if (tkilled == 0) {
if (progressed)
printf("\n");
eerror("%s: no matching processes found", applet);
}
return tkilled;
}
else if (nkilled == -1)
return 0;
tkilled += nkilled;
break;
case SC_TIMEOUT:
if (item->value < 1) {
item = NULL;
break;
}
ts.tv_sec = 0;
ts.tv_nsec = POLL_INTERVAL;
for (nsecs = 0; nsecs < item->value; nsecs++) {
for (nloops = 0;
nloops < ONE_SECOND / POLL_INTERVAL;
nloops++)
{
if ((nrunning = do_stop(exec, argv,
pid, uid, 0, test)) == 0)
return 0;
if (nanosleep(&ts, NULL) == -1) {
if (progressed) {
printf("\n");
progressed = false;
}
if (errno == EINTR)
eerror("%s: caught an"
" interrupt", applet);
else {
eerror("%s: nanosleep: %s",
applet, strerror(errno));
return 0;
}
}
}
if (progress) {
printf(".");
fflush(stdout);
progressed = true;
}
}
break;
default:
if (progressed) {
printf("\n");
progressed = false;
}
eerror("%s: invalid schedule item `%d'",
applet, item->type);
return 0;
}
if (item)
item = TAILQ_NEXT(item, entries);
}
if (test || (tkilled > 0 && nrunning == 0))
return nkilled;
if (progressed)
printf("\n");
if (nrunning == 1)
eerror("%s: %d process refused to stop", applet, nrunning);
else
eerror("%s: %d process(es) refused to stop", applet, nrunning);
return -nrunning;
}
static void static void
handle_signal(int sig) handle_signal(int sig)
{ {
@@ -689,6 +279,7 @@ int main(int argc, char **argv)
int stdout_fd; int stdout_fd;
int stderr_fd; int stderr_fd;
pid_t pid, spid; pid_t pid, spid;
RC_PIDLIST *pids;
int i; int i;
char *svcname = getenv("RC_SVCNAME"); char *svcname = getenv("RC_SVCNAME");
RC_STRINGLIST *env_list; RC_STRINGLIST *env_list;
@@ -707,7 +298,6 @@ int main(int argc, char **argv)
unsigned int start_wait = 0; unsigned int start_wait = 0;
applet = basename_c(argv[0]); applet = basename_c(argv[0]);
TAILQ_INIT(&schedule);
atexit(cleanup); atexit(cleanup);
signal_setup(SIGINT, handle_signal); signal_setup(SIGINT, handle_signal);
@@ -876,7 +466,7 @@ int main(int argc, char **argv)
break; break;
case 's': /* --signal <signal> */ case 's': /* --signal <signal> */
sig = parse_signal(optarg); sig = parse_signal(applet, optarg);
break; break;
case 't': /* --test */ case 't': /* --test */
@@ -1062,13 +652,13 @@ int main(int argc, char **argv)
if (!stop) if (!stop)
oknodo = true; oknodo = true;
if (retry) if (retry)
parse_schedule(retry, sig); parse_schedule(applet, retry, sig);
else if (test || oknodo) else if (test || oknodo)
parse_schedule("0", sig); parse_schedule(applet, "0", sig);
else else
parse_schedule(NULL, sig); parse_schedule(applet, NULL, sig);
i = run_stop_schedule(exec, (const char *const *)margv, i = run_stop_schedule(applet, exec, (const char *const *)margv,
pidfile, uid, test, progress); get_pid(applet, pidfile), uid, test, progress, false);
if (i < 0) if (i < 0)
/* We failed to stop something */ /* We failed to stop something */
@@ -1090,14 +680,18 @@ int main(int argc, char **argv)
} }
if (pidfile) if (pidfile)
pid = get_pid(pidfile); pid = get_pid(applet, pidfile);
else else
pid = 0; pid = 0;
if (do_stop(exec, (const char * const *)margv, pid, uid, if (pid)
0, test) > 0) 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); eerrorx("%s: %s is already running", applet, exec);
free(pids);
if (test) { if (test) {
if (rc_yesno(getenv("EINFO_QUIET"))) if (rc_yesno(getenv("EINFO_QUIET")))
exit (EXIT_SUCCESS); exit (EXIT_SUCCESS);
@@ -1365,7 +959,7 @@ int main(int argc, char **argv)
alive = true; alive = true;
} else { } else {
if (pidfile) { if (pidfile) {
pid = get_pid(pidfile); pid = get_pid(applet, pidfile);
if (pid == -1) { if (pid == -1) {
eerrorx("%s: did not " eerrorx("%s: did not "
"create a valid" "create a valid"
@@ -1374,8 +968,8 @@ int main(int argc, char **argv)
} }
} else } else
pid = 0; pid = 0;
if (do_stop(exec, (const char *const *)margv, if (do_stop(applet, exec, (const char *const *)margv,
pid, uid, 0, test) > 0) pid, uid, 0, test, false) > 0)
alive = true; alive = true;
} }

View File

@@ -61,12 +61,13 @@ static struct pam_conv conv = { NULL, NULL};
#include "queue.h" #include "queue.h"
#include "rc.h" #include "rc.h"
#include "rc-misc.h" #include "rc-misc.h"
#include "rc-schedules.h"
#include "_usage.h" #include "_usage.h"
#include "helpers.h" #include "helpers.h"
const char *applet = NULL; const char *applet = NULL;
const char *extraopts = NULL; const char *extraopts = NULL;
const char *getoptstring = "D:d:e:g:I:Kk:m:N:p:r:Su:1:2:" \ const char *getoptstring = "D:d:e:g:I:Kk:m:N:p:R:r:Su:1:2:3" \
getoptstring_COMMON; getoptstring_COMMON;
const struct option longopts[] = { const struct option longopts[] = {
{ "respawn-delay", 1, NULL, 'D'}, { "respawn-delay", 1, NULL, 'D'},
@@ -80,11 +81,13 @@ const struct option longopts[] = {
{ "nicelevel", 1, NULL, 'N'}, { "nicelevel", 1, NULL, 'N'},
{ "pidfile", 1, NULL, 'p'}, { "pidfile", 1, NULL, 'p'},
{ "respawn-period", 1, NULL, 'P'}, { "respawn-period", 1, NULL, 'P'},
{ "retry", 1, NULL, 'R'},
{ "chroot", 1, NULL, 'r'}, { "chroot", 1, NULL, 'r'},
{ "start", 0, NULL, 'S'}, { "start", 0, NULL, 'S'},
{ "user", 1, NULL, 'u'}, { "user", 1, NULL, 'u'},
{ "stdout", 1, NULL, '1'}, { "stdout", 1, NULL, '1'},
{ "stderr", 1, NULL, '2'}, { "stderr", 1, NULL, '2'},
{ "reexec", 0, NULL, '3'},
longopts_COMMON longopts_COMMON
}; };
const char * const longopts_help[] = { const char * const longopts_help[] = {
@@ -99,11 +102,13 @@ const char * const longopts_help[] = {
"Set a nicelevel when starting", "Set a nicelevel when starting",
"Match pid found in this file", "Match pid found in this file",
"Set respawn time period", "Set respawn time period",
"Retry schedule to use when stopping",
"Chroot to this directory", "Chroot to this directory",
"Start daemon", "Start daemon",
"Change the process user", "Change the process user",
"Redirect stdout to file", "Redirect stdout to file",
"Redirect stderr to file", "Redirect stderr to file",
"reexec (used internally)",
longopts_help_COMMON longopts_help_COMMON
}; };
const char *usagestring = NULL; const char *usagestring = NULL;
@@ -124,6 +129,13 @@ static bool exiting = false;
#ifdef TIOCNOTTY #ifdef TIOCNOTTY
static int tty_fd = -1; static int tty_fd = -1;
#endif #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 = 5;
static char *pidfile = NULL;
static char *svcname = NULL;
extern char **environ; extern char **environ;
@@ -147,32 +159,72 @@ static void cleanup(void)
free(changeuser); free(changeuser);
} }
static pid_t get_pid(const char *pidfile) static void re_exec(void)
{ {
FILE *fp; syslog(LOG_WARNING, "Re-executing for %s", svcname);
pid_t pid; execlp("supervise-daemon", "supervise-daemon", svcname, "--reexec",
(char *) NULL);
if (! pidfile) syslog(LOG_ERR, "Unable to execute supervise-daemon: %s",
return -1; strerror(errno));
exit(EXIT_FAILURE);
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;
} }
static void child_process(char *exec, char **argv, char *svcname, static void handle_signal(int sig)
int start_count) {
int serrno = errno;
syslog(LOG_WARNING, "caught signal %d", sig);
if (sig == SIGTERM)
exiting = true;
/* Restore errno */
errno = serrno;
if (! exiting)
re_exec();
}
static char * expand_home(const char *home, const char *path)
{
char *opath, *ppath, *p, *nh;
size_t len;
struct passwd *pw;
if (!path || *path != '~')
return xstrdup(path);
opath = ppath = xstrdup(path);
if (ppath[1] != '/' && ppath[1] != '\0') {
p = strchr(ppath + 1, '/');
if (p)
*p = '\0';
pw = getpwnam(ppath + 1);
if (pw) {
home = pw->pw_dir;
ppath = p;
if (ppath)
*ppath = '/';
} else
home = NULL;
} else
ppath++;
if (!home) {
free(opath);
return xstrdup(path);
}
if (!ppath) {
free(opath);
return xstrdup(home);
}
len = strlen(ppath) + strlen(home) + 1;
nh = xmalloc(len);
snprintf(nh, len, "%s%s", home, ppath);
free(opath);
return nh;
}
static void child_process(char *exec, char **argv)
{ {
RC_STRINGLIST *env_list; RC_STRINGLIST *env_list;
RC_STRING *env; RC_STRING *env;
@@ -197,11 +249,13 @@ static void child_process(char *exec, char **argv, char *svcname,
setsid(); setsid();
if (svcname) { if (svcname) {
start_time = time(NULL); start_time = time(NULL);
from_time_t(start_time_string, start_time); from_time_t(start_time_string, start_time);
rc_service_value_set(svcname, "start_time", start_time_string); rc_service_value_set(svcname, "start_time", start_time_string);
sprintf(start_count_string, "%i", start_count); sprintf(start_count_string, "%i", respawn_count);
rc_service_value_set(svcname, "start_count", start_count_string); 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) { if (nicelevel) {
@@ -344,12 +398,12 @@ sprintf(start_count_string, "%i", start_count);
*cmdline = '\0'; *cmdline = '\0';
c = argv; c = argv;
while (*c) { while (c && *c) {
strcat(cmdline, *c); strcat(cmdline, *c);
strcat(cmdline, " "); strcat(cmdline, " ");
c++; c++;
} }
syslog(LOG_INFO, "Running command line: %s", cmdline); syslog(LOG_INFO, "Child command line: %s", cmdline);
execvp(exec, argv); execvp(exec, argv);
#ifdef HAVE_PAM #ifdef HAVE_PAM
@@ -359,110 +413,162 @@ sprintf(start_count_string, "%i", start_count);
eerrorx("%s: failed to exec `%s': %s", applet, exec,strerror(errno)); eerrorx("%s: failed to exec `%s': %s", applet, exec,strerror(errno));
} }
static void handle_signal(int sig) static void supervisor(char *exec, char **argv)
{ {
int serrno = errno; FILE *fp;
char signame[10] = { '\0' }; int i;
int nkilled;
time_t respawn_now= 0;
time_t first_spawn= 0;
switch (sig) { #ifndef RC_DEBUG
case SIGINT: signal_setup_restart(SIGHUP, handle_signal);
snprintf(signame, sizeof(signame), "SIGINT"); signal_setup_restart(SIGINT, handle_signal);
break; signal_setup_restart(SIGQUIT, handle_signal);
case SIGTERM: signal_setup_restart(SIGILL, handle_signal);
snprintf(signame, sizeof(signame), "SIGTERM"); signal_setup_restart(SIGABRT, handle_signal);
break; signal_setup_restart(SIGFPE, handle_signal);
case SIGQUIT: signal_setup_restart(SIGSEGV, handle_signal);
snprintf(signame, sizeof(signame), "SIGQUIT"); signal_setup_restart(SIGPIPE, handle_signal);
break; signal_setup_restart(SIGALRM, handle_signal);
signal_setup(SIGTERM, handle_signal);
signal_setup_restart(SIGUSR1, handle_signal);
signal_setup_restart(SIGUSR2, handle_signal);
signal_setup_restart(SIGBUS, handle_signal);
signal_setup_restart(SIGPOLL, handle_signal);
signal_setup_restart(SIGPROF, handle_signal);
signal_setup_restart(SIGSYS, handle_signal);
signal_setup_restart(SIGTRAP, handle_signal);
signal_setup_restart(SIGVTALRM, handle_signal);
signal_setup_restart(SIGXCPU, handle_signal);
signal_setup_restart(SIGXFSZ, handle_signal);
#ifdef SIGEMT
signal_setup_restart(SIGEMT, handle_signal);
#endif
signal_setup_restart(SIGIO, handle_signal);
signal_setup_restart(SIGPWR, handle_signal);
#ifdef SIGUNUSED
signal_setup_restart(SIGUNUSED, handle_signal);
#endif
#ifdef SIGRTMIN
for (i = SIGRTMIN; i <= SIGRTMAX; i++)
signal_setup_restart(i, handle_signal);
#endif
#endif
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
*/
i = 0;
while (!exiting) {
wait(&i);
if (exiting) {
signal_setup(SIGCHLD, SIG_IGN);
syslog(LOG_INFO, "stopping %s, pid %d", exec, child_pid);
nkilled = run_stop_schedule(applet, exec, NULL, child_pid, 0,
false, false, true);
if (nkilled > 0)
syslog(LOG_INFO, "killed %d processes", nkilled);
} else {
sleep(respawn_delay);
if (respawn_max > 0 && respawn_period > 0) {
respawn_now = time(NULL);
if (first_spawn == 0)
first_spawn = respawn_now;
if (respawn_now - first_spawn > respawn_period) {
respawn_count = 0;
first_spawn = 0;
} else
respawn_count++;
if (respawn_count >= respawn_max) {
syslog(LOG_WARNING,
"respawned \"%s\" too many times, exiting", exec);
exiting = true;
continue;
}
}
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));
child_pid = fork();
if (child_pid == -1)
eerrorx("%s: fork: %s", applet, strerror(errno));
if (child_pid == 0)
child_process(exec, argv);
}
} }
if (*signame != 0) { if (pidfile && exists(pidfile))
syslog(LOG_INFO, "%s: caught signal %s, exiting", applet, signame); unlink(pidfile);
exiting = true; if (svcname) {
} else rc_service_daemon_set(svcname, exec, (const char *const *)argv,
syslog(LOG_INFO, "%s: caught unknown signal %d", applet, sig); pidfile, false);
rc_service_mark(svcname, RC_SERVICE_STOPPED);
/* Restore errno */ rc_service_value_set(svcname, "child_pid", NULL);
errno = serrno;
}
static char * expand_home(const char *home, const char *path)
{
char *opath, *ppath, *p, *nh;
size_t len;
struct passwd *pw;
if (!path || *path != '~')
return xstrdup(path);
opath = ppath = xstrdup(path);
if (ppath[1] != '/' && ppath[1] != '\0') {
p = strchr(ppath + 1, '/');
if (p)
*p = '\0';
pw = getpwnam(ppath + 1);
if (pw) {
home = pw->pw_dir;
ppath = p;
if (ppath)
*ppath = '/';
} else
home = NULL;
} else
ppath++;
if (!home) {
free(opath);
return xstrdup(path);
} }
if (!ppath) { exit(EXIT_SUCCESS);
free(opath);
return xstrdup(home);
}
len = strlen(ppath) + strlen(home) + 1;
nh = xmalloc(len);
snprintf(nh, len, "%s%s", home, ppath);
free(opath);
return nh;
} }
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
int opt; int opt;
char **c;
int x;
bool start = false; bool start = false;
bool stop = false; bool stop = false;
bool reexec = false;
char *exec = NULL; char *exec = NULL;
char *pidfile = NULL; char *retry = NULL;
int sig = SIGTERM;
char *home = NULL; char *home = NULL;
int tid = 0; int tid = 0;
pid_t child_pid, pid; pid_t pid;
char *svcname = getenv("RC_SVCNAME");
char *tmp; char *tmp;
char *p; char *p;
char *token; char *token;
int i; int i;
int n; int n;
char exec_file[PATH_MAX]; char exec_file[PATH_MAX];
int respawn_count = 0; char name[PATH_MAX];
int respawn_delay = 0; struct timespec ts;
int respawn_max = 10;
int respawn_period = 5;
time_t respawn_now= 0;
time_t first_spawn= 0;
struct passwd *pw; struct passwd *pw;
struct group *gr; struct group *gr;
FILE *fp; FILE *fp;
mode_t numask = 022; mode_t numask = 022;
int child_argc = 0;
char **child_argv = NULL;
char *str = NULL;
char cmdline[PATH_MAX];
applet = basename_c(argv[0]); applet = basename_c(argv[0]);
atexit(cleanup); atexit(cleanup);
svcname = getenv("RC_SVCNAME");
signal_setup(SIGINT, handle_signal); if (!svcname)
signal_setup(SIGQUIT, handle_signal); eerrorx("%s: The RC_SVCNAME environment variable is not set", applet);
signal_setup(SIGTERM, handle_signal);
openlog(applet, LOG_PID, LOG_DAEMON); openlog(applet, LOG_PID, LOG_DAEMON);
if (argc >= 1 && svcname && strcmp(argv[1], svcname))
eerrorx("%s: the first argument must be %s", applet, svcname);
if ((tmp = getenv("SSD_NICELEVEL"))) if ((tmp = getenv("SSD_NICELEVEL")))
if (sscanf(tmp, "%d", &nicelevel) != 1) if (sscanf(tmp, "%d", &nicelevel) != 1)
eerror("%s: invalid nice level `%s' (SSD_NICELEVEL)", eerror("%s: invalid nice level `%s' (SSD_NICELEVEL)",
@@ -483,6 +589,17 @@ int main(int argc, char **argv)
} }
} }
*cmdline = '\0';
c = argv;
while (c && *c) {
strcat(cmdline, *c);
strcat(cmdline, " ");
c++;
}
if (svcname) {
argc--;
argv++;
}
while ((opt = getopt_long(argc, argv, getoptstring, longopts, while ((opt = getopt_long(argc, argv, getoptstring, longopts,
(int *) 0)) != -1) (int *) 0)) != -1)
switch (opt) { switch (opt) {
@@ -515,8 +632,8 @@ int main(int argc, char **argv)
case 'P': /* --respawn-period time */ case 'P': /* --respawn-period time */
n = sscanf(optarg, "%d", &respawn_period); n = sscanf(optarg, "%d", &respawn_period);
if (n != 1 || respawn_delay < 1) if (n != 1 || respawn_period < 1)
eerrorx("Invalid respawn-delay value '%s'", optarg); eerrorx("Invalid respawn-period value '%s'", optarg);
break; break;
case 'S': /* --start */ case 'S': /* --start */
@@ -558,6 +675,9 @@ int main(int argc, char **argv)
pidfile = optarg; pidfile = optarg;
break; break;
case 'R': /* --retry <schedule>|timeout */
retry = optarg;
break;
case 'r': /* --chroot /new/root */ case 'r': /* --chroot /new/root */
ch_root = optarg; ch_root = optarg;
break; break;
@@ -609,78 +729,175 @@ int main(int argc, char **argv)
case '2': /* --stderr /path/to/stderr.logfile */ case '2': /* --stderr /path/to/stderr.logfile */
redirect_stderr = optarg; redirect_stderr = optarg;
break; break;
case '3': /* --reexec */
reexec = true;
break;
case_RC_COMMON_GETOPT case_RC_COMMON_GETOPT
} }
if (!pidfile) if (!pidfile && !reexec)
eerrorx("%s: --pidfile must be specified", applet); eerrorx("%s: --pidfile must be specified", applet);
endpwent(); endpwent();
argc -= optind; argc -= optind;
argv += optind; argv += optind;
exec = *argv; exec = *argv;
if (start) {
if (!exec)
eerrorx("%s: nothing to start", applet);
if (respawn_delay * respawn_max > respawn_period) {
ewarn("%s: Please increase the value of --respawn-period to more "
"than %d to avoid infinite respawning", applet,
respawn_delay * respawn_max);
}
}
/* Expand ~ */ /* Expand ~ */
if (ch_dir && *ch_dir == '~') if (ch_dir && *ch_dir == '~')
ch_dir = expand_home(home, ch_dir); ch_dir = expand_home(home, ch_dir);
if (ch_root && *ch_root == '~') if (ch_root && *ch_root == '~')
ch_root = expand_home(home, 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 */ umask(numask);
if (*exec == '/' || *exec == '.') {
/* Full or relative path */ if (reexec) {
if (ch_root) str = rc_service_value_get(svcname, "argc");
snprintf(exec_file, sizeof(exec_file), sscanf(str, "%d", &child_argc);
"%s/%s", ch_root, exec); child_argv = xmalloc((child_argc + 1) * sizeof(char *));
else memset(child_argv, 0, (child_argc + 1) * sizeof(char *));
snprintf(exec_file, sizeof(exec_file), for (x = 0; x < child_argc; x++) {
"%s", exec); sprintf(name, "argv_%d", x);
} else { str = rc_service_value_get(svcname, name);
/* Something in $PATH */ child_argv[x] = str;
p = tmp = xstrdup(getenv("PATH")); }
*exec_file = '\0'; free(str);
while ((token = strsep(&p, ":"))) { str = rc_service_value_get(svcname, "child_pid");
sscanf(str, "%d", &child_pid);
free(str);
exec = rc_service_value_get(svcname, "exec");
pidfile = rc_service_value_get(svcname, "pidfile");
retry = rc_service_value_get(svcname, "retry");
if (retry) {
parse_schedule(applet, retry, sig);
rc_service_value_set(svcname, "retry", retry);
} else
parse_schedule(applet, NULL, sig);
str = rc_service_value_get(svcname, "respawn_delay");
sscanf(str, "%d", &respawn_delay);
str = rc_service_value_get(svcname, "respawn_max");
sscanf(str, "%d", &respawn_max);
supervisor(exec, child_argv);
} else if (start) {
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) if (ch_root)
snprintf(exec_file, sizeof(exec_file), snprintf(exec_file, sizeof(exec_file),
"%s/%s/%s", "%s/%s", ch_root, exec);
ch_root, token, exec);
else else
snprintf(exec_file, sizeof(exec_file), snprintf(exec_file, sizeof(exec_file),
"%s/%s", token, exec); "%s", exec);
if (exists(exec_file)) } else {
break; /* Something in $PATH */
p = tmp = xstrdup(getenv("PATH"));
*exec_file = '\0'; *exec_file = '\0';
while ((token = strsep(&p, ":"))) {
if (ch_root)
snprintf(exec_file, sizeof(exec_file),
"%s/%s/%s",
ch_root, token, exec);
else
snprintf(exec_file, sizeof(exec_file),
"%s/%s", token, exec);
if (exists(exec_file))
break;
*exec_file = '\0';
}
free(tmp);
} }
free(tmp); if ( !exists(exec_file))
} eerrorx("%s: %s does not exist", applet,
} *exec_file ? exec_file : exec);
if (start && !exists(exec_file)) } else
eerrorx("%s: %s does not exist", applet, eerrorx("%s: nothing to start", applet);
*exec_file ? exec_file : exec);
if (stop) { pid = get_pid(applet, pidfile);
pid = get_pid(pidfile); if (pid != -1)
if (pid == -1) if (do_stop(applet, exec, (const char * const *)argv, pid, uid,
i = pid; 0, false, true) > 0)
else eerrorx("%s: %s is already running", applet, exec);
if (respawn_delay * respawn_max > respawn_period)
ewarn("%s: Please increase the value of --respawn-period to more "
"than %d to avoid infinite respawning", applet,
respawn_delay * respawn_max);
if (retry) {
parse_schedule(applet, retry, sig);
rc_service_value_set(svcname, "retry", retry);
} else
parse_schedule(applet, NULL, sig);
einfov("Detaching to start `%s'", exec);
syslog(LOG_INFO, "Supervisor command line: %s", cmdline);
/* Remove existing pidfile */
if (pidfile)
unlink(pidfile);
/* Make sure we can write a pid file */
fp = fopen(pidfile, "w");
if (! fp)
eerrorx("%s: fopen `%s': %s", applet, pidfile, strerror(errno));
fclose(fp);
rc_service_value_set(svcname, "pidfile", pidfile);
sprintf(name, "%i", respawn_delay);
rc_service_value_set(svcname, "respawn_delay", name);
sprintf(name, "%i", respawn_max);
rc_service_value_set(svcname, "respawn_max", name);
sprintf(name, "%i", respawn_period);
rc_service_value_set(svcname, "respawn_period", name);
child_pid = fork();
if (child_pid == -1)
eerrorx("%s: fork: %s", applet, strerror(errno));
if (child_pid != 0)
/* first parent process, do nothing. */
exit(EXIT_SUCCESS);
#ifdef TIOCNOTTY
tty_fd = open("/dev/tty", O_RDWR);
#endif
devnull_fd = open("/dev/null", O_RDWR);
child_pid = fork();
if (child_pid == -1)
eerrorx("%s: fork: %s", applet, strerror(errno));
else if (child_pid != 0) {
c = argv;
x = 0;
while (c && *c) {
snprintf(name, sizeof(name), "argv_%-d",x);
rc_service_value_set(svcname, name, *c);
x++;
c++;
}
sprintf(name, "%d", x);
rc_service_value_set(svcname, "argc", name);
rc_service_value_set(svcname, "exec", exec);
supervisor(exec, argv);
} else
child_process(exec, argv);
} else if (stop) {
pid = get_pid(applet, pidfile);
if (pid != -1) {
i = kill(pid, SIGTERM); i = kill(pid, SIGTERM);
if (i != 0) if (i != 0)
/* We failed to stop something */ /* We failed to send the signal */
exit(EXIT_FAILURE); ewarn("Unable to shut down the supervisor");
else {
/* wait for the supervisor to go down */
while (kill(pid, 0) == 0) {
ts.tv_sec = 0;
ts.tv_nsec = 1;
nanosleep(&ts, NULL);
}
}
}
/* Even if we have not actually killed anything, we should /* Even if we have not actually killed anything, we should
* remove information about it as it may have unexpectedly * remove information about it as it may have unexpectedly
@@ -696,113 +913,4 @@ int main(int argc, char **argv)
} }
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
} }
pid = get_pid(pidfile);
if (pid != -1)
if (kill(pid, 0) == 0)
eerrorx("%s: %s is already running", applet, exec);
einfov("Detaching to start `%s'", exec);
eindentv();
/* Remove existing pidfile */
if (pidfile)
unlink(pidfile);
/*
* Make sure we can write a pid file
*/
fp = fopen(pidfile, "w");
if (! fp)
eerrorx("%s: fopen `%s': %s", applet, pidfile, strerror(errno));
fclose(fp);
child_pid = fork();
if (child_pid == -1)
eerrorx("%s: fork: %s", applet, strerror(errno));
/* first parent process, do nothing. */
if (child_pid != 0)
exit(EXIT_SUCCESS);
#ifdef TIOCNOTTY
tty_fd = open("/dev/tty", O_RDWR);
#endif
devnull_fd = open("/dev/null", O_RDWR);
child_pid = fork();
if (child_pid == -1)
eerrorx("%s: fork: %s", applet, strerror(errno));
if (child_pid != 0) {
/* this is the supervisor */
umask(numask);
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
*/
i = 0;
while (!exiting) {
wait(&i);
if (exiting) {
syslog(LOG_INFO, "stopping %s, pid %d", exec, child_pid);
kill(child_pid, SIGTERM);
} else {
sleep(respawn_delay);
if (respawn_max > 0 && respawn_period > 0) {
respawn_now = time(NULL);
if (first_spawn == 0)
first_spawn = respawn_now;
if (respawn_now - first_spawn > respawn_period) {
respawn_count = 0;
first_spawn = 0;
} else
respawn_count++;
if (respawn_count >= respawn_max) {
syslog(LOG_INFO, "respawned \"%s\" too many times, "
"exiting", exec);
exiting = true;
continue;
}
}
if (WIFEXITED(i))
syslog(LOG_INFO, "%s, pid %d, exited with return code %d",
exec, child_pid, WEXITSTATUS(i));
else if (WIFSIGNALED(i))
syslog(LOG_INFO, "%s, pid %d, terminated by signal %d",
exec, child_pid, WTERMSIG(i));
child_pid = fork();
if (child_pid == -1)
eerrorx("%s: fork: %s", applet, strerror(errno));
if (child_pid == 0)
child_process(exec, argv, svcname, respawn_count);
}
}
if (pidfile && exists(pidfile))
unlink(pidfile);
if (svcname) {
rc_service_daemon_set(svcname, exec,
(const char *const *)argv,
pidfile, false);
rc_service_mark(svcname, RC_SERVICE_STOPPED);
}
exit(EXIT_SUCCESS);
} else if (child_pid == 0)
child_process(exec, argv, svcname, respawn_count);
} }