Introduce rcctl(8), a simple utility for maintaining rc.conf.local(8).
authorajacoutot <ajacoutot@openbsd.org>
Tue, 19 Aug 2014 14:08:20 +0000 (14:08 +0000)
committerajacoutot <ajacoutot@openbsd.org>
Tue, 19 Aug 2014 14:08:20 +0000 (14:08 +0000)
# rcctl
usage: rcctl enable|disable|status|action [service [flags [...]]]

Lots of man page improvement from the usual suspects (jmc@ and schwarze@)
not hooked up yet but committing now so work can continue in-tree
agreed by several

usr.sbin/rcctl/Makefile [new file with mode: 0644]
usr.sbin/rcctl/rcctl.8 [new file with mode: 0644]
usr.sbin/rcctl/rcctl.sh [new file with mode: 0644]

diff --git a/usr.sbin/rcctl/Makefile b/usr.sbin/rcctl/Makefile
new file mode 100644 (file)
index 0000000..53f124c
--- /dev/null
@@ -0,0 +1,11 @@
+#      $OpenBSD: Makefile,v 1.1 2014/08/19 14:08:20 ajacoutot Exp $
+
+MAN=   rcctl.8
+
+SCRIPT=        rcctl.sh
+
+realinstall:
+       ${INSTALL} ${INSTALL_COPY} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+               ${.CURDIR}/${SCRIPT} ${DESTDIR}${BINDIR}/rcctl
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/rcctl/rcctl.8 b/usr.sbin/rcctl/rcctl.8
new file mode 100644 (file)
index 0000000..924204f
--- /dev/null
@@ -0,0 +1,146 @@
+.\"    $OpenBSD: rcctl.8,v 1.1 2014/08/19 14:08:20 ajacoutot Exp $
+.\"
+.\" Copyright (c) 2014 Antoine Jacoutot <ajacoutot@openbsd.org>
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.Dd $Mdocdate: August 19 2014 $
+.Dt RCCTL 8
+.Os
+.Sh NAME
+.Nm rcctl
+.Nd configure and control services
+.Sh SYNOPSIS
+.Nm rcctl
+.Sm off
+.Cm enable | disable | status | Ar action
+.Sm on
+.Op Ar service Op Cm flags Op Ar arguments
+.Sh DESCRIPTION
+.Nm
+is a simple utility for maintaining
+.Xr rc.conf.local 8 .
+It can enable or disable
+.Xr rc 8
+services and get/set their status and flags.
+It can also be used to interact with
+.Xr rc.d 8
+scripts.
+.Pp
+The following
+.Ar actions
+are available.
+.Pp
+.Bl -tag -width disable
+.It Cm enable
+Enable
+.Ar service
+in
+.Xr rc.conf.local 8 .
+Optionally set
+.Ar service Ns _flags
+to the specified
+.Cm flags
+.Ar arguments .
+If
+.Cm flags
+is appended without any
+.Ar arguments ,
+.Ar service Ns _flags
+is reset to its default value.
+.It Cm disable
+Disable
+.Ar service
+in
+.Xr rc.conf.local 8 .
+.It Cm status
+Display the value of
+.Ar service Ns _flags
+or
+.Ar service .
+If none is provided, list all services with their current status and
+flags.
+.It Ar action
+Run the
+.Xr rc.d 8
+.Ar service
+script with the
+.Ar action
+argument.
+.El
+.Pp                 
+These services have no corresponding
+.Xr rc.d 8
+script and have no
+.Cm flags :
+accounting, check_quotas, ipsec, multicast_host, multicast_router,
+pf, spamd_black.
+.Sh EXIT STATUS
+.Nm Ar action
+returns with the exit status of the
+.Xr rc.d 8
+.Ar service
+script.
+.Nm Cm status
+exits with 0 if the service is enabled and 1 if it is not.
+Otherwise, the
+.Nm
+utility exits 0 on success, and >0 if an error occurs.
+.Sh EXAMPLES
+Disable
+.Xr ntpd 8
+and check its status:
+.Pp
+.Bd -literal -offset indent
+# rcctl disable ntpd
+# rcctl status ntpd
+NO
+# echo $?
+1
+.Ed
+.Pp
+Enable and set
+.Xr ntpd 8
+flags:
+.Pp
+.Bd -literal -offset indent
+# rcctl enable ntpd flags -s
+# rcctl status ntpd
+-s
+# echo $?
+0
+.Ed
+.Pp
+Start
+.Xr ntpd 8
+and check that it is running:
+.Pp
+.Bd -literal -offset indent
+# rcctl start ntpd
+ntpd(ok)
+# rcctl check ntpd
+ntpd(ok)
+# echo $?
+0
+.Ed
+.Sh SEE ALSO
+.Xr rc.conf.local 8 ,
+.Xr rc.d 8
+.Sh HISTORY
+.Nm
+first appeared in
+.Ox 5.7 .
+.Sh AUTHORS
+.Nm
+was written by
+.An Antoine Jacoutot Aq Mt ajacoutot@openbsd.org .
diff --git a/usr.sbin/rcctl/rcctl.sh b/usr.sbin/rcctl/rcctl.sh
new file mode 100644 (file)
index 0000000..2f7ede5
--- /dev/null
@@ -0,0 +1,332 @@
+#!/bin/sh
+#
+# Copyright (c) 2014 Antoine Jacoutot <ajacoutot@openbsd.org>
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+# get local functions from rc.subr(8)
+FUNCS_ONLY=1
+. /etc/rc.d/rc.subr
+_rc_parse_conf
+
+usage() {
+       _rc_err "usage: ${0##*/} enable|disable|status|action [service [flags [...]]]"
+}
+
+needs_root()
+{
+       if [ "$(id -u)" -ne 0 ]; then
+               _rc_err "${0##*/} ${1:+$1: }need root privileges"
+       fi
+}
+
+rcconf_edit_begin() {
+       _TMP_RCCONF=$(mktemp -p /etc -t rc.conf.local.XXXXXXXXXX) || exit 1
+       if [ -f /etc/rc.conf.local ]; then
+               cp -p /etc/rc.conf.local ${_TMP_RCCONF} || exit 1
+       else
+               touch /etc/rc.conf.local || exit 1
+       fi
+}
+
+rcconf_edit_end() {
+       mv ${_TMP_RCCONF} /etc/rc.conf.local || exit 1
+       if [ ! -s /etc/rc.conf.local ]; then
+               rm /etc/rc.conf.local || exit 1
+       fi
+}
+
+svc_default_enabled() {
+       local _ret=1
+       local _svc=$1
+       [ -n "${_svc}" ] || return
+
+       # get _defaults_ values only
+       _rc_parse_conf /etc/rc.conf
+
+       svc_is_enabled ${_svc} && _ret=0
+
+       # reparse _all_ values
+       svc_is_base ${_svc} && _rc_parse_conf
+
+       return ${_ret}
+}
+
+svc_get_all()
+{
+       local _i
+
+       (
+               ls -A /etc/rc.d | grep -v rc.subr
+               for _i in ${_allowed_keys[@]}; do
+                       echo ${_i}
+               done | grep -Ev '(nfs_server|savecore_flag|amd_master|pf_rules|ipsec_rules|shlib_dirs|pkg_scripts)'
+       ) | sort
+}
+
+svc_get_flags() {
+       local daemon_flags
+       local _svc=$1
+       [ -n "${_svc}" ] || return
+
+       if svc_is_special ${_svc}; then
+               echo "$(eval echo \${${_svc}})"
+       elif ! svc_is_base ${_svc}; then
+               daemon_flags="$(eval echo \${${_svc}_flags})"
+               if [ -z "${daemon_flags}" ]; then
+                       eval $(grep '^daemon_flags=' /etc/rc.d/${_svc})
+               fi
+               echo ${daemon_flags}
+       else
+               echo "$(eval echo \${${_svc}_flags})"
+       fi
+}
+
+svc_get_status() {
+       local _svc=$1
+
+       if [ -n "${_svc}" ]; then
+               svc_get_flags ${_svc} | sed '/^$/d'
+               svc_is_enabled ${_svc}
+       else
+               for _i in $(svc_get_all); do
+                       printf "%18s" ${_i}
+                       svc_is_enabled ${_i} && echo -n "(enabled)" || echo -n "(disabled)"
+                       echo -n "\tflags="
+                       svc_get_flags ${_i}
+               done
+       fi
+}
+
+svc_is_avail()
+{
+       local _i
+
+       for _i in $(svc_get_all); do
+               if [ ${_i} = "$1" ]; then
+                       return 0
+               fi
+       done
+       return 1
+}
+
+svc_is_base()
+{
+       local _svc=$1
+       [ -n "${_svc}" ] || return
+
+       grep "^start_daemon " /etc/rc | cut -d ' ' -f2- | grep -qw ${_svc}
+}
+
+svc_is_enabled()
+{
+       local _flags _i
+       local _svc=$1
+       [ -n "${_svc}" ] || return
+
+       if svc_is_base ${_svc}; then
+               eval _flags=\${${_svc}_flags}
+               if [ "${_flags}" != "NO" ]; then
+                       return
+               fi
+       elif svc_is_special ${_svc}; then
+               eval _flags=\${${_svc}}
+               if [ "${_flags}" != "NO" ]; then
+                       return
+               fi
+       else
+               echo ${pkg_scripts} | grep -qw ${_svc} && return
+       fi
+
+       return 1
+}
+
+svc_is_special()
+{
+       local _svc=$1
+       [ -n "${_svc}" ] || return
+
+       echo ${_allowed_keys[@]} | grep -qw ${_svc}
+}
+
+append_to_pkg_scripts()
+{
+       local _svc=$1
+       [ -n "${_svc}" ] || return
+
+       svc_is_enabled ${_svc} && return
+
+       rcconf_edit_begin
+       if [ -n "${pkg_scripts}" ]; then
+               grep -v "^pkg_scripts.*=" /etc/rc.conf.local >${_TMP_RCCONF}
+               echo pkg_scripts="${pkg_scripts} ${_svc}" >>${_TMP_RCCONF}
+       else
+               echo pkg_scripts="${_svc}" >>${_TMP_RCCONF}
+       fi
+       rcconf_edit_end
+}
+
+rm_from_pkg_scripts()
+{
+       local _i _pkg_scripts
+       local _svc=$1
+       [ -n "${_svc}" ] || return
+
+       [ -z "${pkg_scripts}" ] && return
+
+       for _i in ${pkg_scripts}; do
+               if [ ${_i} != ${_svc} ]; then
+                       _pkg_scripts="${_pkg_scripts} ${_i}"
+               fi
+       done
+       pkg_scripts=$(printf ' %s' ${_pkg_scripts})
+       pkg_scripts=${_pkg_scripts## }
+
+       rcconf_edit_begin
+       grep -v "^pkg_scripts.*=" /etc/rc.conf.local >${_TMP_RCCONF}
+       if [ -n "${pkg_scripts}" ]; then
+               echo pkg_scripts="${pkg_scripts}" >>${_TMP_RCCONF}
+       fi
+       rcconf_edit_end
+}
+
+add_flags()
+{
+       local _flags _numargs=$#
+       local _svc=$2
+       [ -n "${_svc}" ] || return
+
+       # svc is already enabled and we did not (re)set the flags
+       #if svc_is_enabled "${_svc}" && test -z "$3"; then
+       #       return
+       #fi
+
+       #if [ -n "$3" -a "$3" = "flags" -a -n "$4" ]; then
+       if [ -n "$3" -a "$3" = "flags" ]; then
+               if [ -n "$4" ]; then
+                       while [ "${_numargs}" -ge 4 ]
+                       do
+                               eval _flags=\"\$${_numargs} ${_flags}\"
+                               let _numargs--
+                       done
+                       set -A _flags -- ${_flags}
+               fi
+       elif svc_is_base ${_svc}; then
+               # base svc: save current flags because they are reset below
+               set -A _flags --  $(eval echo \${${_svc}_flags})
+       fi
+
+       # special var
+       if svc_is_special ${_svc}; then
+               rcconf_edit_begin
+               grep -v "^${_svc}.*=" /etc/rc.conf.local >${_TMP_RCCONF}
+               if ! svc_default_enabled ${_svc}; then
+                       echo "${_svc}=YES" >>${_TMP_RCCONF}
+               fi
+               rcconf_edit_end
+               return
+       fi
+
+       # base-system script
+       if svc_is_base ${_svc}; then
+               rcconf_edit_begin
+               grep -v "^${_svc}_flags.*=" /etc/rc.conf.local >${_TMP_RCCONF}
+               if ! svc_default_enabled ${_svc} || test "${#_flags[*]}" -gt 0; then
+                       echo ${_svc}_flags=${_flags[@]} >>${_TMP_RCCONF}
+               fi
+               rcconf_edit_end
+               return
+       fi
+
+       # pkg script
+       if [ -n "$3" -a "$3" = "flags" ]; then
+               rcconf_edit_begin
+               grep -v "^${_svc}_flags.*=" /etc/rc.conf.local >${_TMP_RCCONF}
+               if [ "${#_flags[*]}" -gt 0 ]; then
+                       echo ${_svc}_flags=${_flags[@]} >>${_TMP_RCCONF}
+               fi
+               rcconf_edit_end
+       fi
+}
+
+rm_flags()
+{
+       local _svc=$1
+       [ -n "${_svc}" ] || return
+
+       rcconf_edit_begin
+       if svc_is_special ${_svc}; then
+               grep -v "^${_svc}.*=" /etc/rc.conf.local >${_TMP_RCCONF}
+               if svc_default_enabled ${_svc}; then
+                       echo "${_svc}=NO" >>${_TMP_RCCONF}
+               fi
+       else
+               grep -v "^${_svc}_flags.*=" /etc/rc.conf.local >${_TMP_RCCONF}
+               if svc_default_enabled ${_svc}; then
+                       echo "${_svc}_flags=NO" >>${_TMP_RCCONF}
+               fi
+       fi
+       rcconf_edit_end
+}
+
+if [ $# -gt 0 ]; then
+       if [ -n "$2" ]; then
+               if ! svc_is_avail $2; then
+                       _rc_err "service $2 does not exist"
+               fi
+       elif [ "$1" != "status" ]; then
+               usage
+       fi
+       if [ -n "$3" ]; then
+               if [ "$3" = "flags" ]; then
+                       if [ "$1" != "enable" ]; then
+                               _rc_err "\"flags\" can only be set with \"enable\""
+                       fi
+                       if svc_is_special $2; then
+                               _rc_err "\"$2\" is a special variable, cannot set \"flags\""
+                       fi
+               else
+                       usage
+               fi
+       fi
+       case $1 in
+               disable)
+                       needs_root $1
+                       if ! svc_is_base $2 && ! svc_is_special $2; then
+                               rm_from_pkg_scripts $2
+                       fi
+                       rm_flags $2
+                       ;;
+               enable)
+                       needs_root $1
+                       add_flags $*
+                       if ! svc_is_base $2 && ! svc_is_special $2; then
+                               append_to_pkg_scripts $2
+                       fi
+                       ;;
+               status)
+                       svc_get_status $2
+                       ;;
+               start|stop|restart|reload|check)
+                       if svc_is_special $2; then
+                               _rc_err "\"$2\" is a special variable, no rc.d(8) script"
+                       fi
+                       /etc/rc.d/$2 $1
+                       ;;
+               *)
+                       usage
+                       ;;
+       esac
+else
+       usage
+fi