From: ajacoutot Date: Tue, 6 Jan 2015 11:47:50 +0000 (+0000) Subject: Major rcctl(8) rewrite to simplify it and add new features. It can now X-Git-Url: http://artulab.com/gitweb/?a=commitdiff_plain;h=9ade77b40469e31003cd45753e7566bed2760b55;p=openbsd Major rcctl(8) rewrite to simplify it and add new features. It can now 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@ --- diff --git a/usr.sbin/rcctl/rcctl.sh b/usr.sbin/rcctl/rcctl.sh index 26aca62f10d..aa451070c96 100644 --- a/usr.sbin/rcctl/rcctl.sh +++ b/usr.sbin/rcctl/rcctl.sh @@ -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 +# Copyright (c) 2014, 2015 Antoine Jacoutot # Copyright (c) 2014 Ingo Schwarze # # 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}