Major rcctl(8) rewrite to simplify it and add new features. It can now
authorajacoutot <ajacoutot@openbsd.org>
Tue, 6 Jan 2015 11:47:50 +0000 (11:47 +0000)
committerajacoutot <ajacoutot@openbsd.org>
Tue, 6 Jan 2015 11:47:50 +0000 (11:47 +0000)
configure "user" and "timeout". Committing now because there's been no
release yet including rcctl(8) so it's easier to modify its usage.

Usage extended (*retaining full backward compatibility for now*) to:
rcctl set|get|getdef foobar [flags|user|timeout|status]

The followings will be dropped soon but not right now to give people
time to adjust:
rcctl enable sshd flags # 'enable' takes no flags, use 'rcctl set ...'
rcctl status sshd # use 'rcctl get sshd [flags|status|timeout|user]'
rcctl default sshd # use 'rcctl getdef sshd [flags|status|timeout|user]'
rcctl status # use 'rcctl getall'

Man page changes will come soon. Make sure you have an up-to-date rc.subr.

discussed with schwarze@ robert@ jasper@ sthen@
"love the idea" jasper@
ok schwarze@

usr.sbin/rcctl/rcctl.sh

index 26aca62..aa45107 100644 (file)
@@ -1,8 +1,8 @@
 #!/bin/sh
 #
-# $OpenBSD: rcctl.sh,v 1.55 2015/01/01 09:44:20 ajacoutot Exp $
+# $OpenBSD: rcctl.sh,v 1.56 2015/01/06 11:47:50 ajacoutot Exp $
 #
-# Copyright (c) 2014 Antoine Jacoutot <ajacoutot@openbsd.org>
+# Copyright (c) 2014, 2015 Antoine Jacoutot <ajacoutot@openbsd.org>
 # Copyright (c) 2014 Ingo Schwarze <schwarze@openbsd.org>
 #
 # Permission to use, copy, modify, and distribute this software for any
@@ -27,8 +27,8 @@ _rc_parse_conf
 
 usage()
 {
-       _rc_err "usage: ${0##*/} [-df] enable|disable|status|default|order|action
-             [service | daemon [flags [arguments]] | daemons]"
+       _rc_err "usage: ${0##*/} [-df] enable|disable|get|set|getdef|getall|order|action
+             [service | daemon [variable [arguments]] | daemons]"
 }
 
 needs_root()
@@ -46,6 +46,57 @@ ls_rcscripts() {
        done
 }
 
+pkg_scripts_append()
+{
+       local _svc=$1
+       [ -n "${_svc}" ] || return
+
+       rcconf_edit_begin
+       if [ -z "${pkg_scripts}" ]; then
+               echo pkg_scripts="${_svc}" >>${_TMP_RCCONF}
+       elif ! echo ${pkg_scripts} | grep -qw -- ${_svc}; then
+               grep -v "^pkg_scripts.*=" /etc/rc.conf.local >${_TMP_RCCONF}
+               echo pkg_scripts="${pkg_scripts} ${_svc}" >>${_TMP_RCCONF}
+       fi
+       rcconf_edit_end
+}
+
+pkg_scripts_order()
+{
+       local _svcs="$*"
+       [ -n "${_svcs}" ] || return
+
+       needs_root ${action}
+       local _pkg_scripts _svc
+       for _svc in ${_svcs}; do
+               if svc_is_base ${_svc} || svc_is_special ${_svc}; then
+                       _rc_err "${0##*/}: ${_svc} is not a pkg script"
+               elif ! svc_get ${_svc} status; then
+                       _rc_err "${0##*/}: ${_svc} is not enabled"
+               fi
+       done
+       _pkg_scripts=$(echo "${_svcs} ${pkg_scripts}" | tr "[:blank:]" "\n" | \
+                    awk -v ORS=' ' '!x[$0]++')
+       rcconf_edit_begin
+       grep -v "^pkg_scripts.*=" /etc/rc.conf.local >${_TMP_RCCONF}
+       echo pkg_scripts=${_pkg_scripts} >>${_TMP_RCCONF}
+       rcconf_edit_end
+}
+
+pkg_scripts_rm()
+{
+       local _svc=$1
+       [ -n "${_svc}" ] || return
+
+       [ -z "${pkg_scripts}" ] && return
+
+       rcconf_edit_begin
+       sed "/^pkg_scripts[[:>:]]/{s/[[:<:]]${_svc}[[:>:]]//g
+           s/['\"]//g;s/ *= */=/;s/   */ /g;s/ $//;/=$/d;}" \
+           /etc/rc.conf.local >${_TMP_RCCONF}
+       rcconf_edit_end
+}
+
 rcconf_edit_begin()
 {
        _TMP_RCCONF=$(mktemp -p /etc -t rc.conf.local.XXXXXXXXXX) || exit 1
@@ -64,19 +115,19 @@ rcconf_edit_end()
        if [ ! -s /etc/rc.conf.local ]; then
                rm /etc/rc.conf.local || exit 1
        fi
+       _rc_parse_conf # reload new values
 }
 
-svc_avail()
+svc_is_avail()
 {
        local _svc=$1
-       [ -n "${_svc}" ] || return 1
+       [ -n "${_svc}" ] || return
 
-       [ "${_svc}" = "rc.subr" ] && return 1
        [ -x "/etc/rc.d/${_svc}" ] && return 0
-       svc_special ${_svc}
+       svc_is_special ${_svc}
 }
 
-svc_base()
+svc_is_base()
 {
        local _svc=$1
        [ -n "${_svc}" ] || return
@@ -84,219 +135,196 @@ svc_base()
        grep "^start_daemon " /etc/rc | cut -d ' ' -f2- | grep -qw -- ${_svc}
 }
 
-svc_enabled()
+svc_is_special()
 {
        local _svc=$1
        [ -n "${_svc}" ] || return
 
-       [ "$(svc_flags ${_svc})" != "NO" ]
+       echo ${_special_services} | grep -qw -- ${_svc}
 }
 
-svc_enabled_default()
+svc_get()
 {
        local _svc=$1
        [ -n "${_svc}" ] || return
-       local _ret=1
-
-       _rc_parse_conf /etc/rc.conf
-       svc_enabled ${_svc} && _ret=0
-       _rc_parse_conf
-
-       return ${_ret}
-}
 
-svc_flags()
-{
-       local _svc=$1
-       [ -n "${_svc}" ] || return
-       local daemon_flags
+       local _status=0 _val _var=$2
+       local daemon_flags daemon_timeout daemon_user
 
-       if svc_special ${_svc}; then
-               echo "$(eval echo \${${_svc}})"
+       if svc_is_special ${_svc}; then
+               daemon_flags="$(eval echo \${${_svc}})"
        else
                # set pkg daemon_flags to "NO" to match base svc
-               if ! svc_base ${_svc}; then
+               if ! svc_is_base ${_svc}; then
                        if ! echo ${pkg_scripts} | grep -qw -- ${_svc}; then
-                               echo "NO" && return
+                               daemon_flags="NO"
                        fi
                fi
+
                [ -z "${daemon_flags}" ] && \
                        daemon_flags="$(eval echo \"\${${_svc}_flags}\")"
                [ -z "${daemon_flags}" ] && \
-                       daemon_flags="$(svc_flags_default ${_svc})"
-
-               [ -n "${daemon_flags}" ] && print -r -- "${daemon_flags}"
+                       daemon_flags="$(svc_getdef ${_svc} flags)"
+               [ -z "${daemon_timeout}" ] && \
+                       daemon_timeout="$(eval echo \"\${${_svc}_timeout}\")"
+               [ -z "${daemon_timeout}" ] && \
+                       daemon_timeout="$(svc_getdef ${_svc} timeout)"
+               [ -z "${daemon_user}" ] && \
+                       daemon_user="$(eval echo \"\${${_svc}_user}\")"
+               [ -z "${daemon_user}" ] && \
+                       daemon_user="$(svc_getdef ${_svc} user)"
        fi
-}
 
-# to prevent namespace pollution, only call in a subshell
-svc_flags_default()
-{
-       local _svc=$1
-       [ -n "${_svc}" ] || return
+       [ "${daemon_flags}" = "NO" ] && _status=1
 
-       if svc_special ${_svc}; then
-               svc_enabled_default ${_svc} && echo "YES" || echo "NO"
+       if [ -n "${_var}" ]; then
+               [ "${_var}" = "status" ] && return ${_status}
+               eval _val=\${daemon_${_var}}
+               [ -z "${_val}" ] || print -r -- "${_val}"
        else
-               rc_cmd() { }
-               . /etc/rc.d/${_svc} >/dev/null 2>&1
-               [ -n "${daemon_flags}" ] && print -r -- "${daemon_flags}"
+               if svc_is_special ${_svc}; then
+                       echo "${_svc}=${daemon_flags}"
+               else
+                       echo "${_svc}_flags=${daemon_flags}"
+                       echo "${_svc}_timeout=${daemon_timeout}"
+                       echo "${_svc}_user=${daemon_user}"
+               fi
+               return ${_status}
        fi
 }
 
-svc_special()
+# to prevent namespace pollution, only call in a subshell
+svc_getdef()
 {
        local _svc=$1
        [ -n "${_svc}" ] || return
 
-       echo ${_special_services} | grep -qw -- ${_svc}
-}
+       local _status=0 _val _var=$2
+       local daemon_flags daemon_timeout daemon_user
 
-svc_status()
-{
-       local _i _svc=$1
-
-       if [ -n "${_svc}" ]; then
-               svc_flags ${_svc}
-               svc_enabled ${_svc}
+       if svc_is_special ${_svc}; then
+               # unconditionally parse: we always output flags and/or status
+               _rc_parse_conf /etc/rc.conf
+               daemon_flags="$(eval echo \${${_svc}})"
+               [ "${daemon_flags}" = "NO" ] && _status=1
        else
-               for _i in $(ls_rcscripts); do
-                       echo "${_i}_flags=$(svc_flags ${_i})"
-               done
-               for _i in ${_special_services}; do
-                       echo "${_i}=$(svc_flags ${_i})"
-               done
-       fi
-}
+               if ! svc_is_base ${_svc}; then
+                       _status=1 # all pkg_scripts are off by default
+               else
+                       
+                       # abuse /etc/rc.conf behavior of only setting flags
+                       # to empty or "NO" to get our default status;
+                       # we'll get our default flags from the rc.d script
+                       [[ -z ${_var} || ${_var} == status ]] && \
+                               _rc_parse_conf /etc/rc.conf
+                       [ "$(eval echo \${${_svc}_flags})" = "NO" ] && _status=1
+               fi
 
-svc_status_default()
-{
-       local _i _svc=$1
+               rc_cmd() { }
+               . /etc/rc.d/${_svc} >/dev/null 2>&1
+
+               [ -z "${daemon_timeout}" ] && daemon_timeout=30
+               [ -z "${daemon_user}" ] && daemon_user=root
+       fi
 
-       if [ -n "${_svc}" ]; then
-               ( svc_flags_default ${_svc} )
-               svc_enabled_default ${_svc}
+       if [ -n "${_var}" ]; then
+               [ "${_var}" = "status" ] && return ${_status}
+               eval _val=\${daemon_${_var}}
+               [ -z "${_val}" ] || print -r -- "${_val}"
        else
-               for _i in $(ls_rcscripts); do
-                       echo "${_i}_flags=$(svc_flags_default ${_i})"
-               done
-               for _i in ${_special_services}; do
-                       echo "${_i}=$(svc_flags_default ${_i})"
-               done
+               if svc_is_special ${_svc}; then
+                       echo "${_svc}=${daemon_flags}"
+               else
+                       echo "${_svc}_flags=${daemon_flags}"
+                       echo "${_svc}_timeout=${daemon_timeout}"
+                       echo "${_svc}_user=${daemon_user}"
+               fi
+               return ${_status}
        fi
 }
 
-pkg_scripts_append()
+svc_rm()
 {
        local _svc=$1
        [ -n "${_svc}" ] || return
 
        rcconf_edit_begin
-       if [ -z "${pkg_scripts}" ]; then
-               echo pkg_scripts="${_svc}" >>${_TMP_RCCONF}
-       elif ! echo ${pkg_scripts} | grep -qw -- ${_svc}; then
-               grep -v "^pkg_scripts.*=" /etc/rc.conf.local >${_TMP_RCCONF}
-               echo pkg_scripts="${pkg_scripts} ${_svc}" >>${_TMP_RCCONF}
+       if svc_is_special ${_svc}; then
+               grep -v "^${_svc}.*=" /etc/rc.conf.local >${_TMP_RCCONF}
+               ( svc_getdef ${_svc} status ) && \
+                       echo "${_svc}=NO" >>${_TMP_RCCONF}
+       else
+               grep -Ev "^${_svc}_(flags|user|timeout).*=" \
+                       /etc/rc.conf.local >${_TMP_RCCONF}
+               ( svc_getdef ${_svc} status ) && \
+                       echo "${_svc}_flags=NO" >>${_TMP_RCCONF}
        fi
        rcconf_edit_end
 }
 
-pkg_scripts_order()
+svc_set()
 {
-       local _svcs="$*"
-       [ -n "${_svcs}" ] || return
-
-       needs_root ${action}
-       local _pkg_scripts _svc
-       for _svc in ${_svcs}; do
-               if svc_base ${_svc} || svc_special ${_svc}; then
-                       _rc_err "${0##*/}: ${_svc} is not a pkg script"
-               elif ! svc_enabled ${_svc}; then
-                       _rc_err "${0##*/}: ${_svc} is not enabled"
+       local _svc=$1 _var=$2
+       [ -n "${_svc}" -a -n "${_var}" ] || return
+
+       shift 2
+       local _flags="$*"
+
+       if [ "${_var}" = "status" ]; then
+               if [ "${_flags}" = "on" ]; then
+                       _var="flags"
+                       # keep our flags if we're already enabled
+                       eval "_flags=\"\${${_svc}_${_var}}\""
+                       [ "${_flags}" = "NO" ] && unset _flags
+                       if ! svc_is_base ${_svc} && ! svc_is_special ${_svc}; then
+                               pkg_scripts_append ${_svc}
+                       fi
+               elif [ "${_flags}" = "off" ]; then
+                       if ! svc_is_base ${_svc} && ! svc_is_special ${_svc}; then
+                               pkg_scripts_rm ${_svc}
+                       fi
+                       svc_rm ${_svc}
+                       return
+               else
+                       _rc_err "${0##*/}: invalid status \"${_flags}\""
                fi
-       done
-       _pkg_scripts=$(echo "${_svcs} ${pkg_scripts}" | tr "[:blank:]" "\n" | \
-                    awk -v ORS=' ' '!x[$0]++')
-       rcconf_edit_begin
-       grep -v "^pkg_scripts.*=" /etc/rc.conf.local >${_TMP_RCCONF}
-       echo pkg_scripts=${_pkg_scripts} >>${_TMP_RCCONF}
-       rcconf_edit_end
-}
-
-pkg_scripts_rm()
-{
-       local _svc=$1
-       [ -n "${_svc}" ] || return
-
-       [ -z "${pkg_scripts}" ] && return
-
-       rcconf_edit_begin
-       sed "/^pkg_scripts[[:>:]]/{s/[[:<:]]${_svc}[[:>:]]//g
-           s/['\"]//g;s/ *= */=/;s/   */ /g;s/ $//;/=$/d;}" \
-           /etc/rc.conf.local >${_TMP_RCCONF}
-       rcconf_edit_end
-}
-
-add_flags()
-{
-       local _svc=$1
-       [ -n "${_svc}" ] || return
+       else
+               svc_get ${_svc} status || \
+                       _rc_err "${0##*/}: ${svc} is not enabled"
+       fi
 
-       if svc_special ${_svc}; then
+       if svc_is_special ${_svc}; then
+               [ "${_var}" = "flags" ] || return
                rcconf_edit_begin
                grep -v "^${_svc}.*=" /etc/rc.conf.local >${_TMP_RCCONF}
-               if ! svc_enabled_default ${_svc}; then
+               ( svc_getdef ${_svc} status ) || \
                        echo "${_svc}=YES" >>${_TMP_RCCONF}
-               fi
                rcconf_edit_end
                return
        fi
 
-       local _flags
-
-       if [ -n "$2" ]; then
-               shift 2
-               _flags="$*"
-       else
-               # keep our flags since none were given
-               eval "_flags=\"\${${_svc}_flags}\""
-               [ "${_flags}" = "NO" ] && unset _flags
+       if [ "${_var}" = "timeout" ]; then
+               [[ ${_flags} != +([[:digit:]]) || ${_flags} -le 0 ]] && \
+                       _rc_err "${0##*/}: \"${_flags}\" is not an integer"
        fi
 
        # unset flags if they match the default enabled ones
        if [ -n "${_flags}" ]; then
-               [ "${_flags}" = "$(svc_flags_default ${_svc})" ] && \
+               [ "${_flags}" = "$(svc_getdef ${_svc} ${_var})" ] && \
                        unset _flags
        fi
 
        # protect leading whitespace
        [ "${_flags}" = "${_flags# }" ] || _flags="\"${_flags}\""
 
-       rcconf_edit_begin
-       grep -v "^${_svc}_flags.*=" /etc/rc.conf.local >${_TMP_RCCONF}
-       if [ -n "${_flags}" ] || \
-          ( svc_base ${_svc} && ! svc_enabled_default ${_svc} ); then
-               echo "${_svc}_flags=${_flags}" >>${_TMP_RCCONF}
-       fi
-       rcconf_edit_end
-}
-
-rm_flags()
-{
-       local _svc=$1
-       [ -n "${_svc}" ] || return
+       # reset: value may have changed
+       unset ${_svc}_${_var}
 
        rcconf_edit_begin
-       if svc_special ${_svc}; then
-               grep -v "^${_svc}.*=" /etc/rc.conf.local >${_TMP_RCCONF}
-               if svc_enabled_default ${_svc}; then
-                       echo "${_svc}=NO" >>${_TMP_RCCONF}
-               fi
-       else
-               grep -v "^${_svc}_flags.*=" /etc/rc.conf.local >${_TMP_RCCONF}
-               if svc_enabled_default ${_svc}; then
-                       echo "${_svc}_flags=NO" >>${_TMP_RCCONF}
-               fi
+       grep -v "^${_svc}_${_var}.*=" /etc/rc.conf.local >${_TMP_RCCONF}
+       if [ -n "${_flags}" ] || \
+          ( svc_is_base ${_svc} && ! svc_getdef ${_svc} status && [ "${_var}" == "flags" ] ); then
+               echo "${_svc}_${_var}=${_flags}" >>${_TMP_RCCONF}
        fi
        rcconf_edit_end
 }
@@ -324,47 +352,60 @@ else
 fi
 
 if [ -n "${svc}" ]; then
-       if ! svc_avail ${svc}; then
+       [[ ${action} = getall ]] && usage
+       svc_is_avail ${svc} || \
                _rc_err "${0##*/}: service ${svc} does not exist" 2
-       fi
-elif [[ ${action} != @(default|order|status) ]] ; then
+elif [[ ${action} != @(getall|order|status) ]] ; then
        usage
 fi
 
 if [ -n "${flag}" ]; then
-       if [ "${flag}" = "flags" ]; then
-               if [ "${action}" != "enable" ]; then
-                       _rc_err "${0##*/}: \"${flag}\" can only be set with \"enable\""
-               fi
-               if svc_special ${svc} && [ -n "${flags}" ]; then
-                       _rc_err "${0##*/}: \"${svc}\" is a special variable, cannot set \"${flag}\""
-               fi
-               if [ "${flag}" = "flags" -a "${flags}" = "NO" ]; then
-                       _rc_err "${0##*/}: \"flags ${flags}\" contradicts \"enable\""
+       [[ ${flag} != @(flags|status|timeout|user) ]] && usage
+       [[ ${action} != @(enable|get|getdef|set) ]] && usage
+       [[ ${action} == @(enable|set) && ${flag} = flags && ${flags} = NO ]] && \
+               _rc_err "${0##*/}: \"flags NO\" contradicts \"${action}\""
+       if svc_is_special ${svc}; then
+               if [[ ${flag} != @(flags|status) || \
+                       ${action} != @(set|get|getdef|enable) ]] || \
+                       [[ ${action} == @(enable|set) && -n ${flags} ]]; then
+                       _rc_err "${0##*/}: \"${svc}\" is a special variable, cannot \"${action} ${svc} ${flag}\""
                fi
-       else
-               usage
        fi
+       [[ ${action} == enable && ${flag} != flags ]] && \
+               _rc_err "${0##*/}: invalid action \"${action} ${svc} ${flag}\""
+elif [ ${action} = "set" ]; then
+       usage
 fi
 
 case ${action} in
-       default)
-               svc_status_default ${svc}
+       default) # XXX backward compat
+               ( svc_getdef ${svc} flags )
+               ( svc_getdef ${svc} status )
                ;;
        disable)
                needs_root ${action}
-               if ! svc_base ${svc} && ! svc_special ${svc}; then
-                       pkg_scripts_rm ${svc}
-               fi
-               rm_flags ${svc}
+               svc_set ${svc} status off
                ;;
        enable)
                needs_root ${action}
-               add_flags ${svc} "${flag}" "${flags}"
-               if ! svc_base ${svc} && ! svc_special ${svc}; then
-                       pkg_scripts_append ${svc}
+               svc_set ${svc} status on
+               # XXX backward compat
+               if [ -n "${flag}" ]; then
+                       svc_set ${svc} "${flag}" "${flags}"
                fi
                ;;
+       get)
+               svc_get ${svc} "${flag}"
+               ;;
+       getall)
+               for i in $(ls_rcscripts) ${_special_services}; do
+                       svc_get ${i}
+               done
+               return 0 # we do not want the "status"
+               ;;
+       getdef)
+               ( svc_getdef ${svc} "${flag}" )
+               ;;
        order)
                if [ -n "${svcs}" ]; then
                        needs_root ${action}
@@ -373,11 +414,23 @@ case ${action} in
                        [[ -z ${pkg_scripts} ]] || echo ${pkg_scripts}
                fi
                ;;
-       status)
-               svc_status ${svc}
+       set)
+               needs_root ${action}
+               svc_set ${svc} "${flag}" "${flags}"
+               ;;
+       status) # XXX backward compat
+               if [ -n "${svc}" ]; then
+                       svc_get ${svc} flags
+                       svc_get ${svc} status
+               else
+                       for i in $(ls_rcscripts) ${_special_services}; do
+                               svc_get ${i}
+                       done
+                       return 0 # we do not want the "status"
+               fi
                ;;
        start|stop|restart|reload|check)
-               if svc_special ${svc}; then
+               if svc_is_special ${svc}; then
                        _rc_err "${0##*/}: \"${svc}\" is a special variable, no rc.d(8) script"
                fi
                /etc/rc.d/${svc} ${_RC_DEBUG} ${_RC_FORCE} ${action}