Compare commits

..

10 Commits
0.37 ... 0.27.x

Author SHA1 Message Date
William Hubbs
f1716ef9ef Update ChangeLog 2017-06-12 18:13:03 -05:00
William Hubbs
fe5567bb04 fix argument parsing for the sysvinit shutdown wrapper
This fixes #140.
2017-06-12 18:08:14 -05:00
William Hubbs
88e9954828 scripts/shutdown: fix arguments to be sysvinit shutdown compatible
This fixes #140.
2017-06-12 18:08:14 -05:00
William Hubbs
a4b9c97282 Fix link to shutdown for MKSYSVINIT=yes 2017-06-12 18:08:14 -05:00
udeved
d47288d838 scripts/Makefile: make symlinks absolute instead of relative
This closes #142.
2017-06-12 18:08:14 -05:00
udeved
1cf0c98514 scripts/Makefile: respect SBINDIR with MKSYSVINIT
This is for #142.
2017-06-12 18:08:14 -05:00
William Hubbs
0cded681e8 version 0.27.2 2017-06-12 18:06:24 -05:00
Jory A. Pratt
03e61ccd30 kill_all: include limits.h for PATH_MAX 2017-06-08 10:10:07 -05:00
William Hubbs
7067f7670e fix compile issue for musl 2017-06-08 08:49:26 -05:00
William Hubbs
3c198185b2 version 0.27.1 2017-06-08 08:48:09 -05:00
120 changed files with 2906 additions and 4907 deletions

View File

@@ -1,5 +1,4 @@
Using Busybox as your Default Shell with OpenRC # Using Busybox as your Default Shell with OpenRC
===============================================
If you have/bin/sh linked to busybox, you need to be aware of several If you have/bin/sh linked to busybox, you need to be aware of several
incompatibilities between busybox's applets and the standalone incompatibilities between busybox's applets and the standalone

3009
ChangeLog

File diff suppressed because it is too large Load Diff

View File

@@ -1,12 +1,11 @@
Features Scheduled for Removal # Features Scheduled for Removal
==============================
The following is a list of files and features that are going to be removed in The following is a list of files and features that are going to be removed in
the source tree. Every entry should contain what exactly is going away, why it the source tree. Every entry should contain what exactly is going away, why it
is happening, and who is going to be doing the work. When the feature is is happening, and who is going to be doing the work. When the feature is
removed, it should also be removed from this file. removed, it should also be removed from this file.
# Service pause action ## Service pause action
When: 1.0 When: 1.0
@@ -14,7 +13,7 @@ Why: The same affect can be obtained with the --nodeps option to stop.
Who: Who:
# start-stop-daemon options --startas, --chuid , --oknodo ## start-stop-daemon options --startas, --chuid , --oknodo
When: 1.0 When: 1.0
@@ -26,7 +25,7 @@ Why: Obsolete or replaced by other options.
Who: Who:
# runscript and rc symbolic links ## runscript and rc symbolic links
When: 1.0 When: 1.0
@@ -35,7 +34,7 @@ Why: Deprecated in favor of openrc-run and openrc due to naming
Who: Who:
# support for the opts variable in service scripts ## support for the opts variable in service scripts
When: 1.0 When: 1.0
@@ -44,7 +43,7 @@ Why: Deprecated in favor of extra_commands, extra_started_commands
Who: Who:
# support for local_start and local_stop ## support for local_start and local_stop
When: 1.0 When: 1.0
@@ -52,7 +51,7 @@ Why: Deprecated in favor of executable scripts in @SYSCONFDIR@/local.d
Who: Who:
# the mtab service script ## the mtab service script
When: force /etc/mtab to link to /proc/self/mounts in 1.0, remove When: force /etc/mtab to link to /proc/self/mounts in 1.0, remove
service in 2.0 service in 2.0
@@ -62,13 +61,13 @@ Why: /etc/mtab should be a symbolic link to /proc/self/mounts on modern
Who: Who:
# C API Functions in rc.h ## C API Functions in rc.h
If you have a c program that links to librc and uses functions from If you have a c program that links to librc and uses functions from
there, this section will list API functions which are deprecated and there, this section will list API functions which are deprecated and
will be removed along with the reason they are being removed. will be removed along with the reason they are being removed.
## rc_getline() ### rc_getline()
When: 1.0 When: 1.0

View File

@@ -1,5 +1,4 @@
OpenRC History # OpenRC History
==============
This history of OpenRC was written by Daniel Robbins, Roy Marples, William This history of OpenRC was written by Daniel Robbins, Roy Marples, William
Hubbs and others. Hubbs and others.

View File

@@ -15,24 +15,12 @@ include ${TOP}/Makefile.inc
SUBDIR= conf.d etc init.d local.d man scripts sh src support sysctl.d 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 # Build pkgconfig or not
MKPKGCONFIG?= yes MKPKGCONFIG?= yes
ifeq (${MKPKGCONFIG},yes) ifeq (${MKPKGCONFIG},yes)
SUBDIR+= pkgconfig SUBDIR+= pkgconfig
endif endif
# Build zsh completion or not
MKZSHCOMP?= no
ifeq (${MKZSHCOMP},yes)
SUBDIR+= zsh-completion
endif
# We need to ensure that runlevels is done last # We need to ensure that runlevels is done last
SUBDIR+= runlevels SUBDIR+= runlevels

View File

@@ -1,3 +1,3 @@
NAME= openrc NAME= openrc
VERSION= 0.37 VERSION= 0.27.2
PKG= ${NAME}-${VERSION} PKG= ${NAME}-${VERSION}

87
NEWS.md
View File

@@ -1,93 +1,8 @@
OpenRC NEWS # OpenRC NEWS
===========
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.37
start-stop-daemon now supports logging stdout and stderr of daemons to
processes instead of files. These processes are defined by the
output_logger and error_logger variables in standard service scripts, or
by the -3/--output-logger or -4/--error-logger switches if you use
start-stop-daemon directly. For more information on this, see the
start-stop-daemon man page.
## OpenRC 0.36
In this release, the modules-load service has been combined into the
modules service since there is no reason I know of to keep them
separate. However, modules also provides modules-load in case you were
using modules-load in your dependencies.
The consolefont, keymaps, numlock and procfs service scripts no longer
have a dependency on localmount.
If you are a linux user and are still separaating / from /usr,
you will need to add the following line to the appropriate conf.d files:
rc_need="localmount"
## OpenRC 0.35
In this version, the cgroups mounting logic has been moved from the
sysfs service to the cgroups service. This was done so cgroups can be
mounted inside an lxc/lxd container without using the other parts of the
sysfs service.
?As a result of this change, if you are upgrading, you need to add
cgroups to your sysinit runlevel by running the following command as
root:
```
# rc-update add cgroups sysinit
```
For more information, see the following issue:
https://github.com/openrc/openrc/issues/187
Consider this your second notification with regard to /etc/mtab being a
file instead of a symbolic link.
In this version, the mtab service will complain loudly if you have
mtab_is_file set to yes and recommend that you change this to no and
restart the mtab service to migrate /etc/mtab to a symbolic link.
If there is a valid technical reason to keep /etc/mtab as a flat file
instead of a symbolic link to /proc/self/mounts, we are interested and
we will keep the support in that case. Please open an issue and let us
know however. Otherwise, consider this your final notice that the mtab
service will be removed in the future.
## 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
This version mounts efivars read only due to concerns about changes in
this file system making systems unbootable. If you need to change something
in this path, you will need to re-mount it read-write, make the change
and re-mount it read-only.
Also, you can override this behavior by adding a line for efivars to
fstab if you want efivars mounted read-write.
For more information on this issue, see the following url:
https://github.com/openrc/openrc/issues/134
## OpenRC-0.25 ## OpenRC-0.25
This version contains an OpenRC-specific implementation of init for This version contains an OpenRC-specific implementation of init for

View File

@@ -1,5 +1,4 @@
OpenRC README # OpenRC README
=============
OpenRC is a dependency-based init system that works with the OpenRC is a dependency-based init system that works with the
system-provided init program, normally `/sbin/init`. Currently, it does system-provided init program, normally `/sbin/init`. Currently, it does
@@ -23,7 +22,6 @@ below arguments to the make command
PROGLDFLAGS=-static PROGLDFLAGS=-static
LIBNAME=lib64 LIBNAME=lib64
DESTDIR=/tmp/openrc-image DESTDIR=/tmp/openrc-image
MKBASHCOMP=no
MKNET=no MKNET=no
MKPAM=pam MKPAM=pam
MKPREFIX=yes MKPREFIX=yes
@@ -33,7 +31,6 @@ MKSTATICLIBS=no
MKSYSVINIT=yes MKSYSVINIT=yes
MKTERMCAP=ncurses MKTERMCAP=ncurses
MKTERMCAP=termcap MKTERMCAP=termcap
MKZSHCOMP=no
PKG_PREFIX=/usr/pkg PKG_PREFIX=/usr/pkg
LOCAL_PREFIX=/usr/local LOCAL_PREFIX=/usr/local
PREFIX=/usr/local PREFIX=/usr/local

View File

@@ -1,5 +1,4 @@
OpenRC Coding Style Guide # OpenRC Style Guide
=========================
This is the openrc style manual. It governs the coding style of all code This is the openrc style manual. It governs the coding style of all code
in this repository. Follow it. Contact openrc@gentoo.org for any questions in this repository. Follow it. Contact openrc@gentoo.org for any questions

View File

@@ -1,5 +1,4 @@
Setting up the agetty service in OpenRC # Setting up the agetty service in OpenRC
=================================xxxxxx
The agetty service is an OpenRC specific way to monitor and respawn a The agetty service is an OpenRC specific way to monitor and respawn a
getty, using agetty, on Linux. To use this method, make sure you aren't getty, using agetty, on Linux. To use this method, make sure you aren't

View File

@@ -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

View File

@@ -1,24 +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/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.
#
# openrc completion command
#
_openrc()
{
local cur
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
if [[ ${#COMP_WORDS[*]} -le 2 ]]; then
COMPREPLY=($(compgen -W "$(rc-status --list)" -- $cur))
fi
return 0
} &&
complete -F _openrc openrc

View File

@@ -1,29 +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/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.
_openrc_service_script()
{
local script="${COMP_WORDS[0]}"
local cur="${COMP_WORDS[$COMP_CWORD]}"
if [[ ( -f "${script}" || -h "${script}" ) && -r "${script}" ]] \
&& [[ "$(head -n 1 "${script}")" =~ \#\!.*/openrc-run ]]
then
[[ $COMP_CWORD -gt 1 ]] && return 1
COMPREPLY=($(opts="start stop status restart pause zap ineed needsme iuse usesme broken"; \
eval "$(grep '^opts=' "${script}")"; echo "${opts}"))
[[ -n "$COMPREPLY" ]] || COMPREPLY=(start stop restart zap)
COMPREPLY=($(compgen -W "${COMPREPLY[*]}" -- "${cur}"))
else
COMPREPLY=($(compgen -o default -- "${cur}"))
fi
return 0
}
complete -F _openrc_service_script */etc/init.d/*

View File

@@ -1,113 +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/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.
#
# rc-service completion command
#
_rc_service()
{
local cur prev numwords opts
local words i x filename
local action actionpos
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
numwords=${#COMP_WORDS[*]}
if [[ ${prev} == '>' || ${prev} == '<' ]] ; then
COMPREPLY=($(compgen -f -- ${cur}))
return 0
fi
# find action
for x in ${COMP_LINE} ; do
if [[ ${x} =~ --(list|exists|resolve) ]] || [[ ${x} =~ -(l|e|r) ]]
then
action=${x}
break
fi
done
if [[ -n ${action} ]]; then
for ((i = 0; i < ${numwords}; i++ )); do
if [[ ${COMP_WORDS[${i}]} == "${action}" ]]; then
actionpos=${i}
break
fi
done
for ((i = 1; i < ${numwords}; i++ )); do
if [[ ! ${COMP_WORDS[$i]} == -* ]]; then
break
fi
done
fi
if [[ ${COMP_CWORD} -eq 3 ]]; then
return 1
fi
# check if an option was typed
if [[ ${cur} == -* ]]; then
if [[ ${cur} == --* ]]; then
opts="--list --exists --resolve"
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
elif [[ ${cur} == -* ]]; then
opts="-l -e -r"
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
fi
# NOTE: This slows things down!
# (Adapted from bash_completion by Ian Macdonald <ian@caliban.org>)
# This removes any options from the list of completions that have
# already been specified on the command line.
COMPREPLY=($(echo "${COMP_WORDS[@]}" | \
(while read -d ' ' i; do
[[ -z ${i} ]] && continue
# flatten array with spaces on either side,
# otherwise we cannot grep on word boundaries of
# first and last word
COMPREPLY=" ${COMPREPLY[@]} "
# remove word from list of completions
COMPREPLY=(${COMPREPLY/ ${i%% *} / })
done
echo ${COMPREPLY[@]})))
return 0
else
# no option was typed
if [[ ${COMP_CWORD} -eq 1 ]]; then # if first word typed
words="$(rc-service --list | grep ^${cur})" # complete for init scripts
COMPREPLY=($(for i in ${words} ; do \
[[ ${i} == ${cur}* ]] && echo ${i} ; \
done))
return 0
elif [[ ${COMP_CWORD} -eq 2 ]] && [[ ${prev} != -* ]]; then # if second word typed and we didn't type in a function
filename=$(rc-service --resolve ${prev})
opts=$(cat ${filename} | grep "^\w*()" | sed "s/().*$//") # Greps the functions included in the init script
if [[ "x${opts}" == "x" ]] ; then # if no options found loosen the grep algorhythm
opts=$(cat ${filename} | grep "\w*()" | sed "s/().*$//")
fi
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
fi
fi
if [[ ${action} == '--exists' ]] || [[ ${action} == '-e' ]] || \
[[ ${action} == '--resolve' ]] || [[ ${action} == '-r' ]]; then
words="$(rc-service --list | grep ^${cur})"
COMPREPLY=($(for i in ${words} ; do \
[[ ${i} == ${cur}* ]] && echo ${i} ; \
done))
return 0
fi
return 0
} &&
complete -F _rc_service rc-service

View File

@@ -1,31 +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/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.
#
# rc-status completion command
#
_rcstatus()
{
local cur
cur="${COMP_WORDS[COMP_CWORD]}"
if [[ $COMP_CWORD -eq 1 ]]; then
if [[ "${cur}" == --* ]]; then
COMPREPLY=($(compgen -W '--all --list --unused' -- ${cur}))
elif [[ "${cur}" == -* ]]; then
COMPREPLY=($(compgen -W '-a -l -u' -- ${cur}))
else
COMPREPLY=($(compgen -W "$(rc-status --list)" -- ${cur}))
fi
else
unset COMPREPLY
fi
return 0
} &&
complete -F _rcstatus rc-status

View File

@@ -1,42 +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/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.
#
# rc-update completion command
#
_rc_update()
{
local cur show
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
if [[ $COMP_CWORD -eq 1 ]]; then
if [[ "${cur}" == -* ]]; then
COMPREPLY=($(compgen -W '-a -d -s' -- ${cur}))
else
COMPREPLY=($(compgen -W 'add del show' ${cur}))
fi
else
if [[ "${COMP_WORDS[1]}" == "show" ]] || [[ "${COMP_WORDS[1]}" == "-s" ]]; then
show="TRUE"
fi
if ([[ $COMP_CWORD -eq 3 ]] && [[ -z "$show" ]]) || \
([[ $COMP_CWORD -eq 2 ]] && [[ -n "$show" ]]); then
COMPREPLY=($(compgen -W "$(rc-status --list)" -- $cur))
elif [[ $COMP_CWORD -eq 2 ]]; then
COMPREPLY=($(compgen -W "$(rc-service --list)" $cur))
elif [[ ${#COMP_WORDS[*]} -gt 2 ]] ; then
COMPREPLY=($(compgen -W "$(rc-status --list)" -- $cur))
else
unset COMPREPLY
fi
fi
return 0
} &&
complete -F _rc_update rc-update

View File

@@ -1,6 +1,3 @@
# 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

@@ -3,7 +3,7 @@
#no_umounts="/dir1:/var/dir2" #no_umounts="/dir1:/var/dir2"
# #
# Mark certain mount points as critical. # Mark certain mount points as critical.
# This contains a space separated list of mount points which should be # This contains aspace separated list of mount points which should be
# considered critical. If one of these mount points cannot be mounted, # considered critical. If one of these mount points cannot be mounted,
# localmount will fail. # localmount will fail.
# By default, this is empty. # By default, this is empty.

View File

@@ -1,13 +1,5 @@
# As far as we are aware, there are no modern linux tools or use cases
# which require /etc/mtab to be a separate file from /proc/self/mounts,
# so this setting should be commented out.
# If it is set to yes, please comment it out and run this command:
# # rc-service mtab restart
# In the future, the mtab service will be removed since we are not aware
# of any need to manipulate /etc/mtab as a separate file from
# /proc/self/mounts.
# If you have a technical reason we should keep this support, please
# open an issue at https://github.com/openrc/openrc/issues and let us
# know about your situation.
# This setting controls whether /etc/mtab is a file or symbolic link. # This setting controls whether /etc/mtab is a file or symbolic link.
# Most of the time, you shouldn't touch this. However, if the default
# breaks your system in some way, please see the NEWS.md file that comes
# with OpenRC for the actions to take.
# mtab_is_file=no # mtab_is_file=no

View File

@@ -191,48 +191,13 @@ rc_tty_number=12
############################################################################## ##############################################################################
# LINUX CGROUPS RESOURCE MANAGEMENT # LINUX CGROUPS RESOURCE MANAGEMENT
# This sets the mode used to mount cgroups. # If you have cgroups turned on in your kernel, this switch controls
# "hybrid" mounts cgroups version 2 on /sys/fs/cgroup/unified and # whether or not a group for each controller is mounted under
# cgroups version 1 on /sys/fs/cgroup. # /sys/fs/cgroup.
# "legacy" mounts cgroups version 1 on /sys/fs/cgroup # None of the other options in this section work if this is set to "NO".
# "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 setting turns on the memory.use_hierarchy setting in the # The following settings allow you to set up values for the cgroup
# root memory cgroup for cgroups v1.
# It must be set to yes in this file if you want this functionality.
#rc_cggroup_memory_use_hierarchy="NO"
# 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.
@@ -246,9 +211,8 @@ 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 version 1, see Documentation/cgroups-v1/* in the linux kernel #cgroups, see Documentation/cgroups/* in the linux kernel source tree.
# source tree.
# Set the blkio controller settings for this service. # Set the blkio controller settings for this service.
#rc_cgroup_blkio="" #rc_cgroup_blkio=""
@@ -282,33 +246,10 @@ 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.
# Be aware that setting this to yes means all of a service's # This should not be set globally because it kills all of the service's
# child processes will be killed. Keep this in mind if you set this to # child processes, and most of the time this is undesirable. Please set
# yes here instead of for the individual services in # it in /etc/conf.d/<service>.
# /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

@@ -1,6 +1,3 @@
OpenRC Users Guide
==================
# Purpose and description # Purpose and description
OpenRC is an init system for Unixoid operating systems. It takes care of OpenRC is an init system for Unixoid operating systems. It takes care of
@@ -30,8 +27,8 @@ openrc scans the runlevels (default: `/etc/runlevels`) and builds a dependency
graph, then starts the needed service scripts, either serialized (default) or in graph, then starts the needed service scripts, either serialized (default) or in
parallel. parallel.
When all the service scripts are started openrc terminates. There is no When all the init scripts are started openrc terminates. There is no persistent
persistent daemon. (Integration with tools like monit, runit or s6 can be done) daemon. (Integration with tools like monit, runit or s6 can be done)
# Shutdown # Shutdown
@@ -56,6 +53,9 @@ 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
@@ -66,7 +66,7 @@ own if needed. This allows, for example, to have a default runlevel with
disabled. disabled.
The `rc-status` helper will print all currently active runlevels and the state The `rc-status` helper will print all currently active runlevels and the state
of services in them: of init scripts in them:
``` ```
# rc-status # rc-status
@@ -77,7 +77,7 @@ Runlevel: default
``` ```
All runlevels are represented as folders in `/etc/runlevels/` with symlinks to All runlevels are represented as folders in `/etc/runlevels/` with symlinks to
the actual service scripts. the actual init scripts.
Calling openrc with an argument (`openrc default`) will switch to that Calling openrc with an argument (`openrc default`) will switch to that
runlevel; this will start and stop services as needed. runlevel; this will start and stop services as needed.
@@ -86,12 +86,121 @@ Managing runlevels is usually done through the `rc-update` helper, but could of
course be done by hand if desired. course be done by hand if desired.
e.g. `rc-update add nginx default` - add nginx to the default runlevel e.g. `rc-update add nginx default` - add nginx to the default runlevel
Note: This will not auto-start nginx! You'd still have to trigger `rc` or run Note: This will not auto-start nginx! You'd still have to trigger `rc` or run
the service script by hand. the initscript by hand.
FIXME: Document stacked runlevels FIXME: Document stacked runlevels
The default startup uses the runlevels `sysinit`, `boot`, and `default`, The default startup uses the runlevels `boot`, `sysinit` and `default`, in that
in that order. Shutdown uses the `shutdown` runlevel. order. Shutdown uses the `shutdown` runlevel.
# Syntax of Service Scripts
Service scripts are shell scripts. OpenRC aims at using only the standardized
POSIX sh subset for portability reasons. The default interpreter (build-time
toggle) is `/bin/sh`, so using for example mksh is not a problem.
OpenRC has been tested with busybox sh, ash, dash, bash, mksh, zsh and possibly
others. Using busybox sh has been difficult as it replaces commands with
builtins that don't offer the expected features.
The interpreter for initscripts is `#!/sbin/openrc-run`.
Not using this interpreter will break the use of dependencies and is not
supported. (iow: if you insist on using `#!/bin/sh` you're on your own)
A `depend` function declares the dependencies of this service script.
All scripts must have start/stop/status functions, but defaults are provided.
Extra functions can be added easily:
```
extra_commands="checkconfig"
checkconfig() {
doSomething
}
```
This exports the checkconfig function so that `/etc/init.d/someservice
checkconfig` will be available, and it "just" runs this function.
While commands defined in `extra_commands` are always available, commands
defined in `extra_started_commands` will only work when the service is started
and those defined in `extra_stopped_commands` will only work when the service is
stopped. This can be used for implementing graceful reload and similar
behaviour.
Adding a restart function will not work, this is a design decision within
OpenRC. Since there may be dependencies involved (e.g. network -> apache) a
restart function is in general not going to work.
restart is internally mapped to `stop()` + `start()` (plus handling dependencies).
If a service needs to behave differently when it is being restarted vs
started or stopped, it should test the `$RC_CMD` variable, for example:
```
[ "$RC_CMD" = restart ] && do_something
```
# The Depend Function
This function declares the dependencies for a service script. This
determines the order the service scripts start.
```
depend() {
need net
use dns logger netmount
want coolservice
}
```
`need` declares a hard dependency - net always needs to be started before this
service does
`use` is a soft dependency - if dns, logger or netmount is in this runlevel
start it before, but we don't care if it's not in this runlevel.
`want` is between need and use - try to start coolservice if it is
installed on the system, regardless of whether it is in the
runlevel, but we don't care if it starts.
`before` declares that we need to be started before another service
`after` declares that we need to be started after another service, without
creating a dependency (so on calling stop the two are independent)
`provide` allows multiple implementations to provide one service type, e.g.:
`provide cron` is set in all cron-daemons, so any one of them started
satisfies a cron dependency
`keyword` allows platform-specific overrides, e.g. `keyword -lxc` makes this
service script a noop in lxc containers. Useful for things like keymaps,
module loading etc. that are either platform-specific or not available
in containers/virtualization/...
FIXME: Anything missing in this list?
# The Default Functions
All service scripts are assumed to have the following functions:
```
start()
stop()
status()
```
There are default implementations in `lib/rc/sh/openrc-run.sh` - this allows very
compact service scripts. These functions can be overridden per service script as
needed.
The default functions assume the following variables to be set in the service
script:
```
command=
command_args=
pidfile=
```
Thus the 'smallest' service scripts can be half a dozen lines long
# The Magic of `conf.d` # The Magic of `conf.d`
@@ -111,7 +220,7 @@ start() {
} }
``` ```
The big advantage of this split is that most of the time editing of the service The big advantage of this split is that most of the time editing of the init
script can be avoided. script can be avoided.
# Start-Stop-Daemon # Start-Stop-Daemon
@@ -132,39 +241,21 @@ 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 Setting `ulimit` and `nice` values per service can be done through the `rc_ulimit`
`rc_ulimit` variable. variable.
Under Linux, OpenRC can use cgroups for process management as well. Once Under Linux, OpenRC can optionally use CGroups for process management.
the kernel is configured appropriately, the `rc_cgroup_mode` setting in By default each service script's processes are migrated to their own CGroup.
/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 By changing certain values in the `conf.d` file limits can be enforced per
enforced per service. These settings are documented in detail in the service. It is easy to find orphan processes of a service that persist after
default /etc/rc.conf under `LINUX CGROUPS RESOURCE MANAGEMENT`. `stop()`, but by default these will NOT be terminated.
To change this add `rc_cgroup_cleanup="yes"` in the `conf.d` files for services
# Dealing with Orphaned Processes where you desire this functionality.
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.
# Caching # Caching
For performance reasons OpenRC keeps a cache of pre-parsed service metadata For performance reasons OpenRC keeps a cache of pre-parsed initscript metadata
(e.g. `depend`). The default location for this is `/${RC_SVCDIR}/cache`. (e.g. `depend`). The default location for this is `/${RC_SVCDIR}/cache`.
The cache uses `mtime` to check for file staleness. Should any service script The cache uses `mtime` to check for file staleness. Should any service script
@@ -174,5 +265,5 @@ change it'll re-source the relevant files and update the cache
OpenRC has wrappers for many common output tasks in libeinfo. OpenRC has wrappers for many common output tasks in libeinfo.
This allows to print colour-coded status notices and other things. This allows to print colour-coded status notices and other things.
To make the output consistent the bundled service scripts all use ebegin/eend to To make the output consistent the bundled initscripts all use ebegin/eend to
print nice messages. print nice messages.

1
init.d/.gitignore vendored
View File

@@ -1,6 +1,5 @@
agetty agetty
binfmt binfmt
cgroups
modules-load modules-load
bootmisc bootmisc
fsck fsck

View File

@@ -19,12 +19,11 @@ SRCS-FreeBSD= hostid.in modules.in moused.in newsyslog.in pf.in rarpd.in \
rc-enabled.in rpcbind.in savecore.in syslogd.in rc-enabled.in rpcbind.in savecore.in syslogd.in
# These are FreeBSD specific # These are FreeBSD specific
SRCS-FreeBSD+= adjkerntz.in devd.in dumpon.in encswap.in ipfw.in \ SRCS-FreeBSD+= adjkerntz.in devd.in dumpon.in encswap.in ipfw.in \
mixer.in nscd.in powerd.in syscons.in modules-load.in mixer.in nscd.in powerd.in syscons.in
SRCS-Linux= agetty.in binfmt.in devfs.in cgroups.in dmesg.in hwclock.in \ SRCS-Linux= agetty.in binfmt.in devfs.in dmesg.in hwclock.in consolefont.in \
consolefont.in keymaps.in killprocs.in modules.in \ keymaps.in killprocs.in modules.in modules-load.in mount-ro.in mtab.in \
mount-ro.in mtab.in numlock.in procfs.in net-online.in sysfs.in \ numlock.in procfs.in net-online.in sysfs.in termencoding.in
termencoding.in
# Generic BSD scripts # Generic BSD scripts
SRCS-NetBSD= hostid.in moused.in newsyslog.in pf.in rarpd.in rc-enabled.in \ SRCS-NetBSD= hostid.in moused.in newsyslog.in pf.in rarpd.in rc-enabled.in \

View File

@@ -28,12 +28,5 @@ start_pre() {
eerror "symbolic links to it for the ports you want to start" eerror "symbolic links to it for the ports you want to start"
eerror "agetty on and add those to the appropriate runlevels." eerror "agetty on and add those to the appropriate runlevels."
return 1 return 1
else
export EINFO_QUIET="${quiet:-yes}"
fi fi
} }
stop_pre()
{
export EINFO_QUIET="${quiet:-yes}"
}

View File

@@ -1,149 +0,0 @@
#!@SBINDIR@/openrc-run
# 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.
description="Mount the control groups."
cgroup_opts=nodev,noexec,nosuid
depend()
{
keyword -docker -prefix -systemd-nspawn -vserver
after sysfs
}
cgroup1_base()
{
grep -qw cgroup /proc/filesystems || return 0
if ! mountinfo -q /sys/fs/cgroup; then
ebegin "Mounting cgroup filesystem"
local opts="${cgroup_opts},mode=755,size=${rc_cgroupsize:-10m}"
mount -n -t tmpfs -o "${opts}" cgroup_root /sys/fs/cgroup
eend $?
fi
if ! mountinfo -q /sys/fs/cgroup/openrc; then
local agent="${RC_LIBEXECDIR}/sh/cgroup-release-agent.sh"
mkdir /sys/fs/cgroup/openrc
mount -n -t cgroup \
-o none,${cgroup_opts},name=openrc,release_agent="$agent" \
openrc /sys/fs/cgroup/openrc
printf 1 > /sys/fs/cgroup/openrc/notify_on_release
fi
return 0
}
cgroup1_controllers()
{
yesno "${rc_controller_cgroups:-YES}" && [ -e /proc/cgroups ] &&
grep -qw cgroup /proc/filesystems || return 0
while read -r name _ _ enabled _; do
case "${enabled}" in
1) mountinfo -q "/sys/fs/cgroup/${name}" && continue
local x
for x in $rc_cgroup_controllers; do
[ "${name}" = "blkio" ] && [ "${x}" = "io" ] &&
continue 2
[ "${name}" = "${x}" ] &&
continue 2
done
mkdir "/sys/fs/cgroup/${name}"
mount -n -t cgroup -o "${cgroup_opts},${name}" \
"${name}" "/sys/fs/cgroup/${name}"
yesno "${rc_cgroup_memory_use_hierarchy:-no}" &&
[ "${name}" = memory ] &&
echo 1 > /sys/fs/cgroup/memory/memory.use_hierarchy
;;
esac
done < /proc/cgroups
return 0
}
cgroup2_base()
{
grep -qw cgroup2 /proc/filesystems || return 0
local base
base="$(cgroup2_find_path)"
mkdir -p "${base}"
mount -t cgroup2 none -o "${cgroup_opts},nsdelegate" "${base}" 2> /dev/null ||
mount -t cgroup2 none -o "${cgroup_opts}" "${base}"
return 0
}
cgroup2_controllers()
{
grep -qw cgroup2 /proc/filesystems || return 0
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()
{
cgroup1_base
cgroup2_base
cgroup2_controllers
cgroup1_controllers
return 0
}
cgroups_legacy()
{
cgroup1_base
cgroup1_controllers
return 0
}
cgroups_unified()
{
cgroup2_base
cgroup2_controllers
return 0
}
mount_cgroups()
{
case "${rc_cgroup_mode:-hybrid}" in
hybrid) cgroups_hybrid ;;
legacy) cgroups_legacy ;;
unified) cgroups_unified ;;
esac
return 0
}
restorecon_cgroups()
{
if [ -x /sbin/restorecon ]; then
ebegin "Restoring SELinux contexts in /sys/fs/cgroup"
restorecon -rF /sys/fs/cgroup >/dev/null 2>&1
eend $?
fi
return 0
}
start()
{
# set up kernel support for cgroups
if [ -d /sys/fs/cgroup ]; then
mount_cgroups
restorecon_cgroups
fi
return 0
}

View File

@@ -13,7 +13,7 @@ description="Sets a font for the consoles."
depend() depend()
{ {
need termencoding need localmount termencoding
after hotplug bootmisc modules after hotplug bootmisc modules
keyword -docker -lxc -openvz -prefix -systemd-nspawn -uml -vserver -xenu keyword -docker -lxc -openvz -prefix -systemd-nspawn -uml -vserver -xenu
} }

View File

@@ -20,12 +20,9 @@ depend()
mount_dev() mount_dev()
{ {
local action conf_d_dir devfstype msg mountopts local action=--mount devfstype msg=Mounting
action=--mount
conf_d_dir="${RC_SERVICE%/*/*}/conf.d"
msg=Mounting
# Some devices require exec, Bug #92921 # Some devices require exec, Bug #92921
mountopts="exec,nosuid,mode=0755" local mountopts="exec,nosuid,mode=0755"
if yesno ${skip_mount_dev:-no} ; then if yesno ${skip_mount_dev:-no} ; then
einfo "/dev will not be mounted due to user request" einfo "/dev will not be mounted due to user request"
return 0 return 0
@@ -36,7 +33,7 @@ mount_dev()
msg=Remounting msg=Remounting
fi fi
if fstabinfo -q /dev; then if fstabinfo -q /dev; then
ebegin "$msg /dev according to fstab" ebegin "$msg /dev according to @SYSCONFDIR@/fstab"
fstabinfo -q $action /dev fstabinfo -q $action /dev
eend $? eend $?
return 0 return 0
@@ -57,7 +54,7 @@ mount_dev()
ewarn "is no entry for /dev in fstab." ewarn "is no entry for /dev in fstab."
ewarn "This means /dev will not be mounted." ewarn "This means /dev will not be mounted."
ewarn "To avoid this message, set CONFIG_DEVTMPFS or CONFIG_TMPFS to y" ewarn "To avoid this message, set CONFIG_DEVTMPFS or CONFIG_TMPFS to y"
ewarn "in your kernel configuration or see ${conf_d_dir}/${RC_SVCNAME}" ewarn "in your kernel configuration or see @SYSCONFDIR@/conf.d/devfs"
fi fi
return 0 return 0
} }

View File

@@ -11,8 +11,7 @@
description="Sets the hostname of the machine." description="Sets the hostname of the machine."
depend() depend() {
{
after clock after clock
keyword -docker -lxc -prefix -systemd-nspawn keyword -docker -lxc -prefix -systemd-nspawn
} }
@@ -20,13 +19,13 @@ depend()
start() start()
{ {
local h source x local h source x
if [ -s /etc/hostname ] && [ -r /etc/hostname ]; then if [ -s @SYSCONFDIR@/hostname ] && [ -r @SYSCONFDIR@/hostname ]; then
read h x </etc/hostname read h x <@SYSCONFDIR@/hostname
source="from /etc/hostname" source=" from @SYSCONFDIR@/hostname"
else else
# HOSTNAME variable used to be defined in caps in conf.d/hostname. # HOSTNAME variable used to be defined in caps in conf.d/hostname.
# It is also a magic variable in bash. # It is also a magic variable in bash.
h=${hostname:-${HOSTNAME}} # checkbashisms: false positive (HOSTNAME var) h=${hostname-${HOSTNAME}} # checkbashisms: false positive
fi fi
if [ -z "$h" ]; then if [ -z "$h" ]; then
einfo "Using default system hostname" einfo "Using default system hostname"

View File

@@ -105,7 +105,7 @@ start()
done done
[ -n "$modname" ] && [ -n "$modname" ] &&
ewarn "The $modname module needs to be configured in" \ ewarn "The $modname module needs to be configured in" \
"${RC_SERVICE%/*/*}/conf.d/modules or built in." "@SYSCONFDIR@/conf.d/modules or built in."
fi fi
fi fi

View File

@@ -13,7 +13,7 @@ description="Applies a keymap for the consoles."
depend() depend()
{ {
need termencoding need localmount termencoding
after bootmisc clock after bootmisc clock
keyword -docker -lxc -openvz -prefix -systemd-nspawn -uml -vserver -xenu keyword -docker -lxc -openvz -prefix -systemd-nspawn -uml -vserver -xenu
} }

View File

@@ -9,10 +9,7 @@
# This file may not be copied, modified, propagated, or distributed # This file may not be copied, modified, propagated, or distributed
# except according to the terms contained in the LICENSE file. # except according to the terms contained in the LICENSE file.
conf_d_dir="${RC_SERVICE%/*/*}/conf.d" description="Executes user programs in @SYSCONFDIR@/local.d"
local_d_dir="${RC_SERVICE%/*/*}/local.d"
description="Executes user programs in ${local_d_dir}"
depend() depend()
{ {
@@ -22,12 +19,12 @@ depend()
start() start()
{ {
local file has_errors redirect retval
has_errors=0
yesno $rc_verbose || redirect='> /dev/null 2>&1'
ebegin "Starting local" ebegin "Starting local"
local file has_errors=0 redirect retval
yesno $rc_verbose || redirect='> /dev/null 2>&1'
eindent eindent
for file in "${local_d_dir}"/*.start; do for file in @SYSCONFDIR@/local.d/*.start; do
if [ -x "${file}" ]; then if [ -x "${file}" ]; then
vebegin "Executing \"${file}\"" vebegin "Executing \"${file}\""
eval "${file}" $redirect eval "${file}" $redirect
@@ -41,32 +38,32 @@ start()
eoutdent eoutdent
if command -v local_start >/dev/null 2>&1; then if command -v local_start >/dev/null 2>&1; then
ewarn "\"${conf_d_dir}/local\" should be removed." ewarn "\"@SYSCONFDIR@/conf.d/local\" should be removed."
ewarn "Please move the code from the local_start function" ewarn "Please move the code from the local_start function"
ewarn "to executable scripts with an .start extension" ewarn "to executable scripts with an .start extension"
ewarn "in \"${local_d_dir}\"" ewarn "in \"@SYSCONFDIR@/local.d\""
local_start local_start
fi fi
eend ${has_errors} eend ${has_errors}
# We have to end with a zero exit code, because a failed execution # We have to end with a zero exit code, because a failed execution
# of an executable ${local_d_dir}/*.start file shouldn't result in # of an executable @SYSCONFDIR@/local.d/*.start file shouldn't result in
# marking the local service as failed. Otherwise we are unable to # marking the local service as failed. Otherwise we are unable to
# execute any executable ${local_d_dir}/*.stop file, because a failed # execute any executable @SYSCONFDIR@/local.d/*.stop file, because a failed
# marked service cannot be stopped (and the stop function would # marked service cannot be stopped (and the stop function would
# actually call the executable ${local_d_dir}/*.stop file(s)). # actually call the executable @SYSCONFDIR@/local.d/*.stop file(s)).
return 0 return 0
} }
stop() stop()
{ {
local file has_errors redirect retval
has_errors=0
yesno $rc_verbose || redirect='> /dev/null 2>&1'
ebegin "Stopping local" ebegin "Stopping local"
local file has_errors=0 redirect retval
yesno $rc_verbose || redirect='> /dev/null 2>&1'
eindent eindent
for file in "${local_d_dir}"/*.stop; do for file in @SYSCONFDIR@/local.d/*.stop; do
if [ -x "${file}" ]; then if [ -x "${file}" ]; then
vebegin "Executing \"${file}\"" vebegin "Executing \"${file}\""
eval "${file}" $redirect eval "${file}" $redirect
@@ -80,16 +77,16 @@ stop()
eoutdent eoutdent
if command -v local_stop >/dev/null 2>&1; then if command -v local_stop >/dev/null 2>&1; then
ewarn "\"${conf_d_dir}/local\" should be removed." ewarn "\"@SYSCONFDIR@/conf.d/local\" should be removed."
ewarn "Please move the code from the local_stop function" ewarn "Please move the code from the local_stop function"
ewarn "to executable scripts with an .stop extension" ewarn "to executable scripts with an .stop extension"
ewarn "in \"${local_d_dir}\"" ewarn "in \"@SYSCONFDIR@/local.d\""
local_stop local_stop
fi fi
eend ${has_errors} eend ${has_errors}
# An executable ${local_d_dir}/*.stop file which failed with a # An executable @SYSCONFDIR@/local.d/*.stop file which failed with a
# non-zero exit status is not a reason to mark this service # non-zero exit status is not a reason to mark this service
# as failed, therefore we have to end with a zero exit code. # as failed, therefore we have to end with a zero exit code.
return 0 return 0

View File

@@ -14,7 +14,7 @@ description="Mounts disks and swap according to /etc/fstab."
depend() depend()
{ {
need fsck need fsck
use lvm modules root use lvm modules mtab root
after clock lvm modules root after clock lvm modules root
keyword -docker -jail -lxc -prefix -systemd-nspawn -vserver keyword -docker -jail -lxc -prefix -systemd-nspawn -vserver
} }

72
init.d/modules-load.in Normal file
View File

@@ -0,0 +1,72 @@
#!@SBINDIR@/openrc-run
# 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/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.
description="Loads a list of modules from systemd-compatible locations."
depend()
{
keyword -docker -lxc -openvz -prefix -systemd-nspawn -vserver
}
find_modfiles()
{
local dirs="/usr/lib/modules-load.d /run/modules-load.d /etc/modules-load.d"
local basenames files fn x y
for x in $dirs; do
[ ! -d $x ] && continue
for y in $x/*.conf; do
[ -f $y ] && basenames="${basenames}\n${y##*/}"
done
done
basenames=$(printf "$basenames" | sort -u)
for x in $basenames; do
for y in $dirs; do
[ -r $y/$x ] &&
fn=$y/$x
done
files="$files $fn"
done
echo $files
}
load_modules()
{
local file m modules rc x
file=$1
[ -z "$file" ] && return 0
while read m x; do
case $m in
\;*) continue ;;
\#*) continue ;;
*) modules="$modules $m"
;;
esac
done < $file
for x in $modules; do
ebegin "Loading module $x"
case "$RC_UNAME" in
FreeBSD) kldload "$x"; rc=$? ;;
Linux) modprobe --use-blacklist -q "$x"; rc=$? ;;
*) ;;
esac
eend $rc "Failed to load $x"
done
}
start()
{
local x
files=$(find_modfiles)
for x in $files; do
load_modules $x
done
return 0
}

View File

@@ -14,65 +14,10 @@ description="Loads a user defined list of kernel modules."
depend() depend()
{ {
use isapnp use isapnp
provide modules-load want modules-load
keyword -docker -lxc -openvz -prefix -systemd-nspawn -vserver keyword -docker -lxc -openvz -prefix -systemd-nspawn -vserver
} }
find_modfiles()
{
local dirs="/usr/lib/modules-load.d /run/modules-load.d /etc/modules-load.d"
local basenames files fn x y
for x in $dirs; do
[ ! -d $x ] && continue
for y in $x/*.conf; do
[ -f $y ] && basenames="${basenames}\n${y##*/}"
done
done
basenames=$(printf "$basenames" | sort -u)
for x in $basenames; do
for y in $dirs; do
[ -r $y/$x ] &&
fn=$y/$x
done
files="$files $fn"
done
echo $files
}
load_modules()
{
local file m modules rc x
file=$1
[ -z "$file" ] && return 0
while read m x; do
case $m in
\;*) continue ;;
\#*) continue ;;
*) modules="$modules $m"
;;
esac
done < $file
for x in $modules; do
ebegin "Loading module $x"
case "$RC_UNAME" in
FreeBSD) kldload "$x"; rc=$? ;;
Linux) modprobe --use-blacklist -q "$x"; rc=$? ;;
*) ;;
esac
eend $rc "Failed to load $x"
done
}
modules_load_d()
{
local x
files=$(find_modfiles)
for x in $files; do
load_modules $x
done
return 0
}
FreeBSD_modules() FreeBSD_modules()
{ {
local cnt=0 x local cnt=0 x
@@ -137,10 +82,7 @@ Linux_modules()
start() start()
{ {
case "$RC_UNAME" in case "$RC_UNAME" in
FreeBSD|Linux) FreeBSD|Linux) ${RC_UNAME}_modules ;;
modules_load_d
${RC_UNAME}_modules
;;
*) ;; *) ;;
esac esac
return 0 return 0

View File

@@ -14,7 +14,6 @@ description="Update /etc/mtab to match what the kernel knows about"
depend() depend()
{ {
after clock after clock
before localmount
need root need root
keyword -prefix -systemd-nspawn keyword -prefix -systemd-nspawn
} }
@@ -31,10 +30,6 @@ start()
einfo "Creating mtab symbolic link" einfo "Creating mtab symbolic link"
ln -snf /proc/self/mounts /etc/mtab ln -snf /proc/self/mounts /etc/mtab
else else
ewarn "The ${RC_SVCNAME} service will be removed in the future."
ewarn "Please change the mtab_is_file setting to no and run"
ewarn "# rc-service mtab restart"
ewarn "to create the mtab symbolic link."
[ -L /etc/mtab ] && ewarn "Removing /etc/mtab symbolic link" [ -L /etc/mtab ] && ewarn "Removing /etc/mtab symbolic link"
rm -f /etc/mtab rm -f /etc/mtab
einfo "Creating mtab file" einfo "Creating mtab file"

View File

@@ -13,7 +13,7 @@ description="Delays until the network is online or a specific timeout"
depend() depend()
{ {
after modules net after modules
need sysfs need sysfs
provide network-online provide network-online
keyword -docker -jail -lxc -openvz -prefix -systemd-nspawn -uml -vserver keyword -docker -jail -lxc -openvz -prefix -systemd-nspawn -uml -vserver
@@ -23,7 +23,6 @@ get_interfaces()
{ {
local ifname iftype local ifname iftype
for ifname in /sys/class/net/*; do for ifname in /sys/class/net/*; do
[ -h "${ifname}" ] && continue
read iftype < ${ifname}/type read iftype < ${ifname}/type
[ "$iftype" = "1" ] && printf "%s " ${ifname##*/} [ "$iftype" = "1" ] && printf "%s " ${ifname##*/}
done done
@@ -32,7 +31,7 @@ get_interfaces()
start () start ()
{ {
local carriers configured dev gateway ifcount infinite local carriers configured dev gateway ifcount infinite
local carrier operstate rc local rc state x
ebegin "Checking to see if the network is online" ebegin "Checking to see if the network is online"
rc=0 rc=0
@@ -45,12 +44,10 @@ start ()
ifcount=0 ifcount=0
for dev in ${interfaces}; do for dev in ${interfaces}; do
: $((ifcount += 1)) : $((ifcount += 1))
read carrier < /sys/class/net/$dev/carrier 2> /dev/null || read x < /sys/class/net/$dev/carrier
carrier= [ $x -eq 1 ] && : $((carriers += 1))
[ "$carrier" = 1 ] && : $((carriers += 1)) read x < /sys/class/net/$dev/operstate
read operstate < /sys/class/net/$dev/operstate 2> /dev/null || [ "$x" = up ] && : $((configured += 1))
operstate=
[ "$operstate" = up ] && : $((configured += 1))
done done
[ $configured -eq $ifcount ] && [ $carriers -ge 1 ] && break [ $configured -eq $ifcount ] && [ $carriers -ge 1 ] && break
sleep 1 sleep 1
@@ -64,12 +61,8 @@ start ()
if [ $rc -eq 0 ] && yesno ${include_ping_test:-no}; then if [ $rc -eq 0 ] && yesno ${include_ping_test:-no}; then
ping_test_host="${ping_test_host:-google.com}" ping_test_host="${ping_test_host:-google.com}"
if [ -n "$ping_test_host" ]; then if [ -n "$ping_test_host" ]; then
while $infinite || [ $timeout -gt 0 ]; do ping -c 1 $ping_test_host > /dev/null 2>&1
ping -c 1 $ping_test_host > /dev/null 2>&1 rc=$?
rc=$?
[ $rc -eq 0 ] && break
: $((timeout -= 1))
done
fi fi
fi fi
eend $rc "The network is offline" eend $rc "The network is offline"

View File

@@ -76,7 +76,7 @@ tentative()
case "$RC_UNAME" in case "$RC_UNAME" in
Linux) Linux)
[ -n "$(command -v ip)" ] || return 1 [ -x /sbin/ip ] || [ -x /bin/ip ] || return 1
[ -n "$(ip -f inet6 addr show tentative)" ] [ -n "$(ip -f inet6 addr show tentative)" ]
;; ;;
*) *)
@@ -174,7 +174,7 @@ runip()
routeflush() routeflush()
{ {
if [ "$RC_UNAME" = Linux ]; then if [ "$RC_UNAME" = Linux ]; then
if [ -n "$(command -v ip)" ]; then if [ -x /sbin/ip ] || [ -x /bin/ip ]; then
ip route flush scope global ip route flush scope global
ip route delete default 2>/dev/null ip route delete default 2>/dev/null
else else
@@ -346,7 +346,7 @@ stop()
then then
veinfo "$int" veinfo "$int"
runargs /etc/ifdown."$int" "$downcmd" runargs /etc/ifdown."$int" "$downcmd"
if [ -n "$(command -v ip)" ]; then if [ -x /sbin/ip ] || [ -x /bin/ip ]; then
# We need to do this, otherwise we may # We need to do this, otherwise we may
# fail to add things correctly on restart # fail to add things correctly on restart
ip address flush dev "$int" 2>/dev/null ip address flush dev "$int" 2>/dev/null

View File

@@ -15,6 +15,7 @@ ttyn=${rc_tty_number:-${RC_TTY_NUMBER:-12}}
depend() depend()
{ {
need localmount
keyword -docker -lxc -openvz -prefix -systemd-nspawn -vserver keyword -docker -lxc -openvz -prefix -systemd-nspawn -vserver
} }

View File

@@ -16,6 +16,7 @@ depend()
after clock after clock
use devfs use devfs
want modules want modules
need localmount
keyword -docker -lxc -openvz -prefix -systemd-nspawn -vserver keyword -docker -lxc -openvz -prefix -systemd-nspawn -vserver
} }
@@ -26,8 +27,8 @@ start()
[ ! -e /proc/sys/fs/binfmt_misc/register ]; then [ ! -e /proc/sys/fs/binfmt_misc/register ]; then
if ! grep -qs binfmt_misc /proc/filesystems && if ! grep -qs binfmt_misc /proc/filesystems &&
modprobe -q binfmt-misc; then modprobe -q binfmt-misc; then
ewarn "The binfmt-misc module needs to be loaded by" \ ewarn "The binfmt-misc module needs to be configured in" \
"the modules service or built in." "@SYSCONFDIR@/conf.d/modules or built in."
fi fi
if grep -qs binfmt_misc /proc/filesystems; then if grep -qs binfmt_misc /proc/filesystems; then
ebegin "Mounting misc binary format filesystem" ebegin "Mounting misc binary format filesystem"

View File

@@ -49,7 +49,7 @@ start()
fi fi
ebegin "Saving dependency cache" ebegin "Saving dependency cache"
local rc=0 save= local rc=0 save=
for x in depconfig deptree rc.log shutdowntime softlevel; do for x in shutdowntime softlevel rc.log; do
[ -e "$RC_SVCDIR/$x" ] && save="$save $RC_SVCDIR/$x" [ -e "$RC_SVCDIR/$x" ] && save="$save $RC_SVCDIR/$x"
done done
if [ -n "$save" ]; then if [ -n "$save" ]; then

View File

@@ -1,4 +1,4 @@
#!@SBINDIR@/openrc-run S#!@BINDIR@/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

@@ -11,7 +11,7 @@
depend() depend()
{ {
after clock root after clock
before localmount before localmount
keyword -docker -jail -lxc -openvz -prefix -systemd-nspawn -vserver keyword -docker -jail -lxc -openvz -prefix -systemd-nspawn -vserver
} }

View File

@@ -21,7 +21,7 @@ BSD_sysctl()
[ -e /etc/sysctl.conf ] || return 0 [ -e /etc/sysctl.conf ] || return 0
local retval=0 var= comments= conf= local retval=0 var= comments= conf=
eindent eindent
for conf in /etc/sysctl.conf /etc/sysctl.d/*.conf; do for conf in @SYSCONFDIR@/sysctl.conf @SYSCONFDIR@/sysctl.d/*.conf; do
if [ -r "$conf" ]; then if [ -r "$conf" ]; then
vebegin "applying $conf" vebegin "applying $conf"
while read var comments; do while read var comments; do

View File

@@ -101,17 +101,53 @@ mount_misc()
if [ -d /sys/firmware/efi/efivars ] && if [ -d /sys/firmware/efi/efivars ] &&
! mountinfo -q /sys/firmware/efi/efivars; then ! mountinfo -q /sys/firmware/efi/efivars; then
ebegin "Mounting efivarfs filesystem" ebegin "Mounting efivarfs filesystem"
mount -n -t efivarfs -o ro \ mount -n -t efivarfs -o ${sysfs_opts} \
efivarfs /sys/firmware/efi/efivars 2> /dev/null efivarfs /sys/firmware/efi/efivars 2> /dev/null
eend 0 eend 0
fi fi
} }
mount_cgroups()
{
# set up kernel support for cgroups
if [ -d /sys/fs/cgroup ] && ! mountinfo -q /sys/fs/cgroup; then
if grep -qs cgroup /proc/filesystems; then
ebegin "Mounting cgroup filesystem"
local opts="${sysfs_opts},mode=755,size=${rc_cgroupsize:-10m}"
mount -n -t tmpfs -o ${opts} cgroup_root /sys/fs/cgroup
eend $?
fi
fi
mountinfo -q /sys/fs/cgroup || return 0
if ! mountinfo -q /sys/fs/cgroup/openrc; then
local agent="@LIBEXECDIR@/sh/cgroup-release-agent.sh"
mkdir /sys/fs/cgroup/openrc
mount -n -t cgroup \
-o none,${sysfs_opts},name=openrc,release_agent="$agent" \
openrc /sys/fs/cgroup/openrc
printf 1 > /sys/fs/cgroup/openrc/notify_on_release
fi
yesno ${rc_controller_cgroups:-YES} && [ -e /proc/cgroups ] || return 0
while read name hier groups enabled rest; do
case "${enabled}" in
1) mountinfo -q /sys/fs/cgroup/${name} && continue
mkdir /sys/fs/cgroup/${name}
mount -n -t cgroup -o ${sysfs_opts},${name} \
${name} /sys/fs/cgroup/${name}
;;
esac
done < /proc/cgroups
}
restorecon_sys() restorecon_sys()
{ {
if [ -x /sbin/restorecon ]; then if [ -x /sbin/restorecon ]; then
ebegin "Restoring SELinux contexts in /sys" ebegin "Restoring SELinux contexts in /sys"
restorecon -F /sys/devices/system/cpu/online >/dev/null 2>&1 restorecon -F /sys/devices/system/cpu/online >/dev/null 2>&1
restorecon -rF /sys/fs/cgroup >/dev/null 2>&1
eend $? eend $?
fi fi
} }
@@ -120,6 +156,7 @@ start()
{ {
mount_sys mount_sys
mount_misc mount_misc
mount_cgroups
restorecon_sys restorecon_sys
return 0 return 0
} }

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 \
start-stop-daemon.8 supervise-daemon.8 service.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

@@ -8,7 +8,7 @@
.\" This file may not be copied, modified, propagated, or distributed .\" This file may not be copied, modified, propagated, or distributed
.\" except according to the terms contained in the LICENSE file. .\" except according to the terms contained in the LICENSE file.
.\" .\"
.Dd November 30, 2017 .Dd December 31, 2011
.Dt openrc-run 8 SMM .Dt openrc-run 8 SMM
.Os OpenRC .Os OpenRC
.Sh NAME .Sh NAME
@@ -111,17 +111,11 @@ The path to the s6 service directory if you are monitoring this service
with S6. The default is /var/svc.d/${RC_SVCNAME}. with S6. The default is /var/svc.d/${RC_SVCNAME}.
.It Ar s6_svwait_options_start .It Ar s6_svwait_options_start
The options to pass to s6-svwait when starting the service via s6. The options to pass to s6-svwait when starting the service via s6.
.It Ar s6_force_kill
Should we force-kill this service if s6_service_timeout_stop expires
but the service doesn't go down during shutdown? The default is yes.
.It Ar s6_service_timeout_stop .It Ar s6_service_timeout_stop
The amount of time, in milliseconds, s6-svc should wait for the service The amount of time, in milliseconds, s6-svc should wait for the service
to go down when stopping the service. The default is 60000. to go down when stopping the service. The default is 10000.
.It Ar start_stop_daemon_args .It Ar start_stop_daemon_args
List of arguments passed to start-stop-daemon when starting the daemon. List of arguments passed to start-stop-daemon when starting the daemon.
.It Ar supervise_daemon_args
List of arguments passed to supervise-daemon when starting the daemon.
If undefined, start_stop_daemon_args is used as a fallback.
.It Ar command .It Ar command
Daemon to start or stop via Daemon to start or stop via
.Nm start-stop-daemon .Nm start-stop-daemon
@@ -145,56 +139,21 @@ List of arguments to pass to the daemon when starting via
.Nm supervise-daemon . .Nm supervise-daemon .
to force the daemon to stay in the foreground to force the daemon to stay in the foreground
.It Ar command_background .It Ar command_background
Set this to "true", "yes" or "1" (case-insensitive) if you want Set this to "true", "yes" or "1" (case-insensitive) if you want
.Xr start-stop-daemon 8 .Xr start-stop-daemon 8
to force the daemon into the background. This forces the to force the daemon into the background. This forces the
"--make-pidfile" and "--pidfile" options, so the pidfile variable must be set. "--make-pidfile" and "--pidfile" options, so the pidfile variable must be set.
.It Ar command_progress .It Ar command_progress
Set this to "true", "yes" or "1" (case-insensitive) if you want Set this to "true", "yes" or "1" (case-insensitive) if you want
.Xr start-stop-daemon 8 .Xr start-stop-daemon 8
to display a progress meter when waiting for a daemon to stop. to display a progress meter when waiting for a daemon to stop.
.It Ar command_user .It Ar command_user
If the daemon does not support changing to a different user id, you can If the daemon does not support changing to a different user id, you can
use this to change the user id, and optionally group id, before use this to change the user id before
.Xr start-stop-daemon 8 .Xr start-stop-daemon 8
or or
.Xr supervise-daemon 8 .Xr supervise-daemon 8
launches the daemon. launches the daemon
.It Ar output_log
This is the path to a file or named pipe where the standard output from
the service will be redirected. If you are starting this service with
.Xr start-stop-daemon 8 ,
, you must set
.Pa command_background
to true. Keep in mind that this path will be inside the chroot if the
.Pa chroot
variable is set.
.It Ar error_log
The same thing as
.Pa output_log
but for the standard error output.
.It Ar output_logger
This is a process which will be used to log the standard output from the
service. If you are starting this service with
.Xr start-stop-daemon 8 ,
, you must set
.Pa command_background
to true. Keep in mind that this command must be executable as a shell
command inside the chroot if the
.Pa chroot
variable is set. Keep in mind also that this command works by accepting
the stdout of the service on stdin.
An example of a command that can be run this way is logger if you want
your service output to go to syslog.
.It Ar error_logger
The same thing as
.Pa output_logger
but for the standard error output.
.It Ar directory
.Xr start-stop-daemon 8
and
.Xr supervise-daemon 8
will chdir to this directory before starting the daemon.
.It Ar chroot .It Ar chroot
.Xr start-stop-daemon 8 .Xr start-stop-daemon 8
and and
@@ -239,8 +198,6 @@ used along with in_background_fake to support re-entrant services.
.It Ar in_background_fake .It Ar in_background_fake
Space separated list of commands which should always succeed when Space separated list of commands which should always succeed when
in_background is yes. in_background is yes.
.It Ar umask
Set the umask of the daemon.
.Pp .Pp
Keep in mind that eval is used to process chroot, command, command_args_*, Keep in mind that eval is used to process chroot, command, command_args_*,
command_user, pidfile and procname. This may affect how they are command_user, pidfile and procname. This may affect how they are
@@ -260,10 +217,8 @@ 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 attempt to start any services it needs regardless of The service will refuse to start until needed services have started and it
whether they have been added to the runlevel. It will refuse to start will refuse to stop until any services that need it have stopped.
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.
@@ -329,18 +284,6 @@ system.
To see how to influence dependencies in configuration files, see the To see how to influence dependencies in configuration files, see the
.Sx FILES .Sx FILES
section below. section below.
.Sh _pre AND _post FUNCTIONS
Any command defined in extra_commands, extra_started_commands or
extra_stopped_commands can have _pre and _post functions in the service
script. If the command function is called foo, the_pre and _post
functions for it should be called foo_pre and foo_post.
.Pp
These functions should be used to perform preparation before the
command is run and cleanup after the command completes. In order for
.Nm
to record the command as being run successfully, the _pre
function, command function itself and the _post function should all exit
with a zero return code.
.Sh BUILTINS .Sh BUILTINS
.Nm .Nm
defines some builtin functions that you can use inside your service scripts: defines some builtin functions that you can use inside your service scripts:
@@ -469,65 +412,27 @@ If -d, -f or -p is specified, checkpath checks to see if the path
exists, is the right type and has the correct owner and access modes. If exists, is the right type and has the correct owner and access modes. If
any of these tests fail, the path is created and set up as specified. If any of these tests fail, the path is created and set up as specified. If
more than one of -d, -f or -p are specified, the last one will be used. more than one of -d, -f or -p are specified, the last one will be used.
.Pp
The argument to -m is a three or four digit octal number. If this option The argument to -m is a three or four digit octal number. If this option
is not provided, the value defaults to 0644 for files and 0775 for is not provided, the value defaults to 0644 for files and 0775 for
directories. directories.
.Pp
The argument to -o is a representation of the user and/or group which The argument to -o is a representation of the user and/or group which
should own the path. The user and group can be represented numerically should own the path. The user and group can be represented numerically
or with names, and are separated by a colon. or with names, and are separated by a colon.
.Pp
The truncate options (-D and -F) cause the directory or file to be The truncate options (-D and -F) cause the directory or file to be
cleared of all contents. cleared of all contents.
.Pp
If -W is specified, checkpath checks to see if the first path given on If -W is specified, checkpath checks to see if the first path given on
the command line is writable. This is different from how the test the command line is writable. This is different from how the test
command in the shell works, because it also checks to make sure the file command in the shell works, because it also checks to make sure the file
system is not read only. system is not read only.
.Pp
Also, the -d, -f or -p options should not be specified along with this option. Also, the -d, -f or -p options should not be specified along with this option.
.Pp
The -q option suppresses all informational output. If it is specified The -q option suppresses all informational output. If it is specified
twice, all error messages are suppressed as well. twice, all error messages are suppressed as well.
.It Xo
.Ic fstabinfo
.Op Fl M , -mount
.Op Fl R , -remount
.Op Fl b , -blockdevice
.Op Fl m , -mountargs
.Op Fl o , -options
.Op Fl p , -passno Ar passno
.Op Fl t , -type Ar fstype
.Ar path
.Xc
If -b, -m, -o, -p or -t is specified,the appropriate information is
extracted from fstab. If -M or -R are given, file systems are mounted or
remounted.
.Pp
The -q option suppresses all informational output. If it is specified
twice, all error messages are suppressed as well.
.It Xo
.Ic mountinfo
.Op Fl f, -fstype-regex Ar regex
.Op Fl F, -skip-fstype-regex Ar regex
.Op Fl n, -node-regex Ar regex
.Op Fl N, -skip-node-regex Ar regex
.Op Fl o, -options-regex Ar regex
.Op Fl O, -skip-options-regex Ar regex
.Op Fl p, -point-regex Ar regex
.Op Fl P, -skip-point-regex Ar regex
.Op Fl e, -netdev
.Op Fl E, -nonetdev
.Op Fl i, -options
.Op Fl s, -fstype
.Op Fl t, -node
.Ar mount1 mount2 ...
.Xc
The f, F, n, N, o, O, p, P, e and E options specify what you want to
search for or skip in the mounted file systems. The i, s and t options
specify what you want to display. If no mount points are given, all
mount points will be considered.
.It Ic yesno Ar value .It Ic yesno Ar value
If If
.Ar value .Ar value
@@ -614,7 +519,7 @@ rc_net_tap1_provide="!net"
# It's also possible to negate keywords. This is mainly useful for prefix # It's also possible to negate keywords. This is mainly useful for prefix
# users testing OpenRC. # users testing OpenRC.
rc_keyword="!-prefix" rc_keyword="!-prefix"
# This can also be used to block a script from running in all # This can also be used to block a script from runining in all
# containers except one or two # containers except one or two
rc_keyword="!-containers !-docker" rc_keyword="!-containers !-docker"
.Ed .Ed
@@ -753,8 +658,6 @@ Users are encouraged to use the is_newer_than function which returns correctly.
.Xr rc_plugin_hook 3 , .Xr rc_plugin_hook 3 ,
.Xr sh 1p , .Xr sh 1p ,
.Xr start-stop-daemon 8 , .Xr start-stop-daemon 8 ,
.Xr supervise-daemon 8 ,
.Xr uname 1 .Xr uname 1
.Sh AUTHORS .Sh AUTHORS
.An Roy Marples <roy@marples.name> .An Roy Marples <roy@marples.name>
.An William Hubbs <w.d.hubbs@gmail.com>

View File

@@ -16,18 +16,6 @@
.Nd locate and run an OpenRC service with the given arguments .Nd locate and run an OpenRC service with the given arguments
.Sh SYNOPSIS .Sh SYNOPSIS
.Nm .Nm
.Op Fl c , -ifcrashed
.Ar service cmd
.Op Ar ...
.Nm
.Fl d , -debug
.Ar service cmd
.Op Ar ...
.Nm
.Fl D , -nodeps
.Ar service cmd
.Op Ar ...
.Nm
.Op Fl i , -ifexists .Op Fl i , -ifexists
.Ar service cmd .Ar service cmd
.Op Ar ... .Op Ar ...
@@ -40,21 +28,9 @@
.Ar service cmd .Ar service cmd
.Op Ar ... .Op Ar ...
.Nm .Nm
.Op Fl s , -ifstarted
.Ar service cmd
.Op Ar ...
.Nm
.Op Fl S , -ifstopped
.Ar service cmd
.Op Ar ...
.Nm
.Fl e , -exists .Fl e , -exists
.Ar service .Ar service
.Nm .Nm
.Fl Z , -dry-run
.Ar service cmd
.Op Ar ...
.Nm
.Fl l , -list .Fl l , -list
.Nm .Nm
.Fl r , -resolve .Fl r , -resolve
@@ -88,15 +64,6 @@ return 0 if it can find
otherwise -1. otherwise -1.
.Fl r , -resolve .Fl r , -resolve
does the same and also prints the full path of the service to stdout. does the same and also prints the full path of the service to stdout.
.Pp
.Fl d , -debug
sets -x when running the service script(s).
.Pp
.Fl D , -nodeps
ignores dependencies when running the service.
.Pp
.Fl Z , -dry-run
displays commands rather than executing them.
.Sh SEE ALSO .Sh SEE ALSO
.Xr openrc 8 , .Xr openrc 8 ,
.Xr stdout 3 .Xr stdout 3

1
man/service.8 Normal file
View File

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

View File

@@ -131,34 +131,9 @@ Modifies the scheduling priority of the daemon.
.It Fl 1 , -stdout Ar logfile .It Fl 1 , -stdout Ar logfile
Redirect the standard output of the process to logfile when started with Redirect the standard output of the process to logfile when started with
.Fl background . .Fl background .
The logfile Must be an absolute pathname, but relative to the path Must be an absolute pathname, but relative to the path optionally given with
optionally given with
.Fl r , -chroot . .Fl r , -chroot .
The logfile can also be a named pipe. The logfile can also be a named pipe.
.It Fl 2 , -stderr Ar logfile
Redirect the standard error of the process to logfile when started with
.Fl background .
The logfile must be an absolute pathname, but relative to the path
optionally given with
.Fl r , -chroot .
The logfile can also be a named pipe.
.It Fl 3 , -stdout-logger Ar cmd
Run cmd as a child process redirecting the standard output to the
standard input of cmd when started with
.Fl background .
Cmd must be an absolute pathname, but relative to the path optionally given with
.Fl r , -chroot .
This process must be prepared to accept input on stdin and be able to
log it or send it to another location.
.It Fl 4 , -stderr-logger Ar cmd
Run cmd as a child process and
Redirect the standard error of the process to the standard input of cmd
when started with
.Fl background .
Cmd must be an absolute pathname, but relative to the path optionally given with
.Fl r , -chroot .
This process must be prepared to accept input on stdin and be able to
log it or send it to another location.
.It Fl w , -wait Ar milliseconds .It Fl w , -wait Ar milliseconds
Wait Wait
.Ar milliseconds .Ar milliseconds
@@ -176,7 +151,6 @@ These options are only used for stopping daemons:
.It Fl R , -retry Ar timeout | Ar signal Ns / Ns Ar timeout .It Fl R , -retry Ar timeout | Ar signal Ns / Ns Ar timeout
The retry specification can be either a timeout in seconds or multiple The retry specification can be either a timeout in seconds or multiple
signal/timeout pairs (like SIGTERM/5). signal/timeout pairs (like SIGTERM/5).
If this option is not given, the default is SIGTERM/5.
.El .El
.Sh ENVIRONMENT .Sh ENVIRONMENT
.Va SSD_IONICELEVEL .Va SSD_IONICELEVEL

View File

@@ -36,8 +36,6 @@
.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
@@ -117,10 +115,6 @@ 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).
If this option is not given, the default is 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.
@@ -136,7 +130,6 @@ The same thing as
.Fl 1 , -stdout .Fl 1 , -stdout
but with the standard error output. but with the standard error output.
.El .El
.El
.Sh ENVIRONMENT .Sh ENVIRONMENT
.Va SSD_NICELEVEL .Va SSD_NICELEVEL
can also set the scheduling priority of the daemon, but the command line can also set the scheduling priority of the daemon, but the command line

View File

@@ -11,5 +11,3 @@
# Generic definitions # Generic definitions
include ${MK}/os-BSD.mk include ${MK}/os-BSD.mk
CPPFLAGS+= -D_BSD_SOURCE

View File

@@ -13,6 +13,6 @@
SFX= .GNU-kFreeBSD.in SFX= .GNU-kFreeBSD.in
PKG_PREFIX?= /usr PKG_PREFIX?= /usr
CPPFLAGS+= -D_BSD_SOURCE CPPFLAGS+= -D_BSD_SOURCE -D_XOPEN_SOURCE=700
LIBDL= -Wl,-Bdynamic -ldl LIBDL= -Wl,-Bdynamic -ldl
LIBKVM?= LIBKVM?=

View File

@@ -11,5 +11,5 @@
SFX= .GNU.in SFX= .GNU.in
PKG_PREFIX?= /usr PKG_PREFIX?= /usr
CPPFLAGS+= -D_DEFAULT_SOURCE -DMAXPATHLEN=4096 -DPATH_MAX=4096 CPPFLAGS+= -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=700 -DMAXPATHLEN=4096 -DPATH_MAX=4096
LIBDL= -Wl,-Bdynamic -ldl LIBDL= -Wl,-Bdynamic -ldl

View File

@@ -11,7 +11,7 @@
SFX= .Linux.in SFX= .Linux.in
PKG_PREFIX?= /usr PKG_PREFIX?= /usr
CPPFLAGS+= -D_DEFAULT_SOURCE -D_POSIX_C_SOURCE=200809L CPPFLAGS+= -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=700
LIBDL= -Wl,-Bdynamic -ldl LIBDL= -Wl,-Bdynamic -ldl
ifeq (${MKSELINUX},yes) ifeq (${MKSELINUX},yes)

View File

@@ -34,7 +34,6 @@ PICFLAG?= -fPIC
SYSCONFDIR?= ${PREFIX}/etc SYSCONFDIR?= ${PREFIX}/etc
INITDIR?= ${SYSCONFDIR}/init.d INITDIR?= ${SYSCONFDIR}/init.d
CONFDIR?= ${SYSCONFDIR}/conf.d CONFDIR?= ${SYSCONFDIR}/conf.d
CONFMODE?= 0644
LOCALDIR?= ${SYSCONFDIR}/local.d LOCALDIR?= ${SYSCONFDIR}/local.d
SYSCTLDIR?= ${SYSCONFDIR}/sysctl.d SYSCTLDIR?= ${SYSCONFDIR}/sysctl.d
@@ -45,22 +44,20 @@ SBINDIR?= ${PREFIX}/sbin
SBINMODE?= 0755 SBINMODE?= 0755
INCDIR?= ${UPREFIX}/include INCDIR?= ${UPREFIX}/include
INCMODE?= 0644 INCMODE?= 0444
_LIBNAME_SH= case `readlink /lib` in /lib64|lib64) echo "lib64";; *) echo "lib";; esac _LIBNAME_SH= case `readlink /lib` in /lib64|lib64) echo "lib64";; *) echo "lib";; esac
_LIBNAME:= $(shell ${_LIBNAME_SH}) _LIBNAME:= $(shell ${_LIBNAME_SH})
LIBNAME?= ${_LIBNAME} LIBNAME?= ${_LIBNAME}
LIBDIR?= ${UPREFIX}/${LIBNAME} LIBDIR?= ${UPREFIX}/${LIBNAME}
LIBMODE?= 0644 LIBMODE?= 0444
SHLIBDIR?= ${PREFIX}/${LIBNAME} SHLIBDIR?= ${PREFIX}/${LIBNAME}
LIBEXECDIR?= ${PREFIX}/libexec/rc LIBEXECDIR?= ${PREFIX}/libexec/rc
MANPREFIX?= ${UPREFIX}/share MANPREFIX?= ${UPREFIX}/share
MANDIR?= ${MANPREFIX}/man MANDIR?= ${MANPREFIX}/man
MANMODE?= 0644 MANMODE?= 0444
BASHCOMPDIR?= ${UPREFIX}/share/bash-completion/completions
DATADIR?= ${UPREFIX}/share/openrc DATADIR?= ${UPREFIX}/share/openrc
DATAMODE?= 0644 DATAMODE?= 0644
@@ -68,4 +65,4 @@ DATAMODE?= 0644
DOCDIR?= ${UPREFIX}/share/doc DOCDIR?= ${UPREFIX}/share/doc
DOCMODE?= 0644 DOCMODE?= 0644
ZSHCOMPDIR?= ${UPREFIX}/share/zsh/site-functions CONFMODE?= 0644

View File

@@ -1,5 +1,4 @@
Using runit with OpenRC # Using runit with OpenRC
=======================
Beginning with OpenRC-0.21, we support using runit [1] in place of Beginning with OpenRC-0.21, we support using runit [1] in place of
start-stop-daemon for monitoring and restarting daemons. start-stop-daemon for monitoring and restarting daemons.

View File

@@ -38,7 +38,7 @@ BOOT-FreeBSD+= adjkerntz dumpon syscons
BOOT-Linux+= binfmt hwclock keymaps modules mtab procfs termencoding BOOT-Linux+= binfmt hwclock keymaps modules mtab procfs termencoding
SHUTDOWN-Linux= killprocs mount-ro SHUTDOWN-Linux= killprocs mount-ro
SYSINIT-Linux= devfs cgroups dmesg sysfs SYSINIT-Linux= devfs dmesg sysfs
# Generic BSD stuff # Generic BSD stuff
BOOT-NetBSD+= hostid newsyslog savecore syslogd BOOT-NetBSD+= hostid newsyslog savecore syslogd

View File

@@ -1,5 +1,4 @@
Using S6 with OpenRC # Using S6 with OpenRC
====================
Beginning with OpenRC-0.16, we support using the s6 supervision suite Beginning with OpenRC-0.16, we support using the s6 supervision suite
from Skarnet Software in place of start-stop-daemon for monitoring from Skarnet Software in place of start-stop-daemon for monitoring
@@ -40,10 +39,6 @@ s6_service_path - the path to the s6 service directory. The default is
s6_svwait_options_start - the options to pass to s6-svwait when starting s6_svwait_options_start - the options to pass to s6-svwait when starting
the service. If this is not set, s6-svwait will not be called. the service. If this is not set, s6-svwait will not be called.
s6_force_kill - Should we try to force kill this service if the
s6_service_timeout_stop timeout expires when shutting down this service?
The default is yes.
s6_service_timeout_stop - the amount of time, in milliseconds, s6-svc s6_service_timeout_stop - the amount of time, in milliseconds, s6-svc
should wait for a service to go down when stopping. should wait for a service to go down when stopping.

View File

@@ -1,24 +1,3 @@
#!@SHELL@ #!@SHELL@
option_arg= exec @SBINDIR@/openrc-shutdown --halt "$@"
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
exec @SBINDIR@/openrc-shutdown ${option_arg} ${poweroff_arg} "$@"

View File

@@ -1,23 +1,3 @@
#!@SHELL@ #!@SHELL@
option_arg= exec @SBINDIR@/openrc-shutdown --poweroff "$@"
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
exec @SBINDIR@/openrc-shutdown ${option_arg} ${poweroff_arg} "$@"

View File

@@ -1,25 +1,3 @@
#!@SHELL@ #!@SHELL@
option_arg= exec @SBINDIR@/openrc-shutdown --reboot "$@"
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
exec @SBINDIR@/openrc-shutdown ${option_arg} ${poweroff_arg} "$@"

View File

@@ -1,29 +1,21 @@
#!@SHELL@ #!@SHELL@
shutdown_arg= poweroff_arg=
while getopts :akrhPHfFnct: opt; do while getopts :HhPr opt; do
case "$opt" in case "$opt" in
a) ;; h|P) poweroff_arg=--poweroff ;;
k) ;; H) poweroff_arg=--halt ;;
r) shutdown_arg=--reboot ;; r) poweroff_arg=--reboot ;;
h) shutdown_arg=--halt ;; \?) printf "${0##*/}: invalid option ${optarg}\n" >&2
P) shutdown_arg=--poweroff ;;
H) shutdown_arg=--halt ;;
f) ;;
F) ;;
n) ;;
c) ;;
t) ;;
[?]) printf "%s\n" "${0##*/}: invalid command line option" >&2
exit 1 exit 1
;; ;;
esac esac
done done
shift $((OPTIND-1)) shift $((OPTIND-1))
if [ -z "${shutdown_arg}" ]; then if [ -z "${poweroff_arg}" ]; then
shutdown_arg=--single poweroff_arg=--single
fi fi
echo @SBINDIR@/openrc-shutdown ${shutdown_arg} "$@" echo @SBINDIR@/openrc-shutdown ${poweroff_arg} ${@}
exec @SBINDIR@/openrc-shutdown ${shutdown_arg} "$@" exec @SBINDIR@/openrc-shutdown ${poweroff_arg} ${@}

View File

@@ -1,495 +0,0 @@
OpenRC Service Script Writing Guide
===================================
This document is aimed at developers or packagers who
write OpenRC service scripts, either for their own projects, or for
the packages they maintain. It contains advice, suggestions, tips,
tricks, hints, and counsel; cautions, warnings, heads-ups,
admonitions, proscriptions, enjoinders, and reprimands.
It is intended to prevent common mistakes that are found "in the wild"
by pointing out those mistakes and suggesting alternatives. Each
good/bad thing that you should/not do has a section devoted to it. We
don't consider anything exotic, and assume that you will use
start-stop-daemon to manage a fairly typical long-running UNIX
process.
# Syntax of Service Scripts
Service scripts are shell scripts. OpenRC aims at using only the standardized
POSIX sh subset for portability reasons. The default interpreter (build-time
toggle) is `/bin/sh`, so using for example mksh is not a problem.
OpenRC has been tested with busybox sh, ash, dash, bash, mksh, zsh and possibly
others. Using busybox sh has been difficult as it replaces commands with
builtins that don't offer the expected features.
The interpreter for service scripts is `#!/sbin/openrc-run`.
Not using this interpreter will break the use of dependencies and is not
supported. (iow: if you insist on using `#!/bin/sh` you're on your own)
A `depend` function declares the dependencies of this service script.
All scripts must have start/stop/status functions, but defaults are
provided and should be used unless you have a very strong reason not to
use them.
Extra functions can be added easily:
```
extra_commands="checkconfig"
checkconfig() {
doSomething
}
```
This exports the checkconfig function so that `/etc/init.d/someservice
checkconfig` will be available, and it "just" runs this function.
While commands defined in `extra_commands` are always available, commands
defined in `extra_started_commands` will only work when the service is started
and those defined in `extra_stopped_commands` will only work when the service is
stopped. This can be used for implementing graceful reload and similar
behaviour.
Adding a restart function will not work, this is a design decision within
OpenRC. Since there may be dependencies involved (e.g. network -> apache) a
restart function is in general not going to work.
restart is internally mapped to `stop()` + `start()` (plus handling dependencies).
If a service needs to behave differently when it is being restarted vs
started or stopped, it should test the `$RC_CMD` variable, for example:
```
[ "$RC_CMD" = restart ] && do_something
```
# The Depend Function
This function declares the dependencies for a service script. This
determines the order the service scripts start.
```
depend() {
need net
use dns logger netmount
want coolservice
}
```
`need` declares a hard dependency - net always needs to be started before this
service does
`use` is a soft dependency - if dns, logger or netmount is in this runlevel
start it before, but we don't care if it's not in this runlevel.
`want` is between need and use - try to start coolservice if it is
installed on the system, regardless of whether it is in the
runlevel, but we don't care if it starts.
`before` declares that we need to be started before another service
`after` declares that we need to be started after another service, without
creating a dependency (so on calling stop the two are independent)
`provide` allows multiple implementations to provide one service type, e.g.:
`provide cron` is set in all cron-daemons, so any one of them started
satisfies a cron dependency
`keyword` allows platform-specific overrides, e.g. `keyword -lxc` makes this
service script a noop in lxc containers. Useful for things like keymaps,
module loading etc. that are either platform-specific or not available
in containers/virtualization/...
FIXME: Anything missing in this list?
# The Default Functions
All service scripts are assumed to have the following functions:
```
start()
stop()
status()
```
There are default implementations in `lib/rc/sh/openrc-run.sh` - this allows very
compact service scripts. These functions can be overridden per service script as
needed.
The default functions assume the following variables to be set in the service
script:
```
command=
command_args=
pidfile=
```
Thus the 'smallest' service scripts can be half a dozen lines long
## Don't write your own start/stop functions
OpenRC is capable of stopping and starting most daemons based on the
information that you give it. For a well-behaved daemon that
backgrounds itself and writes its own PID file by default, the
following OpenRC variables are likely all that you'll need:
* command
* command_args
* pidfile
Given those three pieces of information, OpenRC will be able to start
and stop the daemon on its own. The following is taken from an
[OpenNTPD](http://www.openntpd.org/) service script:
```sh
command="/usr/sbin/ntpd"
# The special RC_SVCNAME variable contains the name of this service.
pidfile="/run/${RC_SVCNAME}.pid"
command_args="-p ${pidfile}"
```
If the daemon runs in the foreground by default but has options to
background itself and to create a pidfile, then you'll also need
* command_args_background
That variable should contain the flags needed to background your
daemon, and to make it write a PID file. Take for example the
following snippet of an
[NRPE](https://github.com/NagiosEnterprises/nrpe) service script:
```sh
command="/usr/bin/nrpe"
command_args="--config=/etc/nagios/nrpe.cfg"
command_args_background="--daemon"
pidfile="/run/${RC_SVCNAME}.pid"
```
Since NRPE runs as *root* by default, it needs no special permissions
to write to `/run/nrpe.pid`. OpenRC takes care of starting and
stopping the daemon with the appropriate arguments, even passing the
`--daemon` flag during startup to force NRPE into the background (NRPE
knows how to write its own PID file).
But what if the daemon isn't so well behaved? What if it doesn't know
how to background itself or create a pidfile? If it can do neither,
then use,
* command_background=true
which will additionally pass `--make-pidfile` to start-stop-daemon,
causing it to create the `$pidfile` for you (rather than the daemon
itself being responsible for creating the PID file).
If your daemon doesn't know how to change its own user or group, then
you can tell start-stop-daemon to launch it as an unprivileged user
with
* command_user="user:group"
Finally, if your daemon always forks into the background but fails to
create a PID file, then your only option is to use
* procname
With `procname`, OpenRC will try to find the running daemon by
matching the name of its process. That's not so reliable, but daemons
shouldn't background themselves without creating a PID file in the
first place. The next example is part of the [CA NetConsole
Daemon](https://oss.oracle.com/projects/cancd/) service script:
```sh
command="/usr/sbin/cancd"
command_args="-p ${CANCD_PORT}
-l ${CANCD_LOG_DIR}
-o ${CANCD_LOG_FORMAT}"
command_user="cancd"
# cancd daemonizes itself, but doesn't write a PID file and doesn't
# have an option to run in the foreground. So, the best we can do
# is try to match the process name when stopping it.
procname="cancd"
```
To recap, in order of preference:
1. If the daemon backgrounds itself and creates its own PID file, use
`pidfile`.
2. If the daemon does not background itself (or has an option to run
in the foreground) and does not create a PID file, then use
`command_background=true` and `pidfile`.
3. If the daemon backgrounds itself and does not create a PID file,
use `procname` instead of `pidfile`. But, if your daemon has the
option to run in the foreground, then you should do that instead
(that would be the case in the previous item).
4. The last case, where the daemon does not background itself but
does create a PID file, doesn't make much sense. If there's a way
to disable the daemon's PID file (or, to write it straight into the
garbage), then do that, and use `command_background=true`.
## Reloading your daemon's configuration
Many daemons will reload their configuration files in response to a
signal. Suppose your daemon will reload its configuration in response
to a `SIGHUP`. It's possible to add a new "reload" command to your
service script that performs this action. First, tell the service
script about the new command.
```sh
extra_started_commands="reload"
```
We use `extra_started_commands` as opposed to `extra_commands` because
the "reload" action is only valid while the daemon is running (that
is, started). Now, start-stop-daemon can be used to send the signal to
the appropriate process (assuming you've defined the `pidfile`
variable elsewhere):
```sh
reload() {
ebegin "Reloading ${RC_SVCNAME}"
start-stop-daemon --signal HUP --pidfile "${pidfile}"
eend $?
}
```
## Don't restart/reload with a broken config
Often, users will start a daemon, make some configuration change, and
then attempt to restart the daemon. If the recent configuration change
contains a mistake, the result will be that the daemon is stopped but
then cannot be started again (due to the configuration error). It's
possible to prevent that situation with a function that checks for
configuration errors, and a combination of the `start_pre` and
`stop_pre` hooks.
```sh
checkconfig() {
# However you want to check this...
}
start_pre() {
# If this isn't a restart, make sure that the user's config isn't
# busted before we try to start the daemon (this will produce
# better error messages than if we just try to start it blindly).
#
# If, on the other hand, this *is* a restart, then the stop_pre
# action will have ensured that the config is usable and we don't
# need to do that again.
if [ "${RC_CMD}" != "restart" ] ; then
checkconfig || return $?
fi
}
stop_pre() {
# If this is a restart, check to make sure the user's config
# isn't busted before we stop the running daemon.
if [ "${RC_CMD}" = "restart" ] ; then
checkconfig || return $?
fi
}
```
To prevent a *reload* with a broken config, keep it simple:
```sh
reload() {
checkconfig || return $?
ebegin "Reloading ${RC_SVCNAME}"
start-stop-daemon --signal HUP --pidfile "${pidfile}"
eend $?
}
```
## PID files should be writable only by root
PID files must be writable only by *root*, which means additionally
that they must live in a *root*-owned directory. This directory is
normally /run under Linux and /var/run under other operating systems.
Some daemons run as an unprivileged user account, and create their PID
files (as the unprivileged user) in a path like
`/var/run/foo/foo.pid`. That can usually be exploited by the unprivileged
user to kill *root* processes, since when a service is stopped, *root*
usually sends a SIGTERM to the contents of the PID file (which are
controlled by the unprivileged user). The main warning sign for that
problem is using `checkpath` to set ownership on the directory
containing the PID file. For example,
```sh
# BAD BAD BAD BAD BAD BAD BAD BAD
start_pre() {
# Ensure that the pidfile directory is writable by the foo user/group.
checkpath --directory --mode 0700 --owner foo:foo "/var/run/foo"
}
# BAD BAD BAD BAD BAD BAD BAD BAD
```
If the *foo* user owns `/var/run/foo`, then he can put whatever he wants
in the `/var/run/foo/foo.pid` file. Even if *root* owns the PID file, the
*foo* user can delete it and replace it with his own. To avoid
security concerns, the PID file must be created as *root* and live in
a *root*-owned directory. If your daemon is responsible for forking
and writing its own PID file but the PID file is still owned by the
unprivileged runtime user, then you may have an upstream issue.
Once the PID file is being created as *root* (before dropping
privileges), it can be written directly to a *root*-owned
directory. For example, the *foo* daemon might write
`/var/run/foo.pid`. No calls to checkpath are needed. Note: there is
nothing technically wrong with using a directory structure like
`/var/run/foo/foo.pid`, so long as *root* owns the PID file and the
directory containing it.
Ideally (see "Upstream your service scripts"), your service script
will be integrated upstream and the build system will determine the
appropriate directory for the pid file. For example,
```sh
pidfile="@piddir@/${RC_SVCNAME}.pid"
```
A decent example of this is the [Nagios core service
script](https://github.com/NagiosEnterprises/nagioscore/blob/master/openrc-init.in),
where the full path to the PID file is specified at build-time.
## Don't let the user control the PID file location
It's usually a mistake to let the end user control the PID file
location through a conf.d variable, for a few reasons:
1. When the PID file path is controlled by the user, you need to
ensure that its parent directory exists and is writable. This
adds unnecessary code to the service script.
2. If the PID file path changes while the service is running, then
you'll find yourself unable to stop the service.
3. The directory that should contain the PID file is best determined
by the upstream build system (see "Upstream your service scripts").
On Linux, the preferred location these days is `/run`. Other systems
still use `/var/run`, though, and a `./configure` script is the
best place to decide which one you want.
4. Nobody cares where the PID file is located, anyway.
Since OpenRC service names must be unique, a value of
```sh
pidfile="/var/run/${RC_SVCNAME}.pid"
```
guarantees that your PID file has a unique name.
## Upstream your service scripts (for packagers)
The ideal place for an OpenRC service script is **upstream**. Much like
systemd services, a well-crafted OpenRC service script should be
distribution-agnostic, and the best place for it is upstream. Why? For
two reasons. First, having it upstream means that there's a single
authoritative source for improvements. Second, a few paths in every
service script are dependent upon flags passed to the build system. For
example,
```sh
command=/usr/bin/foo
```
in an autotools-based build system should really be
```sh
command=@bindir@/foo
```
so that the user's value of `--bindir` is respected. If you keep the
service script in your own distribution's repository, then you have to
keep the command path and package synchronized yourself, and that's no
fun.
## Be wary of "need net" dependencies
There are two things you need to know about "need net" dependencies:
1. They are not satisfied by the loopback interface, so "need net"
requires some *other* interface to be up.
2. Depending on the value of `rc_depend_strict` in `rc.conf`, the
"need net" will be satisfied when either *any* non-loopback
interface is up, or when *all* non-loopback interfaces are up.
The first item means that "need net" is wrong for daemons that are
happy with `0.0.0.0`, and the second point means that "need net" is
wrong for daemons that need a particular (for example, the WAN)
interface. We'll consider the two most common users of "need net";
network clients who access some network resource, and network servers
who provide them.
### Network clients
Network clients typically want the WAN interface to be up. That may
tempt you to depend on the WAN interface; but first, you should ask
yourself a question: does anything bad happen if the WAN interface is
not available? In other words, if the administrator wants to disable
the WAN, should the service be stopped? Usually the answer to that
question is "no," and in that case, you should forego the "net"
dependency entirely.
Suppose, for example, that your service retrieves virus signature
updates from the internet. In order to do its job correctly, it needs
a (working) internet connection. However, the service itself does not
require the WAN interface to be up: if it is, great; otherwise, the
worst that will happen is that a "server unavailable" warning will be
logged. The signature update service will not crash, and—perhaps more
importantly—you don't want it to terminate if the administrator turns
off the WAN interface for a second.
### Network servers
Network servers are generally easier to handle than their client
counterparts. Most server daemons listen on `0.0.0.0` (all addresses)
by default, and are therefore satisfied to have the loopback interface
present and operational. OpenRC ships with the loopback service in the
*boot* runlevel, and therefore most server daemons require no further
network dependencies.
The exceptions to this rule are those daemons who produce negative
side-effects when the WAN is unavailable. For example, the Nagios
server daemon will generate "the sky is falling" alerts for as long as
your monitored hosts are unreachable. So in that case, you should
require some other interface (often the WAN) to be up. A "need"
dependency would be appropriate, because you want Nagios to be
stopped before the network is taken down.
If your daemon can optionally be configured to listen on a particular
interface, then please see the "Depending on a particular interface"
section.
### Depending on a particular interface
If you need to depend on one particular interface, usually it's not
easy to determine programmatically what that interface is. For
example, if your *sshd* daemon listens on `192.168.1.100` (rather than
`0.0.0.0`), then you have two problems:
1. Parsing `sshd_config` to figure that out; and
2. Determining which network service name corresponds to the
interface for `192.168.1.100`.
It's generally a bad idea to parse config files in your service
scripts, but the second problem is the harder one. Instead, the most
robust (i.e. the laziest) approach is to make the user specify the
dependency when he makes a change to sshd_config. Include something
like the following in the service configuration file,
```sh
# Specify the network service that corresponds to the "bind" setting
# in your configuration file. For example, if you bind to 127.0.0.1,
# this should be set to "loopback" which provides the loopback interface.
rc_need="loopback"
```
This is a sensible default for daemons that are happy with `0.0.0.0`,
but lets the user specify something else, like `rc_need="net.wan"` if
he needs it. The burden is on the user to determine the appropriate
service whenever he changes the daemon's configuration file.

View File

@@ -33,9 +33,6 @@ else
kmode="-a" kmode="-a"
fi fi
# Set the SELinux label on console before everything so we dont lose output
[ -x /sbin/restorecon ] && /sbin/restorecon -F /dev/console
# Try and set a font and as early as we can # Try and set a font and as early as we can
if service_present "$RC_DEFAULTLEVEL" consolefont || if service_present "$RC_DEFAULTLEVEL" consolefont ||
service_present "$RC_BOOTLEVEL" consolefont; then service_present "$RC_BOOTLEVEL" consolefont; then

View File

@@ -83,7 +83,6 @@ elif ! mountinfo -q /run; then
fi fi
fi fi
[ -x /sbin/restorecon ] && /sbin/restorecon -rF /run
checkpath -d $RC_SVCDIR checkpath -d $RC_SVCDIR
checkpath -d -m 0775 -o root:uucp /run/lock checkpath -d -m 0775 -o root:uucp /run/lock

View File

@@ -133,10 +133,11 @@ _status()
elif service_inactive; then elif service_inactive; then
ewarn "status: inactive" ewarn "status: inactive"
return 16 return 16
elif service_crashed; then
eerror "status: crashed"
return 32
elif service_started; then elif service_started; then
if service_crashed; then
eerror "status: crashed"
return 32
fi
einfo "status: started" einfo "status: started"
return 0 return 0
else else
@@ -242,9 +243,6 @@ sourcex "@LIBEXECDIR@/sh/s6.sh"
sourcex "@LIBEXECDIR@/sh/start-stop-daemon.sh" sourcex "@LIBEXECDIR@/sh/start-stop-daemon.sh"
sourcex "@LIBEXECDIR@/sh/supervise-daemon.sh" sourcex "@LIBEXECDIR@/sh/supervise-daemon.sh"
# Load our script
sourcex "$RC_SERVICE"
# Set verbose mode # Set verbose mode
if yesno "${rc_verbose:-$RC_VERBOSE}"; then if yesno "${rc_verbose:-$RC_VERBOSE}"; then
EINFO_VERBOSE=yes EINFO_VERBOSE=yes
@@ -257,26 +255,26 @@ 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)" = "cgroup_add_service" ] if [ "$(command -v cgroup_add_service)" = \
"cgroup_add_service" ]
then then
if grep -qs /sys/fs/cgroup /proc/1/mountinfo if [ -d /sys/fs/cgroup -a ! -w /sys/fs/cgroup ]; then
then eerror "No permission to apply cgroup settings"
if [ -d /sys/fs/cgroup -a ! -w /sys/fs/cgroup ]; then break
eerror "No permission to apply cgroup settings"
break
fi
fi fi
cgroup_add_service cgroup_add_service /sys/fs/cgroup/openrc
cgroup_add_service /sys/fs/cgroup/systemd/system
fi fi
[ "$(command -v cgroup_set_limits)" = "cgroup_set_limits" ] && [ "$(command -v cgroup_set_limits)" = \
cgroup_set_limits "cgroup_set_limits" ] && \
[ "$(command -v cgroup2_set_limits)" = "cgroup2_set_limits" ] && cgroup_set_limits
[ "$_cmd" = start ] &&
cgroup2_set_limits
break break
fi fi
done done
# Load our script
sourcex "$RC_SERVICE"
eval "printf '%s\n' $required_dirs" | while read _d; do eval "printf '%s\n' $required_dirs" | while read _d; do
if [ -n "$_d" ] && [ ! -d "$_d" ]; then if [ -n "$_d" ] && [ ! -d "$_d" ]; then
eerror "$RC_SVCNAME: \`$_d' is not a directory" eerror "$RC_SVCNAME: \`$_d' is not a directory"
@@ -366,14 +364,10 @@ while [ -n "$1" ]; do
then then
"$1"_post || exit $? "$1"_post || exit $?
fi fi
[ "$(command -v cgroup_cleanup)" = "cgroup_cleanup" ] && [ "$(command -v cgroup_cleanup)" = "cgroup_cleanup" -a \
[ "$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,56 +14,46 @@ description_cgroup_cleanup="Kill all processes in the cgroup"
cgroup_find_path() cgroup_find_path()
{ {
local OIFS name dir result local OIFS n name dir result
[ -n "$1" ] || return 0 [ -n "$1" ] || return 0
OIFS="$IFS" OIFS="$IFS"
IFS=":" IFS=":"
while read -r _ name dir; do while read n name dir; do
[ "$name" = "$1" ] && result="$dir" [ "$name" = "$1" ] && result="$dir"
done < /proc/1/cgroup done < /proc/1/cgroup
IFS="$OIFS" IFS="$OIFS"
printf "%s" "${result}" echo $result
} }
cgroup_get_pids() cgroup_get_pids()
{ {
local cgroup_procs p pids local p
cgroup_procs="$(cgroup2_find_path)" pids=
[ -n "${cgroup_procs}" ] && while read p; do
cgroup_procs="${cgroup_procs}/${RC_SVCNAME}/cgroup.procs" || [ $p -eq $$ ] || pids="${pids} ${p}"
cgroup_procs="/sys/fs/cgroup/openrc/${RC_SVCNAME}/tasks" done < /sys/fs/cgroup/openrc/${RC_SVCNAME}/tasks
[ -f "${cgroup_procs}" ] || return 0 [ -n "$pids" ]
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/unified/${RC_SVCNAME}" ] || [ -d "/sys/fs/cgroup/openrc/${RC_SVCNAME}" ]
[ -d "/sys/fs/cgroup/${RC_SVCNAME}" ] ||
[ -d "/sys/fs/cgroup/openrc/${RC_SVCNAME}" ]
} }
cgroup_set_values() cgroup_set_values()
{ {
[ -n "$1" ] && [ -n "$2" ] && [ -d "/sys/fs/cgroup/$1" ] || return 0 [ -n "$1" -a -n "$2" -a -d "/sys/fs/cgroup/$1" ] || return 0
local controller h local controller="$1" h=$(cgroup_find_path "$1")
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" ] && [ "$controller" != "cpuacct" ]; do while [ -n "$1" -a "$controller" != "cpuacct" ]; do
case "$1" in case "$1" in
$controller.*) $controller.*)
if [ -n "${name}" ] && [ -w "${cgroup}/${name}" ] && if [ -n "$name" -a -w "$cgroup/$name" -a -n "$val" ]; then
[ -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
@@ -78,7 +68,7 @@ cgroup_set_values()
esac esac
shift shift
done done
if [ -n "${name}" ] && [ -w "${cgroup}/${name}" ] && [ -n "${val}" ]; then if [ -n "$name" -a -w "$cgroup/$name" -a -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
@@ -144,83 +134,21 @@ 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}"
[ ! -d "${rc_cgroup_path}" ] && mkdir "${rc_cgroup_path}"
[ -f "${rc_cgroup_path}"/cgroup.procs ] &&
printf 0 > "${rc_cgroup_path}"/cgroup.procs
[ -z "${rc_cgroup_settings}" ] && return 0
echo "${rc_cgroup_settings}" | while read -r key value; do
[ -z "${key}" ] && continue
[ -z "${value}" ] && continue
[ ! -f "${rc_cgroup_path}/${key}" ] && continue
veinfo "${RC_SVCNAME}: cgroups: setting ${key} to ${value}"
printf "%s\n" "${value}" > "${rc_cgroup_path}/${key}"
done
return 0
}
cgroup_cleanup() cgroup_cleanup()
{ {
cgroup_running || return 0 cgroup_running || return 0
ebegin "starting cgroups cleanup" ebegin "starting cgroups cleanup"
local pids loops=0 for sig in TERM QUIT INT; do
pids="$(cgroup_get_pids)" cgroup_get_pids || { eend 0 "finished" ; return 0 ; }
if [ -n "${pids}" ]; then for i in 0 1; do
kill -s CONT ${pids} 2> /dev/null kill -s $sig $pids
kill -s "${stopsig:-TERM}" ${pids} 2> /dev/null for j in 0 1 2; do
yesno "${rc_send_sighup:-no}" && cgroup_get_pids || { eend 0 "finished" ; return 0 ; }
kill -s HUP ${pids} 2> /dev/null sleep 1
kill -s "${stopsig:-TERM}" ${pids} 2> /dev/null done
while [ -n "$(cgroup_get_pids)" ] && done 2>/dev/null
[ "${loops}" -lt "${rc_timeout_stopsec:-90}" ]; do done
loops=$((loops+1)) cgroup_get_pids || { eend 0 "finished" ; return 0; }
sleep 1 kill -9 $pids
done eend $(cgroup_running && echo 1 || echo 0) "fail to stop all processes"
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,13 +119,6 @@ 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

@@ -12,20 +12,6 @@
[ -z "${s6_service_path}" ] && s6_service_path="/var/svc.d/${RC_SVCNAME}" [ -z "${s6_service_path}" ] && s6_service_path="/var/svc.d/${RC_SVCNAME}"
_s6_force_kill() {
local pid
s6_service_link="${RC_SVCDIR}/s6-scan/${s6_service_path##*/}"
pid="${3%)}"
[ -z "${pid}" ] && return 0
if kill -0 "${pid}" 2> /dev/null; then
ewarn "Sending DOWN & KILL for ${RC_SVCNAME}"
s6-svc -dk "${s6_service_link}"
sleep 1
kill -0 "${pid}" 2>/dev/null && return 1
fi
return 0
}
s6_start() s6_start()
{ {
if [ ! -d "${s6_service_path}" ]; then if [ ! -d "${s6_service_path}" ]; then
@@ -55,11 +41,7 @@ s6_stop()
fi fi
s6_service_link="${RC_SVCDIR}/s6-scan/${s6_service_path##*/}" s6_service_link="${RC_SVCDIR}/s6-scan/${s6_service_path##*/}"
ebegin "Stopping ${name:-$RC_SVCNAME}" ebegin "Stopping ${name:-$RC_SVCNAME}"
s6-svc -d -wD -T ${s6_service_timeout_stop:-60000} "${s6_service_link}" s6-svc -wD -d -T ${s6_service_timeout_stop:-10000} "${s6_service_link}"
set -- $(s6-svstat "${s6_service_link}")
[ "$1" = "up" ] &&
yesno "${s6_force_kill:-yes}" &&
_s6_force_kill "$@"
set -- $(s6-svstat "${s6_service_link}") set -- $(s6-svstat "${s6_service_link}")
[ "$1" = "down" ] [ "$1" = "down" ]
eend $? "Failed to stop ${name:-$RC_SVCNAME}" eend $? "Failed to stop ${name:-$RC_SVCNAME}"

View File

@@ -38,25 +38,15 @@ ssd_start()
service_inactive && _inactive=true service_inactive && _inactive=true
mark_service_inactive mark_service_inactive
fi fi
[ -n "$output_logger" ] &&
output_logger_arg="--stdout-logger \"$output_logger\""
[ -n "$error_logger" ] &&
error_logger_arg="--stderr-logger \"$error_logger\""
#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 start-stop-daemon --start \ eval start-stop-daemon --start \
--exec $command \ --exec $command \
${chroot:+--chroot} $chroot \ ${chroot:+--chroot} $chroot \
${directory:+--chdir} $directory \
${output_log+--stdout} $output_log \
${error_log+--stderr} $error_log \
${output_logger_arg} \
${error_logger_arg} \
${procname:+--name} $procname \ ${procname:+--name} $procname \
${pidfile:+--pidfile} $pidfile \ ${pidfile:+--pidfile} $pidfile \
${command_user+--user} $command_user \ ${command_user+--user} $command_user \
${umask+--umask} $umask \
$_background $start_stop_daemon_args \ $_background $start_stop_daemon_args \
-- $command_args $command_args_background -- $command_args $command_args_background
if eend $? "Failed to start ${name:-$RC_SVCNAME}"; then if eend $? "Failed to start ${name:-$RC_SVCNAME}"; then

View File

@@ -22,19 +22,14 @@ 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 "${RC_SVCNAME}" --start \ eval supervise-daemon --start \
${retry:+--retry} $retry \
${directory:+--chdir} $directory \
${chroot:+--chroot} $chroot \ ${chroot:+--chroot} $chroot \
${output_log+--stdout} ${output_log} \
${error_log+--stderr} $error_log \
${pidfile:+--pidfile} $pidfile \ ${pidfile:+--pidfile} $pidfile \
${respawn_delay:+--respawn-delay} $respawn_delay \ ${respawn_delay:+--respawn-delay} $respawn_delay \
${respawn_max:+--respawn-max} $respawn_max \ ${respawn_max:+--respawn-max} $respawn_max \
${respawn_period:+--respawn-period} $respawn_period \ ${respawn_period:+--respawn-period} $respawn_period \
${command_user+--user} $command_user \ ${command_user+--user} $command_user \
${umask+--umask} $umask \ $supervise_daemon_args \
${supervise_daemon_args:-${start_stop_daemon_args}} \
$command \ $command \
-- $command_args $command_args_foreground -- $command_args $command_args_foreground
rc=$? rc=$?
@@ -53,48 +48,14 @@ 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 "${RC_SVCNAME}" --stop \ supervise-daemon --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()
{ {
if service_stopping; then _status
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

@@ -28,11 +28,9 @@
#if __GNUC__ > 2 || defined(__INTEL_COMPILER) #if __GNUC__ > 2 || defined(__INTEL_COMPILER)
# define _dead __attribute__((__noreturn__)) # define _dead __attribute__((__noreturn__))
# define _unused __attribute__((__unused__)) # define _unused __attribute__((__unused__))
# define _xasprintf(a, b) __attribute__((__format__(__printf__, a, b)))
#else #else
# define _dead # define _dead
# define _unused # define _unused
# define _xasprintf(a, b)
#endif #endif
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
@@ -55,7 +53,6 @@
} while (/* CONSTCOND */ 0) } while (/* CONSTCOND */ 0)
#endif #endif
#include <stdarg.h>
#include <stdbool.h> #include <stdbool.h>
#include <sys/stat.h> #include <sys/stat.h>
@@ -99,10 +96,8 @@ _unused static char *xstrdup(const char *str)
#undef ERRX #undef ERRX
/* /* basename_c never modifies the argument. As such, if there is a trailing
* basename_c never modifies the argument. As such, if there is a trailing * slash then an empty string is returned. */
* slash then an empty string is returned.
*/
_unused static const char *basename_c(const char *path) _unused static const char *basename_c(const char *path)
{ {
const char *slash = strrchr(path, '/'); const char *slash = strrchr(path, '/');
@@ -126,49 +121,4 @@ _unused static bool existss(const char *pathname)
return (stat(pathname, &buf) == 0 && buf.st_size != 0); 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 #endif

View File

@@ -47,7 +47,6 @@ 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 *);
@@ -66,9 +65,11 @@ int parse_mode(mode_t *, char *);
/* Handy function so we can wrap einfo around our deptree */ /* Handy function so we can wrap einfo around our deptree */
RC_DEPTREE *_rc_deptree_load (int, int *); RC_DEPTREE *_rc_deptree_load (int, int *);
/* Test to see if we can see pid 1 or not */
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,12 +80,9 @@ 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];
@@ -134,14 +131,6 @@ 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;
@@ -149,14 +138,6 @@ 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 (strlen(my_ns) && strlen (proc_ns) && 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

@@ -542,41 +542,52 @@ rc_deptree_order(const RC_DEPTREE *deptree, const char *runlevel, int options)
} }
librc_hidden_def(rc_deptree_order) librc_hidden_def(rc_deptree_order)
/* Given a time, recurse the target path to find out if there are
any older (or newer) files. If false, sets the time to the
oldest (or newest) found.
*/
static bool static bool
deep_mtime_check(const char *target, bool newer, mtime_check(const char *source, const char *target, bool newer,
time_t *rel, char *file) time_t *rel, char *file)
{ {
struct stat buf; struct stat buf;
time_t mtime;
bool retval = true; bool retval = true;
DIR *dp; DIR *dp;
struct dirent *d; struct dirent *d;
char path[PATH_MAX]; char path[PATH_MAX];
int serrno = errno; int serrno = errno;
/* We have to exist */
if (stat(source, &buf) != 0)
return false;
mtime = buf.st_mtime;
/* If target does not exist, return true to mimic shell test */ /* If target does not exist, return true to mimic shell test */
if (stat(target, &buf) != 0) if (stat(target, &buf) != 0)
return true; return true;
if (newer) { if (newer) {
if (*rel < buf.st_mtime) { if (mtime < buf.st_mtime) {
if (rel == NULL)
return false;
retval = false; retval = false;
}
if (file) if (rel != NULL) {
strlcpy(file, target, PATH_MAX); if (*rel < buf.st_mtime) {
*rel = buf.st_mtime; if (file)
strlcpy(file, target, PATH_MAX);
*rel = buf.st_mtime;
}
} }
} else { } else {
if (*rel > buf.st_mtime) { if (mtime > buf.st_mtime) {
if (rel == NULL)
return false;
retval = false; retval = false;
}
if (file) if (rel != NULL) {
strlcpy(file, target, PATH_MAX); if (*rel > buf.st_mtime) {
*rel = buf.st_mtime; if (file)
strlcpy(file, target, PATH_MAX);
*rel = buf.st_mtime;
}
} }
} }
@@ -591,38 +602,16 @@ deep_mtime_check(const char *target, bool newer,
if (d->d_name[0] == '.') if (d->d_name[0] == '.')
continue; continue;
snprintf(path, sizeof(path), "%s/%s", target, d->d_name); snprintf(path, sizeof(path), "%s/%s", target, d->d_name);
if (!deep_mtime_check(path, newer, rel, file)) { if (!mtime_check(source, path, newer, rel, file)) {
retval = false; retval = false;
if (rel == NULL)
break;
} }
} }
closedir(dp); closedir(dp);
return retval; return retval;
} }
/* Recursively check if target is older/newer than source.
* If false, return the filename and most different time (if
* the return value arguments are non-null).
*/
static bool
mtime_check(const char *source, const char *target, bool newer,
time_t *rel, char *file)
{
struct stat buf;
time_t mtime;
bool retval = true;
/* We have to exist */
if (stat(source, &buf) != 0)
return false;
mtime = buf.st_mtime;
retval = deep_mtime_check(target,newer,&mtime,file);
if (rel) {
*rel = mtime;
}
return retval;
}
bool bool
rc_newer_than(const char *source, const char *target, rc_newer_than(const char *source, const char *target,
time_t *newest, char *file) time_t *newest, char *file)
@@ -681,8 +670,6 @@ rc_deptree_update_needed(time_t *newest, char *file)
RC_STRINGLIST *config; RC_STRINGLIST *config;
RC_STRING *s; RC_STRING *s;
int i; int i;
struct stat buf;
time_t mtime;
/* Create base directories if needed */ /* Create base directories if needed */
for (i = 0; depdirs[i]; i++) for (i = 0; depdirs[i]; i++)
@@ -690,48 +677,42 @@ rc_deptree_update_needed(time_t *newest, char *file)
fprintf(stderr, "mkdir `%s': %s\n", depdirs[i], strerror(errno)); fprintf(stderr, "mkdir `%s': %s\n", depdirs[i], strerror(errno));
/* Quick test to see if anything we use has changed and we have /* Quick test to see if anything we use has changed and we have
* data in our deptree. */ * data in our deptree */
if (!existss(RC_DEPTREE_CACHE))
if (stat(RC_DEPTREE_CACHE, &buf) == 0) { return true;
mtime = buf.st_mtime; if (!rc_newer_than(RC_DEPTREE_CACHE, RC_INITDIR, newest, file))
} else { return true;
/* No previous cache found. if (!rc_newer_than(RC_DEPTREE_CACHE, RC_CONFDIR, newest, file))
* We still run the scan, in case of clock skew; we still need to return return true;
* the newest time.
*/
newer = true;
mtime = time(NULL);
}
newer |= !deep_mtime_check(RC_INITDIR,true,&mtime,file);
newer |= !deep_mtime_check(RC_CONFDIR,true,&mtime,file);
#ifdef RC_PKG_INITDIR #ifdef RC_PKG_INITDIR
newer |= !deep_mtime_check(RC_PKG_INITDIR,true,&mtime,file); if (!rc_newer_than(RC_DEPTREE_CACHE, RC_PKG_INITDIR, newest, file))
return true;
#endif #endif
#ifdef RC_PKG_CONFDIR #ifdef RC_PKG_CONFDIR
newer |= !deep_mtime_check(RC_PKG_CONFDIR,true,&mtime,file); if (!rc_newer_than(RC_DEPTREE_CACHE, RC_PKG_CONFDIR, newest, file))
return true;
#endif #endif
#ifdef RC_LOCAL_INITDIRs #ifdef RC_LOCAL_INITDIR
newer |= !deep_mtime_check(RC_LOCAL_INITDIR,true,&mtime,file); if (!rc_newer_than(RC_DEPTREE_CACHE, RC_LOCAL_INITDIR, newest, file))
return true;
#endif #endif
#ifdef RC_LOCAL_CONFDIR #ifdef RC_LOCAL_CONFDIR
newer |= !deep_mtime_check(RC_LOCAL_CONFDIR,true,&mtime,file); if (!rc_newer_than(RC_DEPTREE_CACHE, RC_LOCAL_CONFDIR, newest, file))
return true;
#endif #endif
newer |= !deep_mtime_check(RC_CONF,true,&mtime,file); if (!rc_newer_than(RC_DEPTREE_CACHE, RC_CONF, newest, file))
return true;
/* Some init scripts dependencies change depending on config files /* Some init scripts dependencies change depending on config files
* outside of baselayout, like syslog-ng, so we check those too. */ * outside of baselayout, like syslog-ng, so we check those too. */
config = rc_config_list(RC_DEPCONFIG); config = rc_config_list(RC_DEPCONFIG);
TAILQ_FOREACH(s, config, entries) { TAILQ_FOREACH(s, config, entries) {
newer |= !deep_mtime_check(s->value, true, &mtime, file); if (!rc_newer_than(RC_DEPTREE_CACHE, s->value, newest, file)) {
newer = true;
break;
}
} }
rc_stringlist_free(config); rc_stringlist_free(config);
/* Return newest file time, if requested */
if ((newer) && (newest != NULL)) {
*newest = mtime;
}
return newer; return newer;
} }
librc_hidden_def(rc_deptree_update_needed) librc_hidden_def(rc_deptree_update_needed)

View File

@@ -50,7 +50,6 @@ static const rc_service_state_name_t rc_service_state_names[] = {
{ RC_SERVICE_HOTPLUGGED, "hotplugged" }, { RC_SERVICE_HOTPLUGGED, "hotplugged" },
{ RC_SERVICE_FAILED, "failed" }, { RC_SERVICE_FAILED, "failed" },
{ RC_SERVICE_SCHEDULED, "scheduled"}, { RC_SERVICE_SCHEDULED, "scheduled"},
{ RC_SERVICE_CRASHED, "crashed"},
{ 0, NULL} { 0, NULL}
}; };
@@ -849,10 +848,6 @@ rc_service_state(const char *service)
} }
} }
if (state & RC_SERVICE_STARTED) {
if (rc_service_daemons_crashed(service))
state |= RC_SERVICE_CRASHED;
}
if (state & RC_SERVICE_STOPPED) { if (state & RC_SERVICE_STOPPED) {
dirs = ls_dir(RC_SVCDIR "/scheduled", 0); dirs = ls_dir(RC_SVCDIR "/scheduled", 0);
TAILQ_FOREACH(dir, dirs, entries) { TAILQ_FOREACH(dir, dirs, entries) {
@@ -899,15 +894,12 @@ 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 (value) { if (!(fp = fopen(file, "w")))
if (!(fp = fopen(file, "w"))) return false;
return false; if (value)
fprintf(fp, "%s", value); fprintf(fp, "%s", value);
fclose(fp); fclose(fp);
} else { return true;
unlink(file);
}
return true;
} }
librc_hidden_def(rc_service_value_set) librc_hidden_def(rc_service_value_set)

View File

@@ -188,8 +188,7 @@ typedef enum
/* Optional states service could also be in */ /* Optional states service could also be in */
RC_SERVICE_FAILED = 0x0200, RC_SERVICE_FAILED = 0x0200,
RC_SERVICE_SCHEDULED = 0x0400, RC_SERVICE_SCHEDULED = 0x0400,
RC_SERVICE_WASINACTIVE = 0x0800, RC_SERVICE_WASINACTIVE = 0x0800
RC_SERVICE_CRASHED = 0x1000,
} RC_SERVICE; } RC_SERVICE;
/*! Add the service to the runlevel /*! Add the service to the runlevel

1
src/rc/.gitignore vendored
View File

@@ -56,7 +56,6 @@ mark_service_inactive
mark_service_wasinactive mark_service_wasinactive
mark_service_hotplugged mark_service_hotplugged
mark_service_failed mark_service_failed
mark_service_crashed
rc-abort rc-abort
rc rc
openrc openrc

View File

@@ -5,8 +5,8 @@ include ${MK}/os.mk
SRCS= checkpath.c do_e.c do_mark_service.c do_service.c \ 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 \ do_value.c fstabinfo.c is_newer_than.c is_older_than.c \
mountinfo.c openrc-run.c rc-abort.c rc.c \ mountinfo.c openrc-run.c rc-abort.c rc.c \
rc-depend.c rc-logger.c rc-misc.c rc-pipes.c \ rc-depend.c rc-logger.c rc-misc.c rc-plugin.c \
rc-plugin.c rc-service.c rc-status.c rc-update.c \ rc-service.c rc-status.c rc-update.c \
shell_var.c start-stop-daemon.c supervise-daemon.c swclock.c _usage.c shell_var.c start-stop-daemon.c supervise-daemon.c swclock.c _usage.c
ifeq (${MKSELINUX},yes) ifeq (${MKSELINUX},yes)
@@ -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 \ SBINPROGS = openrc openrc-run rc rc-service rc-update runscript service \
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 \
@@ -41,7 +41,6 @@ RC_SBINPROGS= mark_service_starting mark_service_started \
mark_service_stopping mark_service_stopped \ mark_service_stopping mark_service_stopped \
mark_service_inactive mark_service_wasinactive \ mark_service_inactive mark_service_wasinactive \
mark_service_hotplugged mark_service_failed \ mark_service_hotplugged mark_service_failed \
mark_service_crashed \
rc-abort swclock rc-abort swclock
ifeq (${OS},Linux) ifeq (${OS},Linux)
@@ -124,8 +123,7 @@ is_older_than: is_older_than.o rc-misc.o
mark_service_starting mark_service_started \ mark_service_starting mark_service_started \
mark_service_stopping mark_service_stopped \ mark_service_stopping mark_service_stopped \
mark_service_inactive mark_service_wasinactive \ mark_service_inactive mark_service_wasinactive \
mark_service_hotplugged mark_service_failed \ mark_service_hotplugged mark_service_failed: do_mark_service.o rc-misc.o
mark_service_crashed: do_mark_service.o rc-misc.o
${CC} ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ $^ ${LDADD} ${CC} ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ $^ ${LDADD}
mountinfo: mountinfo.o _usage.o rc-misc.o mountinfo: mountinfo.o _usage.o rc-misc.o
@@ -152,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: rc-service.o _usage.o rc-misc.o rc-service 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 rc-pipes.o rc-schedules.o start-stop-daemon: start-stop-daemon.o _usage.o rc-misc.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 rc-schedules.o supervise-daemon: supervise-daemon.o _usage.o rc-misc.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

@@ -73,32 +73,25 @@ static int do_check(char *path, uid_t uid, gid_t gid, mode_t mode,
inode_t type, bool trunc, bool chowner, bool selinux_on) inode_t type, bool trunc, bool chowner, bool selinux_on)
{ {
struct stat st; struct stat st;
int fd; int fd, flags;
int flags;
int r; int r;
int readfd;
int readflags;
int u; int u;
memset(&st, 0, sizeof(st)); memset(&st, 0, sizeof(st));
flags = O_CREAT|O_NDELAY|O_WRONLY|O_NOCTTY; if (lstat(path, &st) || trunc) {
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;
readfd = open(path, readflags);
if (readfd == -1 || (type == inode_file && trunc)) {
if (type == inode_file) { if (type == inode_file) {
einfo("%s: creating file", path); einfo("%s: creating file", path);
if (!mode) /* 664 */ if (!mode) /* 664 */
mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH; mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH;
flags = O_CREAT|O_NDELAY|O_WRONLY|O_NOCTTY;
#ifdef O_CLOEXEC
flags |= O_CLOEXEC;
#endif
#ifdef O_NOFOLLOW
flags |= O_NOFOLLOW;
#endif
if (trunc)
flags |= O_TRUNC;
u = umask(0); u = umask(0);
fd = open(path, flags, mode); fd = open(path, flags, mode);
umask(u); umask(u);
@@ -106,9 +99,7 @@ static int do_check(char *path, uid_t uid, gid_t gid, mode_t mode,
eerror("%s: open: %s", applet, strerror(errno)); eerror("%s: open: %s", applet, strerror(errno));
return -1; return -1;
} }
if (readfd != -1 && trunc) close (fd);
close(readfd);
readfd = fd;
} else if (type == inode_dir) { } else if (type == inode_dir) {
einfo("%s: creating directory", path); einfo("%s: creating directory", path);
if (!mode) /* 775 */ if (!mode) /* 775 */
@@ -122,12 +113,7 @@ static int do_check(char *path, uid_t uid, gid_t gid, mode_t mode,
strerror (errno)); strerror (errno));
return -1; return -1;
} }
readfd = open(path, readflags); mode = 0;
if (readfd == -1) {
eerror("%s: unable to open directory: %s", applet,
strerror(errno));
return -1;
}
} else if (type == inode_fifo) { } else if (type == inode_fifo) {
einfo("%s: creating fifo", path); einfo("%s: creating fifo", path);
if (!mode) /* 600 */ if (!mode) /* 600 */
@@ -140,76 +126,56 @@ static int do_check(char *path, uid_t uid, gid_t gid, mode_t mode,
strerror (errno)); strerror (errno));
return -1; return -1;
} }
readfd = open(path, readflags);
if (readfd == -1) {
eerror("%s: unable to open fifo: %s", applet,
strerror(errno));
return -1;
}
} }
} } else {
if (fstat(readfd, &st) != -1) {
if (type != inode_dir && S_ISDIR(st.st_mode)) { if (type != inode_dir && S_ISDIR(st.st_mode)) {
eerror("%s: is a directory", path); eerror("%s: is a directory", path);
close(readfd);
return 1; return 1;
} }
if (type != inode_file && S_ISREG(st.st_mode)) { if (type != inode_file && S_ISREG(st.st_mode)) {
eerror("%s: is a file", path); eerror("%s: is a file", path);
close(readfd);
return 1; return 1;
} }
if (type != inode_fifo && S_ISFIFO(st.st_mode)) { if (type != inode_fifo && S_ISFIFO(st.st_mode)) {
eerror("%s: is a fifo", path); eerror("%s: is a fifo", path);
close(readfd);
return -1; return -1;
} }
if (mode && (st.st_mode & 0777) != mode) {
if ((type != inode_dir) && (st.st_nlink > 1)) {
eerror("%s: chmod: %s %s", applet, "Too many hard links to", 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);
if (mode && (st.st_mode & 0777) != mode) {
if ((type != inode_dir) && (st.st_nlink > 1)) {
eerror("%s: chmod: %s %s", applet, "Too many hard links to", path);
return -1;
}
if (S_ISLNK(st.st_mode)) {
eerror("%s: chmod: %s %s", applet, path, " is a symbolic link");
return -1;
}
einfo("%s: correcting mode", path);
if (chmod(path, mode)) {
eerror("%s: chmod: %s", applet, strerror(errno));
return -1;
}
}
if (chowner && (st.st_uid != uid || st.st_gid != gid)) {
if ((type != inode_dir) && (st.st_nlink > 1)) {
eerror("%s: chown: %s %s", applet, "Too many hard links to", path);
return -1;
}
if (S_ISLNK(st.st_mode)) {
eerror("%s: chown: %s %s", applet, path, " is a symbolic link");
return -1;
}
einfo("%s: correcting owner", path);
if (chown(path, uid, gid)) {
eerror("%s: chown: %s", applet, strerror(errno));
return -1;
}
}
if (selinux_on)
selinux_util_label(path);
return 0; return 0;
} }

View File

@@ -68,7 +68,9 @@ int main(int argc, char **argv)
ok = rc_service_started_daemon(service, exec, NULL, idx); ok = rc_service_started_daemon(service, exec, NULL, idx);
} else if (strcmp(applet, "service_crashed") == 0) { } else if (strcmp(applet, "service_crashed") == 0) {
ok = ( rc_service_daemons_crashed(service) && errno != EACCES); ok = (_rc_can_find_pids() &&
rc_service_daemons_crashed(service) &&
errno != EACCES);
} else } else
eerrorx("%s: unknown applet", applet); eerrorx("%s: unknown applet", applet);

View File

@@ -35,11 +35,11 @@
# define GET_ENT getmntent (fp) # define GET_ENT getmntent (fp)
# define GET_ENT_FILE(_name) getmntfile (_name) # define GET_ENT_FILE(_name) getmntfile (_name)
# define END_ENT endmntent (fp) # define END_ENT endmntent (fp)
# define ENT_BLOCKDEVICE(_ent) (_ent)->mnt_fsname # define ENT_BLOCKDEVICE(_ent) ent->mnt_fsname
# define ENT_FILE(_ent) (_ent)->mnt_dir # define ENT_FILE(_ent) ent->mnt_dir
# define ENT_TYPE(_ent) (_ent)->mnt_type # define ENT_TYPE(_ent) ent->mnt_type
# define ENT_OPTS(_ent) (_ent)->mnt_opts # define ENT_OPTS(_ent) ent->mnt_opts
# define ENT_PASS(_ent) (_ent)->mnt_passno # define ENT_PASS(_ent) ent->mnt_passno
#else #else
# define HAVE_GETFSENT # define HAVE_GETFSENT
# include <fstab.h> # include <fstab.h>
@@ -48,11 +48,11 @@
# define GET_ENT getfsent () # define GET_ENT getfsent ()
# define GET_ENT_FILE(_name) getfsfile (_name) # define GET_ENT_FILE(_name) getfsfile (_name)
# define END_ENT endfsent () # define END_ENT endfsent ()
# define ENT_BLOCKDEVICE(_ent) (_ent)->fs_spec # define ENT_BLOCKDEVICE(_ent) ent->fs_spec
# define ENT_TYPE(_ent) (_ent)->fs_vfstype # define ENT_TYPE(_ent) ent->fs_vfstype
# define ENT_FILE(_ent) (_ent)->fs_file # define ENT_FILE(_ent) ent->fs_file
# define ENT_OPTS(_ent) (_ent)->fs_mntops # define ENT_OPTS(_ent) ent->fs_mntops
# define ENT_PASS(_ent) (_ent)->fs_passno # define ENT_PASS(_ent) ent->fs_passno
#endif #endif
#include "einfo.h" #include "einfo.h"
@@ -114,24 +114,24 @@ do_mount(struct ENT *ent, bool remount)
argv[0] = UNCONST("mount"); argv[0] = UNCONST("mount");
argv[1] = UNCONST("-o"); argv[1] = UNCONST("-o");
argv[2] = ENT_OPTS(ent); argv[2] = ENT_OPTS(*ent);
argv[3] = UNCONST("-t"); argv[3] = UNCONST("-t");
argv[4] = ENT_TYPE(ent); argv[4] = ENT_TYPE(*ent);
if (!remount) { if (!remount) {
argv[5] = ENT_BLOCKDEVICE(ent); argv[5] = ENT_BLOCKDEVICE(*ent);
argv[6] = ENT_FILE(ent); argv[6] = ENT_FILE(*ent);
argv[7] = NULL; argv[7] = NULL;
} else { } else {
#ifdef __linux__ #ifdef __linux__
argv[5] = UNCONST("-o"); argv[5] = UNCONST("-o");
argv[6] = UNCONST("remount"); argv[6] = UNCONST("remount");
argv[7] = ENT_BLOCKDEVICE(ent); argv[7] = ENT_BLOCKDEVICE(*ent);
argv[8] = ENT_FILE(ent); argv[8] = ENT_FILE(*ent);
argv[9] = NULL; argv[9] = NULL;
#else #else
argv[5] = UNCONST("-u"); argv[5] = UNCONST("-u");
argv[6] = ENT_BLOCKDEVICE(ent); argv[6] = ENT_BLOCKDEVICE(*ent);
argv[7] = ENT_FILE(ent); argv[7] = ENT_FILE(*ent);
argv[8] = NULL; argv[8] = NULL;
#endif #endif
} }
@@ -141,7 +141,7 @@ do_mount(struct ENT *ent, bool remount)
/* NOTREACHED */ /* NOTREACHED */
case 0: case 0:
execvp(argv[0], argv); execvp(argv[0], argv);
eerror("%s: execvp: %s", applet, strerror(errno)); eerror("%s: execv: %s", applet, strerror(errno));
_exit(EXIT_FAILURE); _exit(EXIT_FAILURE);
/* NOTREACHED */ /* NOTREACHED */
default: default:

View File

@@ -65,7 +65,7 @@ static int mount_proc(void)
break; break;
case 0: case 0:
/* attempt to mount /proc */ /* attempt to mount /proc */
execlp("mount", "mount", "-t", "proc", "proc", "/proc", NULL); execl("mount", "mount", "-t", "proc", "proc", "/proc", NULL);
syslog(LOG_ERR, "Unable to execute mount"); syslog(LOG_ERR, "Unable to execute mount");
exit(1); exit(1);
break; break;
@@ -87,11 +87,10 @@ static int mount_proc(void)
static bool is_user_process(pid_t pid) static bool is_user_process(pid_t pid)
{ {
char *buf = NULL; char buf[PATH_MAX+1];
FILE *fp; FILE *fp;
char *path = NULL; char path[PATH_MAX+1];
pid_t temp_pid; pid_t temp_pid;
size_t size;
bool user_process = true; bool user_process = true;
while (pid >0 && user_process) { while (pid >0 && user_process) {
@@ -99,9 +98,8 @@ static bool is_user_process(pid_t pid)
user_process = false; user_process = false;
continue; continue;
} }
xasprintf(&path, "/proc/%d/status", pid); snprintf(path, sizeof(path), "/proc/%d/status", pid);
fp = fopen(path, "r"); fp = fopen(path, "r");
free(path);
/* /*
* if we could not open the file, the process disappeared, which * if we could not open the file, the process disappeared, which
* leaves us no way to determine for sure whether it was a user * leaves us no way to determine for sure whether it was a user
@@ -114,14 +112,11 @@ static bool is_user_process(pid_t pid)
} }
temp_pid = -1; temp_pid = -1;
while (! feof(fp)) { while (! feof(fp)) {
buf = NULL; buf[0] = 0;
if (getline(&buf, &size, fp) != -1) { if (fgets(buf, sizeof(buf), fp))
sscanf(buf, "PPid: %d", &temp_pid); sscanf(buf, "PPid: %d", &temp_pid);
free(buf); else
} else {
free(buf);
break; break;
}
} }
fclose(fp); fclose(fp);
if (temp_pid == -1) { if (temp_pid == -1) {
@@ -140,7 +135,7 @@ static int signal_processes(int sig, RC_STRINGLIST *omits, bool dryrun)
sigset_t oldsigs; sigset_t oldsigs;
DIR *dir; DIR *dir;
struct dirent *d; struct dirent *d;
char *buf = NULL; char buf[PATH_MAX+1];
pid_t pid; pid_t pid;
int sendcount = 0; int sendcount = 0;
@@ -175,11 +170,7 @@ static int signal_processes(int sig, RC_STRINGLIST *omits, bool dryrun)
continue; continue;
/* Is this a process we have been requested to omit? */ /* Is this a process we have been requested to omit? */
if (buf) { sprintf(buf, "%d", pid);
free(buf);
buf = NULL;
}
xasprintf(&buf, "%d", pid);
if (rc_stringlist_find(omits, buf)) if (rc_stringlist_find(omits, buf))
continue; continue;

View File

@@ -248,6 +248,7 @@ find_mounts(struct args *args)
struct opt *o; struct opt *o;
int netdev; int netdev;
char *tmp; char *tmp;
size_t l;
if ((nmnts = getmntinfo(&mnts, MNT_NOWAIT)) == 0) if ((nmnts = getmntinfo(&mnts, MNT_NOWAIT)) == 0)
eerrorx("getmntinfo: %s", strerror (errno)); eerrorx("getmntinfo: %s", strerror (errno));
@@ -263,7 +264,11 @@ find_mounts(struct args *args)
if (! options) if (! options)
options = xstrdup(o->o_name); options = xstrdup(o->o_name);
else { else {
xasprintf(&tmp, "%s,%s", options, o->o_name); l = strlen(options) +
strlen(o->o_name) + 2;
tmp = xmalloc(sizeof (char) * l);
snprintf(tmp, l, "%s,%s", options,
o->o_name);
free(options); free(options);
options = tmp; options = tmp;
} }
@@ -310,7 +315,6 @@ find_mounts(struct args *args)
{ {
FILE *fp; FILE *fp;
char *buffer; char *buffer;
size_t size;
char *p; char *p;
char *from; char *from;
char *to; char *to;
@@ -325,8 +329,8 @@ find_mounts(struct args *args)
list = rc_stringlist_new(); list = rc_stringlist_new();
buffer = NULL; buffer = xmalloc(sizeof(char) * PATH_MAX * 3);
while (getline(&buffer, &size, fp) != -1) { while (fgets(buffer, PATH_MAX * 3, fp)) {
netdev = -1; netdev = -1;
p = buffer; p = buffer;
from = strsep(&p, " "); from = strsep(&p, " ");
@@ -342,8 +346,6 @@ find_mounts(struct args *args)
} }
process_mount(list, args, from, to, fst, opts, netdev); process_mount(list, args, from, to, fst, opts, netdev);
free(buffer);
buffer = NULL;
} }
free(buffer); free(buffer);
fclose(fp); fclose(fp);
@@ -378,7 +380,7 @@ int main(int argc, char **argv)
regex_t *skip_point_regex = NULL; regex_t *skip_point_regex = NULL;
RC_STRINGLIST *nodes; RC_STRINGLIST *nodes;
RC_STRING *s; RC_STRING *s;
char *real_path = NULL; char real_path[PATH_MAX + 1];
int opt; int opt;
int result; int result;
char *this_path; char *this_path;
@@ -448,12 +450,9 @@ int main(int argc, char **argv)
eerrorx("%s: `%s' is not a mount point", eerrorx("%s: `%s' is not a mount point",
argv[0], argv[optind]); argv[0], argv[optind]);
this_path = argv[optind++]; this_path = argv[optind++];
real_path = realpath(this_path, NULL); if (realpath(this_path, real_path))
if (real_path)
this_path = real_path; this_path = real_path;
rc_stringlist_add(args.mounts, this_path); rc_stringlist_add(args.mounts, this_path);
free(real_path);
real_path = NULL;
} }
nodes = find_mounts(&args); nodes = find_mounts(&args);
rc_stringlist_free(args.mounts); rc_stringlist_free(args.mounts);

View File

@@ -35,7 +35,6 @@
#include "rc-wtmp.h" #include "rc-wtmp.h"
#include "version.h" #include "version.h"
static const char *path_default = "/sbin:/usr/sbin:/bin:/usr/bin";
static const char *rc_default_runlevel = "default"; static const char *rc_default_runlevel = "default";
static pid_t do_openrc(const char *runlevel) static pid_t do_openrc(const char *runlevel)
@@ -54,7 +53,7 @@ static pid_t do_openrc(const char *runlevel)
sigemptyset(&signals); sigemptyset(&signals);
sigprocmask(SIG_SETMASK, &signals, NULL); sigprocmask(SIG_SETMASK, &signals, NULL);
printf("Starting %s runlevel\n", runlevel); printf("Starting %s runlevel\n", runlevel);
execlp("openrc", "openrc", runlevel, NULL); execl("/sbin/openrc", "/sbin/openrc", runlevel, NULL);
perror("exec"); perror("exec");
break; break;
default: default:
@@ -89,7 +88,7 @@ static void init(const char *default_runlevel)
static void handle_reexec(char *my_name) static void handle_reexec(char *my_name)
{ {
execlp(my_name, my_name, "reexec", NULL); execl(my_name, my_name, "reexec", NULL);
return; return;
} }
@@ -184,9 +183,6 @@ int main(int argc, char **argv)
sigaction(SIGINT, &sa, NULL); sigaction(SIGINT, &sa, NULL);
reboot(RB_DISABLE_CAD); reboot(RB_DISABLE_CAD);
/* set default path */
setenv("PATH", path_default, 1);
if (! reexec) if (! reexec)
init(default_runlevel); init(default_runlevel);

View File

@@ -109,7 +109,7 @@ static void
handle_signal(int sig) handle_signal(int sig)
{ {
int serrno = errno; int serrno = errno;
char *signame = NULL; char signame[10] = { '\0' };
struct winsize ws; struct winsize ws;
switch (sig) { switch (sig) {
@@ -134,22 +134,20 @@ handle_signal(int sig)
break; break;
case SIGINT: case SIGINT:
if (!signame) if (!signame[0])
xasprintf(&signame, "SIGINT"); snprintf(signame, sizeof(signame), "SIGINT");
/* FALLTHROUGH */ /* FALLTHROUGH */
case SIGTERM: case SIGTERM:
if (!signame) if (!signame[0])
xasprintf(&signame, "SIGTERM"); snprintf(signame, sizeof(signame), "SIGTERM");
/* FALLTHROUGH */ /* FALLTHROUGH */
case SIGQUIT: case SIGQUIT:
if (!signame) if (!signame[0])
xasprintf(&signame, "SIGQUIT"); snprintf(signame, sizeof(signame), "SIGQUIT");
/* Send the signal to our children too */ /* Send the signal to our children too */
if (service_pid > 0) if (service_pid > 0)
kill(service_pid, sig); kill(service_pid, sig);
eerror("%s: caught %s, aborting", applet, signame); eerrorx("%s: caught %s, aborting", applet, signame);
free(signame);
exit(EXIT_FAILURE);
/* NOTREACHED */ /* NOTREACHED */
default: default:
@@ -163,12 +161,11 @@ handle_signal(int sig)
static void static void
unhotplug() unhotplug()
{ {
char *file = NULL; char file[PATH_MAX];
xasprintf(&file, RC_SVCDIR "/hotplugged/%s", applet); snprintf(file, sizeof(file), RC_SVCDIR "/hotplugged/%s", applet);
if (exists(file) && unlink(file) != 0) if (exists(file) && unlink(file) != 0)
eerror("%s: unlink `%s': %s", applet, file, strerror(errno)); eerror("%s: unlink `%s': %s", applet, file, strerror(errno));
free(file);
} }
static void static void
@@ -488,7 +485,7 @@ svc_exec(const char *arg1, const char *arg2)
static bool static bool
svc_wait(const char *svc) svc_wait(const char *svc)
{ {
char *file = NULL; char file[PATH_MAX];
int fd; int fd;
bool forever = false; bool forever = false;
RC_STRINGLIST *keywords; RC_STRINGLIST *keywords;
@@ -501,7 +498,8 @@ svc_wait(const char *svc)
forever = true; forever = true;
rc_stringlist_free(keywords); rc_stringlist_free(keywords);
xasprintf(&file, RC_SVCDIR "/exclusive/%s", basename_c(svc)); snprintf(file, sizeof(file), RC_SVCDIR "/exclusive/%s",
basename_c(svc));
interval.tv_sec = 0; interval.tv_sec = 0;
interval.tv_nsec = WAIT_INTERVAL; interval.tv_nsec = WAIT_INTERVAL;
@@ -514,29 +512,23 @@ svc_wait(const char *svc)
if (fd != -1) { if (fd != -1) {
if (flock(fd, LOCK_SH | LOCK_NB) == 0) { if (flock(fd, LOCK_SH | LOCK_NB) == 0) {
close(fd); close(fd);
free(file);
return true; return true;
} }
close(fd); close(fd);
} }
if (errno == ENOENT) { if (errno == ENOENT)
free(file);
return true; return true;
} if (errno != EWOULDBLOCK)
if (errno != EWOULDBLOCK) { eerrorx("%s: open `%s': %s", applet, file,
eerror("%s: open `%s': %s", applet, file,
strerror(errno)); strerror(errno));
free(file);
exit(EXIT_FAILURE);
}
if (nanosleep(&interval, NULL) == -1) { if (nanosleep(&interval, NULL) == -1) {
if (errno != EINTR) if (errno != EINTR)
goto finish; return false;
} }
if (!forever) { if (!forever) {
timespecsub(&timeout, &interval, &timeout); timespecsub(&timeout, &interval, &timeout);
if (timeout.tv_sec <= 0) if (timeout.tv_sec <= 0)
goto finish; return false;
timespecsub(&warn, &interval, &warn); timespecsub(&warn, &interval, &warn);
if (warn.tv_sec <= 0) { if (warn.tv_sec <= 0) {
ewarn("%s: waiting for %s (%d seconds)", ewarn("%s: waiting for %s (%d seconds)",
@@ -546,8 +538,6 @@ svc_wait(const char *svc)
} }
} }
} }
finish:
free(file);
return false; return false;
} }
@@ -1115,10 +1105,9 @@ int main(int argc, char **argv)
bool runscript = false; bool runscript = false;
int retval, opt, depoptions = RC_DEP_TRACE; int retval, opt, depoptions = RC_DEP_TRACE;
RC_STRING *svc; RC_STRING *svc;
char *path = NULL; char path[PATH_MAX], lnk[PATH_MAX];
char *lnk = NULL;
char *dir, *save = NULL, *saveLnk = NULL; char *dir, *save = NULL, *saveLnk = NULL;
char *pidstr = NULL; char pidstr[10];
size_t l = 0, ll; size_t l = 0, ll;
const char *file; const char *file;
struct stat stbuf; struct stat stbuf;
@@ -1145,14 +1134,12 @@ int main(int argc, char **argv)
* This works fine, provided that we ONLY allow multiplexed services * This works fine, provided that we ONLY allow multiplexed services
* to exist in the same directory as the master link. * to exist in the same directory as the master link.
* Also, the master link as to be a real file in the init dir. */ * Also, the master link as to be a real file in the init dir. */
path = realpath(argv[1], NULL); if (!realpath(argv[1], path)) {
if (!path) {
fprintf(stderr, "realpath: %s\n", strerror(errno)); fprintf(stderr, "realpath: %s\n", strerror(errno));
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
lnk = xmalloc(4096); memset(lnk, 0, sizeof(lnk));
memset(lnk, 0, 4096); if (readlink(argv[1], lnk, sizeof(lnk)-1)) {
if (readlink(argv[1], lnk, 4096)) {
dir = dirname(path); dir = dirname(path);
if (strchr(lnk, '/')) { if (strchr(lnk, '/')) {
save = xstrdup(dir); save = xstrdup(dir);
@@ -1166,7 +1153,8 @@ int main(int argc, char **argv)
} else } else
file = basename_c(argv[1]); file = basename_c(argv[1]);
ll = strlen(dir) + strlen(file) + 2; ll = strlen(dir) + strlen(file) + 2;
xasprintf(&service, "%s/%s", dir, file); service = xmalloc(ll);
snprintf(service, ll, "%s/%s", dir, file);
if (stat(service, &stbuf) != 0) { if (stat(service, &stbuf) != 0) {
free(service); free(service);
service = xstrdup(lnk); service = xstrdup(lnk);
@@ -1174,7 +1162,6 @@ int main(int argc, char **argv)
free(save); free(save);
free(saveLnk); free(saveLnk);
} }
free(lnk);
if (!service) if (!service)
service = xstrdup(path); service = xstrdup(path);
applet = basename_c(service); applet = basename_c(service);
@@ -1198,7 +1185,7 @@ int main(int argc, char **argv)
/* Set an env var so that we always know our pid regardless of any /* 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_* subshells the init script may create so that our mark_service_*
functions can always instruct us of this change */ functions can always instruct us of this change */
xasprintf(&pidstr, "%d", (int) getpid()); snprintf(pidstr, sizeof(pidstr), "%d", (int) getpid());
setenv("RC_OPENRC_PID", pidstr, 1); setenv("RC_OPENRC_PID", pidstr, 1);
/* /*
* RC_RUNSCRIPT_PID is deprecated, but we will keep it for a while * RC_RUNSCRIPT_PID is deprecated, but we will keep it for a while
@@ -1268,9 +1255,6 @@ int main(int argc, char **argv)
case_RC_COMMON_GETOPT 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 /* If we're changing runlevels and not called by rc then we cannot
work with any dependencies */ work with any dependencies */
if (deps && getenv("RC_PID") == NULL && if (deps && getenv("RC_PID") == NULL &&
@@ -1285,8 +1269,6 @@ int main(int argc, char **argv)
unsetenv("IN_BACKGROUND"); unsetenv("IN_BACKGROUND");
} }
if (rc_yesno(getenv("IN_DRYRUN")))
dry_run = true;
if (rc_yesno(getenv("IN_HOTPLUG"))) { if (rc_yesno(getenv("IN_HOTPLUG"))) {
if (!service_plugable()) if (!service_plugable())
eerrorx("%s: not allowed to be hotplugged", applet); eerrorx("%s: not allowed to be hotplugged", applet);

View File

@@ -87,8 +87,6 @@ write_log(int logfd, const char *buffer, size_t bytes)
} }
if (!in_escape) { if (!in_escape) {
if (!isprint((int) *p) && *p != '\n')
goto cont;
if (write(logfd, p++, 1) == -1) if (write(logfd, p++, 1) == -1)
eerror("write: %s", strerror(errno)); eerror("write: %s", strerror(errno));
continue; continue;

View File

@@ -51,8 +51,7 @@ rc_conf_yesno(const char *setting)
static const char *const env_whitelist[] = { static const char *const env_whitelist[] = {
"EERROR_QUIET", "EINFO_QUIET", "EERROR_QUIET", "EINFO_QUIET",
"IN_BACKGROUND", "IN_DRYRUN", "IN_HOTPLUG", "IN_BACKGROUND", "IN_HOTPLUG",
"RC_DEBUG", "RC_NODEPS",
"LANG", "LC_MESSAGES", "TERM", "LANG", "LC_MESSAGES", "TERM",
"EINFO_COLOR", "EINFO_VERBOSE", "EINFO_COLOR", "EINFO_VERBOSE",
NULL NULL
@@ -132,8 +131,7 @@ env_config(void)
char *npp; char *npp;
char *tok; char *tok;
const char *sys = rc_sys(); const char *sys = rc_sys();
char *buffer = NULL; char buffer[PATH_MAX];
size_t size = 0;
/* Ensure our PATH is prefixed with the system locations first /* Ensure our PATH is prefixed with the system locations first
for a little extra security */ for a little extra security */
@@ -172,7 +170,8 @@ env_config(void)
free(e); free(e);
if ((fp = fopen(RC_KRUNLEVEL, "r"))) { if ((fp = fopen(RC_KRUNLEVEL, "r"))) {
if (getline(&buffer, &size, fp) != -1) { memset(buffer, 0, sizeof (buffer));
if (fgets(buffer, sizeof (buffer), fp)) {
l = strlen (buffer) - 1; l = strlen (buffer) - 1;
if (buffer[l] == '\n') if (buffer[l] == '\n')
buffer[l] = 0; buffer[l] = 0;
@@ -182,7 +181,6 @@ env_config(void)
} else } else
setenv("RC_DEFAULTLEVEL", RC_LEVEL_DEFAULT, 1); setenv("RC_DEFAULTLEVEL", RC_LEVEL_DEFAULT, 1);
free(buffer);
if (sys) if (sys)
setenv("RC_SYS", sys, 1); setenv("RC_SYS", sys, 1);
@@ -219,27 +217,14 @@ 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)
{ {
char *file = NULL; char file[PATH_MAX];
int fd; int fd;
xasprintf(&file, RC_SVCDIR "/exclusive/%s", applet); snprintf(file, sizeof(file), RC_SVCDIR "/exclusive/%s", applet);
fd = open(file, O_WRONLY | O_CREAT | O_NONBLOCK, 0664); fd = open(file, O_WRONLY | O_CREAT | O_NONBLOCK, 0664);
free(file);
if (fd == -1) if (fd == -1)
return -1; return -1;
if (flock(fd, LOCK_EX | LOCK_NB) == -1) { if (flock(fd, LOCK_EX | LOCK_NB) == -1) {
@@ -253,12 +238,11 @@ svc_lock(const char *applet)
int int
svc_unlock(const char *applet, int fd) svc_unlock(const char *applet, int fd)
{ {
char *file = NULL; char file[PATH_MAX];
xasprintf(&file, RC_SVCDIR "/exclusive/%s", applet); snprintf(file, sizeof(file), RC_SVCDIR "/exclusive/%s", applet);
close(fd); close(fd);
unlink(file); unlink(file);
free(file);
return -1; return -1;
} }
@@ -362,7 +346,7 @@ RC_DEPTREE * _rc_deptree_load(int force, int *regen)
int serrno = errno; int serrno = errno;
int merrno; int merrno;
time_t t; time_t t;
char *file = NULL; char file[PATH_MAX];
struct stat st; struct stat st;
struct utimbuf ut; struct utimbuf ut;
FILE *fp; FILE *fp;
@@ -384,10 +368,7 @@ RC_DEPTREE * _rc_deptree_load(int force, int *regen)
eend (retval, "Failed to update the dependency tree"); eend (retval, "Failed to update the dependency tree");
if (retval == 0) { if (retval == 0) {
if (stat(RC_DEPTREE_CACHE, &st) != 0) { stat(RC_DEPTREE_CACHE, &st);
eerror("stat(%s): %s", RC_DEPTREE_CACHE, strerror(errno));
return NULL;
}
if (st.st_mtime < t) { if (st.st_mtime < t) {
eerror("Clock skew detected with `%s'", file); eerror("Clock skew detected with `%s'", file);
eerrorn("Adjusting mtime of `" RC_DEPTREE_CACHE eerrorn("Adjusting mtime of `" RC_DEPTREE_CACHE
@@ -411,6 +392,34 @@ RC_DEPTREE * _rc_deptree_load(int force, int *regen)
return rc_deptree_load(); return rc_deptree_load();
} }
bool _rc_can_find_pids(void)
{
RC_PIDLIST *pids;
RC_PID *pid;
RC_PID *pid2;
bool retval = false;
if (geteuid() == 0)
return true;
/* If we cannot see process 1, then we don't test to see if
* services crashed or not */
pids = rc_find_pids(NULL, NULL, 0, 1);
if (pids) {
pid = LIST_FIRST(pids);
if (pid) {
retval = true;
while (pid) {
pid2 = LIST_NEXT(pid, entries);
free(pid);
pid = pid2;
}
}
free(pids);
}
return retval;
}
static const struct { static const struct {
const char * const name; const char * const name;
RC_SERVICE bit; RC_SERVICE bit;
@@ -423,7 +432,6 @@ static const struct {
{ "service_hotplugged", RC_SERVICE_HOTPLUGGED, }, { "service_hotplugged", RC_SERVICE_HOTPLUGGED, },
{ "service_wasinactive", RC_SERVICE_WASINACTIVE, }, { "service_wasinactive", RC_SERVICE_WASINACTIVE, },
{ "service_failed", RC_SERVICE_FAILED, }, { "service_failed", RC_SERVICE_FAILED, },
{ "service_crashed", RC_SERVICE_CRASHED, },
}; };
RC_SERVICE lookup_service_state(const char *service) RC_SERVICE lookup_service_state(const char *service)
@@ -466,27 +474,3 @@ 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;
}

View File

@@ -1,57 +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/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.
*/
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#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)
return -1;
else if (pid > 0) {
/* parent */
close(pfd[0]);
return pfd[pipe_write_end];
} else if (pid == 0) {
/* child */
close(pfd[pipe_write_end]);
if (pfd[0] != STDIN_FILENO) {
if (dup2(pfd[0], STDIN_FILENO) < 0)
exit(1);
close(pfd[0]);
}
execl("/bin/sh", "sh", "-c", cmd, NULL);
exit(1);
}
}

View File

@@ -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/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_PIPES_H
#define RC_PIPES_H
int rc_pipe_command(char *cmd);
#endif

View File

@@ -68,7 +68,7 @@ rc_plugin_load(void)
DIR *dp; DIR *dp;
struct dirent *d; struct dirent *d;
PLUGIN *plugin; PLUGIN *plugin;
char *file = NULL; char file[PATH_MAX];
void *h; void *h;
int (*fptr)(RC_HOOK, const char *); int (*fptr)(RC_HOOK, const char *);
@@ -85,9 +85,8 @@ rc_plugin_load(void)
if (d->d_name[0] == '.') if (d->d_name[0] == '.')
continue; continue;
xasprintf(&file, RC_PLUGINDIR "/%s", d->d_name); snprintf(file, sizeof(file), RC_PLUGINDIR "/%s", d->d_name);
h = dlopen(file, RTLD_LAZY); h = dlopen(file, RTLD_LAZY);
free(file);
if (h == NULL) { if (h == NULL) {
eerror("dlopen: %s", dlerror()); eerror("dlopen: %s", dlerror());
continue; continue;

View File

@@ -1,422 +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/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 (!(pid > 0 || exec || uid || (argv && *argv)))
return 0;
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;
}

View File

@@ -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/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,6 +39,7 @@
#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
@@ -256,19 +257,19 @@ static int read_context_file(const char *filename, char **context)
{ {
int ret = -1; int ret = -1;
FILE *fp; FILE *fp;
char *filepath = NULL; char filepath[PATH_MAX];
char *line = NULL; char *line = NULL;
char *p; char *p;
char *p2; char *p2;
size_t len = 0; size_t len = 0;
ssize_t read; ssize_t read;
xasprintf(&filepath, "%s/%s", selinux_contexts_path(), filename); memset(filepath, '\0', PATH_MAX);
snprintf(filepath, PATH_MAX - 1, "%s/%s", selinux_contexts_path(), filename);
fp = fopen(filepath, "r"); fp = fopen(filepath, "r");
if (fp == NULL) { if (fp == NULL) {
eerror("Failed to open context file: %s", filename); eerror("Failed to open context file: %s", filename);
free(filepath);
return -1; return -1;
} }
@@ -294,31 +295,10 @@ static int read_context_file(const char *filename, char **context)
} }
free(line); free(line);
free(filepath);
fclose(fp); fclose(fp);
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;
@@ -332,7 +312,7 @@ void selinux_setup(char **argv)
return; return;
} }
if (read_run_init_context(&run_init_t) != 0) { if (read_context_file(RUN_INIT_FILE, &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);
@@ -359,13 +339,14 @@ void selinux_setup(char **argv)
goto out; goto out;
} }
curr_t = xstrdup(context_type_get(curr_con)); curr_t = 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

@@ -29,33 +29,23 @@
const char *applet = NULL; const char *applet = NULL;
const char *extraopts = NULL; const char *extraopts = NULL;
const char *getoptstring = "cdDe:ilr:INsSZ" getoptstring_COMMON; const char *getoptstring = "e:ilr:IN" getoptstring_COMMON;
const struct option longopts[] = { const struct option longopts[] = {
{ "debug", 0, NULL, 'd' },
{ "nodeps", 0, NULL, 'D' },
{ "exists", 1, NULL, 'e' }, { "exists", 1, NULL, 'e' },
{ "ifcrashed", 0, NULL, 'c' },
{ "ifexists", 0, NULL, 'i' }, { "ifexists", 0, NULL, 'i' },
{ "ifinactive", 0, NULL, 'I' }, { "ifinactive", 0, NULL, 'I' },
{ "ifnotstarted", 0, NULL, 'N' }, { "ifnotstarted", 0, NULL, 'N' },
{ "ifstarted", 0, NULL, 's' },
{ "ifstopped", 0, NULL, 'S' },
{ "list", 0, NULL, 'l' }, { "list", 0, NULL, 'l' },
{ "resolve", 1, NULL, 'r' }, { "resolve", 1, NULL, 'r' },
{ "dry-run", 0, NULL, 'Z' },
longopts_COMMON longopts_COMMON
}; };
const char * const longopts_help[] = { const char * const longopts_help[] = {
"set xtrace when running the command",
"ignore dependencies",
"tests if the service exists or not", "tests if the service exists or not",
"if the service is crashed then run the command",
"if the service exists then run the command", "if the service exists then run the command",
"if the service is inactive then run the command", "if the service is inactive then run the command",
"if the service is not started then run the command", "if the service is not started then run the command",
"list all available services", "list all available services",
"resolve the service name to an init script", "resolve the service name to an init script",
"dry run (show what would happen)",
longopts_help_COMMON longopts_help_COMMON
}; };
const char *usagestring = "" \ const char *usagestring = "" \
@@ -71,12 +61,9 @@ int main(int argc, char **argv)
RC_STRINGLIST *list; RC_STRINGLIST *list;
RC_STRING *s; RC_STRING *s;
RC_SERVICE state; RC_SERVICE state;
bool if_crashed = false;
bool if_exists = false; bool if_exists = false;
bool if_inactive = false; bool if_inactive = false;
bool if_notstarted = false; bool if_notstarted = false;
bool if_started = false;
bool if_stopped = false;
applet = basename_c(argv[0]); applet = basename_c(argv[0]);
/* Ensure that we are only quiet when explicitly told to be */ /* Ensure that we are only quiet when explicitly told to be */
@@ -86,21 +73,12 @@ int main(int argc, char **argv)
longopts, (int *) 0)) != -1) longopts, (int *) 0)) != -1)
{ {
switch (opt) { switch (opt) {
case 'd':
setenv("RC_DEBUG", "yes", 1);
break;
case 'D':
setenv("RC_NODEPS", "yes", 1);
break;
case 'e': case 'e':
service = rc_service_resolve(optarg); service = rc_service_resolve(optarg);
opt = service ? EXIT_SUCCESS : EXIT_FAILURE; opt = service ? EXIT_SUCCESS : EXIT_FAILURE;
free(service); free(service);
return opt; return opt;
/* NOTREACHED */ /* NOTREACHED */
case 'c':
if_crashed = true;
break;
case 'i': case 'i':
if_exists = true; if_exists = true;
break; break;
@@ -128,15 +106,6 @@ int main(int argc, char **argv)
free(service); free(service);
return EXIT_SUCCESS; return EXIT_SUCCESS;
/* NOTREACHED */ /* 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 case_RC_COMMON_GETOPT
} }
@@ -152,16 +121,10 @@ int main(int argc, char **argv)
eerrorx("%s: service `%s' does not exist", applet, *argv); eerrorx("%s: service `%s' does not exist", applet, *argv);
} }
state = rc_service_state(*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)) if (if_inactive && ! (state & RC_SERVICE_INACTIVE))
return 0; return 0;
if (if_notstarted && (state & RC_SERVICE_STARTED)) if (if_notstarted && (state & RC_SERVICE_STARTED))
return 0; return 0;
if (if_started && ! (state & RC_SERVICE_STARTED))
return 0;
if (if_stopped && ! (state & RC_SERVICE_STOPPED))
return 0;
*argv = service; *argv = service;
execv(*argv, argv); execv(*argv, argv);
eerrorx("%s: %s", applet, strerror(errno)); eerrorx("%s: %s", applet, strerror(errno));

View File

@@ -54,6 +54,7 @@ const char *usagestring = "" \
"Usage: rc-status [options] <runlevel>...\n" \ "Usage: rc-status [options] <runlevel>...\n" \
" or: rc-status [options] [-a | -c | -l | -m | -r | -s | -u]"; " or: rc-status [options] [-a | -c | -l | -m | -r | -s | -u]";
static bool test_crashed = false;
static RC_DEPTREE *deptree; static RC_DEPTREE *deptree;
static RC_STRINGLIST *types; static RC_STRINGLIST *types;
@@ -127,8 +128,6 @@ 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);
@@ -144,16 +143,11 @@ print_service(const char *service)
color = ECOLOR_WARN; color = ECOLOR_WARN;
} else if (state & RC_SERVICE_STARTED) { } else if (state & RC_SERVICE_STARTED) {
errno = 0; errno = 0;
if (rc_service_daemons_crashed(service) && errno != EACCES) if (test_crashed &&
rc_service_daemons_crashed(service) &&
errno != EACCES)
{ {
child_pid = rc_service_value_get(service, "child_pid"); snprintf(status, sizeof(status), " crashed ");
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);
@@ -237,6 +231,8 @@ int main(int argc, char **argv)
char *p, *runlevel = NULL; char *p, *runlevel = NULL;
int opt, retval = 0; int opt, retval = 0;
test_crashed = _rc_can_find_pids();
applet = basename_c(argv[0]); applet = basename_c(argv[0]);
while ((opt = getopt_long(argc, argv, getoptstring, longopts, while ((opt = getopt_long(argc, argv, getoptstring, longopts,
(int *) 0)) != -1) (int *) 0)) != -1)

View File

@@ -62,7 +62,7 @@ add(const char *runlevel, const char *service)
if (!rc_service_exists(service)) { if (!rc_service_exists(service)) {
if (errno == ENOEXEC) if (errno == ENOEXEC)
eerror("%s: service `%s' is not executable", eerror("%s: service `%s' is not executeable",
applet, service); applet, service);
else else
eerror("%s: service `%s' does not exist", eerror("%s: service `%s' does not exist",
@@ -166,7 +166,7 @@ show(RC_STRINGLIST *runlevels, bool verbose)
RC_STRING *runlevel; RC_STRING *runlevel;
RC_STRINGLIST *in; RC_STRINGLIST *in;
bool inone; bool inone;
char *buffer = NULL; char buffer[PATH_MAX];
size_t l; size_t l;
rc_stringlist_sort(&services); rc_stringlist_sort(&services);
@@ -182,11 +182,9 @@ show(RC_STRINGLIST *runlevels, bool verbose)
inone = true; inone = true;
} else { } else {
l = strlen(runlevel->value); l = strlen(runlevel->value);
buffer = xmalloc(l+1);
memset (buffer, ' ', l); memset (buffer, ' ', l);
buffer[l] = 0; buffer[l] = 0;
rc_stringlist_add (in, buffer); rc_stringlist_add (in, buffer);
free(buffer);
} }
} }

View File

@@ -78,6 +78,9 @@ const char *usagestring = "" \
#define INITSH RC_LIBEXECDIR "/sh/init.sh" #define INITSH RC_LIBEXECDIR "/sh/init.sh"
#define INITEARLYSH RC_LIBEXECDIR "/sh/init-early.sh" #define INITEARLYSH RC_LIBEXECDIR "/sh/init-early.sh"
#define SHUTDOWN "/sbin/shutdown"
#define SULOGIN "/sbin/sulogin"
#define INTERACTIVE RC_SVCDIR "/interactive" #define INTERACTIVE RC_SVCDIR "/interactive"
#define DEVBOOT "/dev/.rcboot" #define DEVBOOT "/dev/.rcboot"
@@ -285,8 +288,8 @@ open_shell(void)
/* VSERVER systems cannot really drop to shells */ /* VSERVER systems cannot really drop to shells */
if (sys && strcmp(sys, RC_SYS_VSERVER) == 0) if (sys && strcmp(sys, RC_SYS_VSERVER) == 0)
{ {
execlp("halt", "halt", "-f", (char *) NULL); execl("/sbin/halt", "/sbin/halt", "-f", (char *) NULL);
eerrorx("%s: unable to exec `halt -f': %s", eerrorx("%s: unable to exec `/sbin/halt': %s",
applet, strerror(errno)); applet, strerror(errno));
} }
#endif #endif
@@ -333,26 +336,26 @@ set_krunlevel(const char *level)
return true; return true;
} }
static char *get_krunlevel(void) static size_t
get_krunlevel(char *buffer, int buffer_len)
{ {
char *buffer = NULL;
FILE *fp; FILE *fp;
size_t i = 0; size_t i = 0;
if (!exists(RC_KRUNLEVEL)) if (!exists(RC_KRUNLEVEL))
return NULL; return 0;
if (!(fp = fopen(RC_KRUNLEVEL, "r"))) { if (!(fp = fopen(RC_KRUNLEVEL, "r"))) {
eerror("fopen `%s': %s", RC_KRUNLEVEL, strerror(errno)); eerror("fopen `%s': %s", RC_KRUNLEVEL, strerror(errno));
return NULL; return 0;
} }
if (getline(&buffer, &i, fp) != -1) { if (fgets(buffer, buffer_len, fp)) {
i = strlen(buffer); i = strlen(buffer);
if (buffer[i - 1] == '\n') if (buffer[i - 1] == '\n')
buffer[i - 1] = 0; buffer[i - 1] = 0;
} }
fclose(fp); fclose(fp);
return buffer; return i;
} }
static void static void
@@ -502,9 +505,6 @@ do_sysinit()
* sys */ * sys */
if ((sys = rc_sys())) if ((sys = rc_sys()))
setenv("RC_SYS", sys, 1); 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 static bool
@@ -743,7 +743,7 @@ int main(int argc, char **argv)
RC_STRING *service; RC_STRING *service;
bool going_down = false; bool going_down = false;
int depoptions = RC_DEP_STRICT | RC_DEP_TRACE; int depoptions = RC_DEP_STRICT | RC_DEP_TRACE;
char *krunlevel = NULL; char krunlevel [PATH_MAX];
char pidstr[10]; char pidstr[10];
int opt; int opt;
bool parallel; bool parallel;
@@ -892,8 +892,7 @@ int main(int argc, char **argv)
(strcmp(newlevel, RC_LEVEL_SYSINIT) != 0 && (strcmp(newlevel, RC_LEVEL_SYSINIT) != 0 &&
strcmp(newlevel, getenv("RC_BOOTLEVEL")) != 0)) strcmp(newlevel, getenv("RC_BOOTLEVEL")) != 0))
{ {
krunlevel = get_krunlevel(); if (get_krunlevel(krunlevel, sizeof(krunlevel))) {
if (krunlevel) {
newlevel = krunlevel; newlevel = krunlevel;
set_krunlevel(NULL); set_krunlevel(NULL);
} }

View File

@@ -19,6 +19,10 @@
* 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>
@@ -59,14 +63,12 @@ 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-pipes.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 = "I:KN:PR:Sa:bc:d:e:g:ik:mn:op:s:tu:r:w:x:1:2:3:4:" \ const char *getoptstring = "I:KN:PR:Sa:bc:d:e:g:ik:mn:op:s:tu:r:w:x:1:2:" \
getoptstring_COMMON; getoptstring_COMMON;
const struct option longopts[] = { const struct option longopts[] = {
{ "ionice", 1, NULL, 'I'}, { "ionice", 1, NULL, 'I'},
@@ -94,8 +96,6 @@ const struct option longopts[] = {
{ "exec", 1, NULL, 'x'}, { "exec", 1, NULL, 'x'},
{ "stdout", 1, NULL, '1'}, { "stdout", 1, NULL, '1'},
{ "stderr", 1, NULL, '2'}, { "stderr", 1, NULL, '2'},
{ "stdout-logger",1, NULL, '3'},
{ "stderr-logger",1, NULL, '4'},
{ "progress", 0, NULL, 'P'}, { "progress", 0, NULL, 'P'},
longopts_COMMON longopts_COMMON
}; };
@@ -125,13 +125,25 @@ const char * const longopts_help[] = {
"Binary to start/stop", "Binary to start/stop",
"Redirect stdout to file", "Redirect stdout to file",
"Redirect stderr to file", "Redirect stderr to file",
"Redirect stdout to process",
"Redirect stderr to process",
"Print dots each second while waiting", "Print dots each second while waiting",
longopts_help_COMMON longopts_help_COMMON
}; };
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;
@@ -154,6 +166,20 @@ 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)
{ {
@@ -162,6 +188,385 @@ 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)
{ {
@@ -259,7 +664,6 @@ int main(int argc, char **argv)
#endif #endif
int opt; int opt;
size_t size = 0;
bool start = false; bool start = false;
bool stop = false; bool stop = false;
bool oknodo = false; bool oknodo = false;
@@ -281,13 +685,10 @@ int main(int argc, char **argv)
int tid = 0; int tid = 0;
char *redirect_stderr = NULL; char *redirect_stderr = NULL;
char *redirect_stdout = NULL; char *redirect_stdout = NULL;
char *stderr_process = NULL;
char *stdout_process = NULL;
int stdin_fd; int stdin_fd;
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;
@@ -295,10 +696,10 @@ int main(int argc, char **argv)
char *tmp, *newpath, *np; char *tmp, *newpath, *np;
char *p; char *p;
char *token; char *token;
char *exec_file = NULL; char exec_file[PATH_MAX];
struct passwd *pw; struct passwd *pw;
struct group *gr; struct group *gr;
char *line = NULL; char line[130];
FILE *fp; FILE *fp;
size_t len; size_t len;
mode_t numask = 022; mode_t numask = 022;
@@ -306,6 +707,7 @@ 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);
@@ -474,7 +876,7 @@ int main(int argc, char **argv)
break; break;
case 's': /* --signal <signal> */ case 's': /* --signal <signal> */
sig = parse_signal(applet, optarg); sig = parse_signal(optarg);
break; break;
case 't': /* --test */ case 't': /* --test */
@@ -507,14 +909,6 @@ int main(int argc, char **argv)
redirect_stderr = optarg; redirect_stderr = optarg;
break; 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_RC_COMMON_GETOPT case_RC_COMMON_GETOPT
} }
@@ -566,9 +960,6 @@ int main(int argc, char **argv)
if (redirect_stdout || redirect_stderr) if (redirect_stdout || redirect_stderr)
eerrorx("%s: --stdout and --stderr are only relevant" eerrorx("%s: --stdout and --stderr are only relevant"
" with --start", applet); " with --start", applet);
if (stdout_process || stderr_process)
eerrorx("%s: --stdout-logger and --stderr-logger are only relevant"
" with --start", applet);
if (start_wait) if (start_wait)
ewarn("using --wait with --stop has no effect," ewarn("using --wait with --stop has no effect,"
" use --retry instead"); " use --retry instead");
@@ -581,15 +972,6 @@ int main(int argc, char **argv)
if ((redirect_stdout || redirect_stderr) && !background) if ((redirect_stdout || redirect_stderr) && !background)
eerrorx("%s: --stdout and --stderr are only relevant" eerrorx("%s: --stdout and --stderr are only relevant"
" with --background", applet); " 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 ~ */ /* Expand ~ */
@@ -605,22 +987,26 @@ int main(int argc, char **argv)
if (*exec == '/' || *exec == '.') { if (*exec == '/' || *exec == '.') {
/* Full or relative path */ /* Full or relative path */
if (ch_root) if (ch_root)
xasprintf(&exec_file, "%s/%s", ch_root, exec); snprintf(exec_file, sizeof(exec_file),
"%s/%s", ch_root, exec);
else else
xasprintf(&exec_file, "%s", exec); snprintf(exec_file, sizeof(exec_file),
"%s", exec);
} else { } else {
/* Something in $PATH */ /* Something in $PATH */
p = tmp = xstrdup(getenv("PATH")); p = tmp = xstrdup(getenv("PATH"));
exec_file = NULL; *exec_file = '\0';
while ((token = strsep(&p, ":"))) { while ((token = strsep(&p, ":"))) {
if (ch_root) if (ch_root)
xasprintf(&exec_file, "%s/%s/%s", ch_root, token, exec); snprintf(exec_file, sizeof(exec_file),
"%s/%s/%s",
ch_root, token, exec);
else else
xasprintf(&exec_file, "%s/%s", token, exec); snprintf(exec_file, sizeof(exec_file),
if (exec_file && exists(exec_file)) "%s/%s", token, exec);
if (exists(exec_file))
break; break;
free(exec_file); *exec_file = '\0';
exec_file = NULL;
} }
free(tmp); free(tmp);
} }
@@ -628,8 +1014,8 @@ int main(int argc, char **argv)
if (start && !exists(exec_file)) { if (start && !exists(exec_file)) {
eerror("%s: %s does not exist", applet, eerror("%s: %s does not exist", applet,
*exec_file ? exec_file : exec); *exec_file ? exec_file : exec);
free(exec_file);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
if (start && retry) if (start && retry)
ewarn("using --retry with --start has no effect," ewarn("using --retry with --start has no effect,"
@@ -641,10 +1027,7 @@ int main(int argc, char **argv)
if (interpreted && !pidfile) { if (interpreted && !pidfile) {
fp = fopen(exec_file, "r"); fp = fopen(exec_file, "r");
if (fp) { if (fp) {
line = NULL; p = fgets(line, sizeof(line), fp);
if (getline(&line, &size, fp) == -1)
eerrorx("%s: %s", applet, strerror(errno));
p = line;
fclose(fp); fclose(fp);
if (p != NULL && line[0] == '#' && line[1] == '!') { if (p != NULL && line[0] == '#' && line[1] == '!') {
p = line + 2; p = line + 2;
@@ -656,8 +1039,7 @@ int main(int argc, char **argv)
if (p[len] == '\n') if (p[len] == '\n')
p[len] = '\0'; p[len] = '\0';
token = strsep(&p, " "); token = strsep(&p, " ");
free(exec_file); strncpy(exec_file, token, sizeof(exec_file));
xasprintf(&exec_file, "%s", token);
opt = 0; opt = 0;
for (nav = argv; *nav; nav++) for (nav = argv; *nav; nav++)
opt++; opt++;
@@ -680,20 +1062,13 @@ int main(int argc, char **argv)
if (!stop) if (!stop)
oknodo = true; oknodo = true;
if (retry) if (retry)
parse_schedule(applet, retry, sig); parse_schedule(retry, sig);
else if (test || oknodo) else if (test || oknodo)
parse_schedule(applet, "0", sig); parse_schedule("0", sig);
else else
parse_schedule(applet, NULL, sig); parse_schedule(NULL, sig);
if (pidfile) { i = run_stop_schedule(exec, (const char *const *)margv,
pid = get_pid(applet, pidfile); pidfile, uid, test, progress);
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) if (i < 0)
/* We failed to stop something */ /* We failed to stop something */
@@ -715,18 +1090,14 @@ int main(int argc, char **argv)
} }
if (pidfile) if (pidfile)
pid = get_pid(applet, pidfile); pid = get_pid(pidfile);
else else
pid = 0; pid = 0;
if (pid) if (do_stop(exec, (const char * const *)margv, pid, uid,
pids = rc_find_pids(NULL, NULL, 0, pid); 0, test) > 0)
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);
@@ -912,12 +1283,6 @@ int main(int argc, char **argv)
eerrorx("%s: unable to open the logfile" eerrorx("%s: unable to open the logfile"
" for stdout `%s': %s", " for stdout `%s': %s",
applet, redirect_stdout, strerror(errno)); 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 (redirect_stderr) {
if ((stderr_fd = open(redirect_stderr, if ((stderr_fd = open(redirect_stderr,
@@ -926,21 +1291,13 @@ int main(int argc, char **argv)
eerrorx("%s: unable to open the logfile" eerrorx("%s: unable to open the logfile"
" for stderr `%s': %s", " for stderr `%s': %s",
applet, redirect_stderr, strerror(errno)); 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) if (background)
dup2(stdin_fd, STDIN_FILENO); dup2(stdin_fd, STDIN_FILENO);
if (background || redirect_stdout || stdout_process if (background || redirect_stdout || rc_yesno(getenv("EINFO_QUIET")))
|| rc_yesno(getenv("EINFO_QUIET")))
dup2(stdout_fd, STDOUT_FILENO); dup2(stdout_fd, STDOUT_FILENO);
if (background || redirect_stderr || stderr_process if (background || redirect_stderr || rc_yesno(getenv("EINFO_QUIET")))
|| rc_yesno(getenv("EINFO_QUIET")))
dup2(stderr_fd, STDERR_FILENO); dup2(stderr_fd, STDERR_FILENO);
for (i = getdtablesize() - 1; i >= 3; --i) for (i = getdtablesize() - 1; i >= 3; --i)
@@ -1008,7 +1365,7 @@ int main(int argc, char **argv)
alive = true; alive = true;
} else { } else {
if (pidfile) { if (pidfile) {
pid = get_pid(applet, pidfile); pid = get_pid(pidfile);
if (pid == -1) { if (pid == -1) {
eerrorx("%s: did not " eerrorx("%s: did not "
"create a valid" "create a valid"
@@ -1017,8 +1374,8 @@ int main(int argc, char **argv)
} }
} else } else
pid = 0; pid = 0;
if (do_stop(applet, exec, (const char *const *)margv, if (do_stop(exec, (const char *const *)margv,
pid, uid, 0, test, false) > 0) pid, uid, 0, test) > 0)
alive = true; alive = true;
} }

View File

@@ -61,13 +61,12 @@ 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:r:Su:1:2:3" \ const char *getoptstring = "D:d:e:g:I:Kk:m:N:p:r:Su:1:2:" \
getoptstring_COMMON; getoptstring_COMMON;
const struct option longopts[] = { const struct option longopts[] = {
{ "respawn-delay", 1, NULL, 'D'}, { "respawn-delay", 1, NULL, 'D'},
@@ -81,13 +80,11 @@ 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[] = {
@@ -102,13 +99,11 @@ 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;
@@ -129,13 +124,6 @@ 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;
@@ -159,89 +147,32 @@ static void cleanup(void)
free(changeuser); free(changeuser);
} }
static void re_exec_supervisor(void) static pid_t get_pid(const char *pidfile)
{ {
syslog(LOG_WARNING, "Re-executing for %s", svcname); FILE *fp;
execlp("supervise-daemon", "supervise-daemon", svcname, "--reexec", pid_t pid;
(char *) NULL);
syslog(LOG_ERR, "Unable to execute supervise-daemon: %s",
strerror(errno));
exit(EXIT_FAILURE);
}
static void handle_signal(int sig) if (! pidfile)
{ return -1;
int serrno = errno;
syslog(LOG_WARNING, "caught signal %d", sig); if ((fp = fopen(pidfile, "r")) == NULL) {
ewarnv("%s: fopen `%s': %s", applet, pidfile, strerror(errno));
if (sig == SIGTERM) return -1;
exiting = true;
/* Restore errno */
errno = serrno;
if (! exiting)
re_exec_supervisor();
}
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; if (fscanf(fp, "%d", &pid) != 1) {
nh = xmalloc(len); ewarnv("%s: no pid found in `%s'", applet, pidfile);
snprintf(nh, len, "%s%s", home, ppath); fclose(fp);
free(opath); return -1;
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;
fclose(fp);
return pid;
} }
static void child_process(char *exec, char **argv) static void child_process(char *exec, char **argv, char *svcname,
int start_count)
{ {
RC_STRINGLIST *env_list; RC_STRINGLIST *env_list;
RC_STRING *env; RC_STRING *env;
@@ -251,7 +182,8 @@ static void child_process(char *exec, char **argv)
size_t len; size_t len;
char *newpath; char *newpath;
char *np; char *np;
char *cmdline = NULL; char **c;
char cmdline[PATH_MAX];
time_t start_time; time_t start_time;
char start_count_string[20]; char start_count_string[20];
char start_time_string[20]; char start_time_string[20];
@@ -265,13 +197,11 @@ static void child_process(char *exec, char **argv)
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", respawn_count); sprintf(start_count_string, "%i", start_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) {
@@ -411,9 +341,15 @@ static void child_process(char *exec, char **argv)
for (i = getdtablesize() - 1; i >= 3; --i) for (i = getdtablesize() - 1; i >= 3; --i)
fcntl(i, F_SETFD, FD_CLOEXEC); fcntl(i, F_SETFD, FD_CLOEXEC);
cmdline = make_cmdline(argv);
syslog(LOG_INFO, "Child command line: %s", cmdline); *cmdline = '\0';
free(cmdline); c = argv;
while (*c) {
strcat(cmdline, *c);
strcat(cmdline, " ");
c++;
}
syslog(LOG_INFO, "Running command line: %s", cmdline);
execvp(exec, argv); execvp(exec, argv);
#ifdef HAVE_PAM #ifdef HAVE_PAM
@@ -423,166 +359,109 @@ static void child_process(char *exec, char **argv)
eerrorx("%s: failed to exec `%s': %s", applet, exec,strerror(errno)); eerrorx("%s: failed to exec `%s': %s", applet, exec,strerror(errno));
} }
static void supervisor(char *exec, char **argv) static void handle_signal(int sig)
{ {
FILE *fp; int serrno = errno;
int i; char signame[10] = { '\0' };
int nkilled;
time_t respawn_now= 0;
time_t first_spawn= 0;
#ifndef RC_DEBUG switch (sig) {
signal_setup_restart(SIGHUP, handle_signal); case SIGINT:
signal_setup_restart(SIGINT, handle_signal); snprintf(signame, sizeof(signame), "SIGINT");
signal_setup_restart(SIGQUIT, handle_signal); break;
signal_setup_restart(SIGILL, handle_signal); case SIGTERM:
signal_setup_restart(SIGABRT, handle_signal); snprintf(signame, sizeof(signame), "SIGTERM");
signal_setup_restart(SIGFPE, handle_signal); break;
signal_setup_restart(SIGSEGV, handle_signal); case SIGQUIT:
signal_setup_restart(SIGPIPE, handle_signal); snprintf(signame, sizeof(signame), "SIGQUIT");
signal_setup_restart(SIGALRM, handle_signal); break;
signal_setup(SIGTERM, handle_signal);
signal_setup_restart(SIGUSR1, handle_signal);
signal_setup_restart(SIGUSR2, handle_signal);
signal_setup_restart(SIGBUS, handle_signal);
#ifdef SIGPOLL
signal_setup_restart(SIGPOLL, handle_signal);
#endif
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);
#ifdef SIGPWR
signal_setup_restart(SIGPWR, handle_signal);
#endif
#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 (pidfile && exists(pidfile)) if (*signame != 0) {
unlink(pidfile); syslog(LOG_INFO, "%s: caught signal %s, exiting", applet, signame);
if (svcname) { exiting = true;
rc_service_daemon_set(svcname, exec, (const char *const *)argv, } else
pidfile, false); syslog(LOG_INFO, "%s: caught unknown signal %d", applet, sig);
rc_service_mark(svcname, RC_SERVICE_STOPPED);
rc_service_value_set(svcname, "child_pid", NULL); /* Restore errno */
errno = serrno;
}
static char * expand_home(const char *home, const char *path)
{
char *opath, *ppath, *p, *nh;
size_t len;
struct passwd *pw;
if (!path || *path != '~')
return xstrdup(path);
opath = ppath = xstrdup(path);
if (ppath[1] != '/' && ppath[1] != '\0') {
p = strchr(ppath + 1, '/');
if (p)
*p = '\0';
pw = getpwnam(ppath + 1);
if (pw) {
home = pw->pw_dir;
ppath = p;
if (ppath)
*ppath = '/';
} else
home = NULL;
} else
ppath++;
if (!home) {
free(opath);
return xstrdup(path);
} }
exit(EXIT_SUCCESS); 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;
} }
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 *retry = NULL; char *pidfile = NULL;
int sig = SIGTERM;
char *home = NULL; char *home = NULL;
int tid = 0; int tid = 0;
pid_t pid; pid_t child_pid, 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 = NULL; char exec_file[PATH_MAX];
char *varbuf = NULL; int respawn_count = 0;
struct timespec ts; int respawn_delay = 0;
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 = NULL;
applet = basename_c(argv[0]); applet = basename_c(argv[0]);
atexit(cleanup); 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 && svcname && strcmp(argv[1], svcname)) signal_setup(SIGINT, handle_signal);
eerrorx("%s: the first argument is %s and must be %s", signal_setup(SIGQUIT, handle_signal);
applet, argv[1], svcname); signal_setup(SIGTERM, handle_signal);
openlog(applet, LOG_PID, LOG_DAEMON);
if ((tmp = getenv("SSD_NICELEVEL"))) if ((tmp = getenv("SSD_NICELEVEL")))
if (sscanf(tmp, "%d", &nicelevel) != 1) if (sscanf(tmp, "%d", &nicelevel) != 1)
@@ -604,11 +483,6 @@ int main(int argc, char **argv)
} }
} }
cmdline = make_cmdline(argv);
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) {
@@ -641,8 +515,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_period < 1) if (n != 1 || respawn_delay < 1)
eerrorx("Invalid respawn-period value '%s'", optarg); eerrorx("Invalid respawn-delay value '%s'", optarg);
break; break;
case 'S': /* --start */ case 'S': /* --start */
@@ -684,9 +558,6 @@ 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;
@@ -738,182 +609,78 @@ 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 && !reexec) if (!pidfile)
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);
umask(numask); /* Validate that the binary exists if we are starting */
if (*exec == '/' || *exec == '.') {
if (reexec) { /* Full or relative path */
str = rc_service_value_get(svcname, "argc"); if (ch_root)
sscanf(str, "%d", &child_argc); snprintf(exec_file, sizeof(exec_file),
child_argv = xmalloc((child_argc + 1) * sizeof(char *)); "%s/%s", ch_root, exec);
memset(child_argv, 0, (child_argc + 1) * sizeof(char *)); else
for (x = 0; x < child_argc; x++) { snprintf(exec_file, sizeof(exec_file),
xasprintf(&varbuf, "argv_%d", x); "%s", exec);
str = rc_service_value_get(svcname, varbuf); } else {
child_argv[x] = str; /* Something in $PATH */
free(varbuf); p = tmp = xstrdup(getenv("PATH"));
varbuf = NULL; *exec_file = '\0';
} while ((token = strsep(&p, ":"))) {
free(str);
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)
xasprintf(&exec_file, "%s/%s", ch_root, exec); snprintf(exec_file, sizeof(exec_file),
"%s/%s/%s",
ch_root, token, exec);
else else
xasprintf(&exec_file, "%s", exec); snprintf(exec_file, sizeof(exec_file),
} else { "%s/%s", token, exec);
/* Something in $PATH */ if (exists(exec_file))
p = tmp = xstrdup(getenv("PATH")); break;
exec_file = NULL; *exec_file = '\0';
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 (!exists(exec_file)) {
eerror("%s: %s does not exist", applet,
*exec_file ? exec_file : exec);
free(exec_file);
exit(EXIT_FAILURE);
}
} else
eerrorx("%s: nothing to start", applet);
pid = get_pid(applet, pidfile);
if (pid != -1)
if (do_stop(applet, exec, (const char * const *)argv, pid, uid,
0, false, true) > 0)
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);
free(cmdline);
cmdline = NULL;
/* 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);
varbuf = NULL;
xasprintf(&varbuf, "%i", respawn_delay);
rc_service_value_set(svcname, "respawn_delay", varbuf);
xasprintf(&varbuf, "%i", respawn_max);
rc_service_value_set(svcname, "respawn_max", varbuf);
xasprintf(&varbuf, "%i", respawn_period);
rc_service_value_set(svcname, "respawn_period", varbuf);
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) {
varbuf = NULL;
xasprintf(&varbuf, "argv_%-d",x);
rc_service_value_set(svcname, varbuf, *c);
free(varbuf);
varbuf = NULL;
x++;
c++;
}
xasprintf(&varbuf, "%d", x);
rc_service_value_set(svcname, "argc", varbuf);
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);
if (i != 0)
/* We failed to send the signal */
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);
}
} }
free(tmp);
} }
}
if (start && !exists(exec_file))
eerrorx("%s: %s does not exist", applet,
*exec_file ? exec_file : exec);
if (stop) {
pid = get_pid(pidfile);
if (pid == -1)
i = pid;
else
i = kill(pid, SIGTERM);
if (i != 0)
/* We failed to stop something */
exit(EXIT_FAILURE);
/* 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
@@ -929,4 +696,113 @@ 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);
} }

Some files were not shown because too many files have changed in this diff Show More