[Please reply to freebsd-rc@]
Hi,
I would like your feedback and testers of the attached patch. This
implements multiple instance support in rc.d scripts. You can try it
by replacing /etc/rc.subr with the attached one.
More details are as follow. Typically, an rc.d/foo script has the
following structure and rc.conf variables:
/etc/rc.d/foo:
name=foo
rcvar=foo_enable
...
load_rc_command $name
run_rc_command $*
/etc/rc.conf:
foo_enable=YES
foo_flags=-f -l -a -g
The above supports one instance for one script. After replacing
rc.subr, you can specify additional instances in rc.conf:
/etc/rc.conf:
foo_instances=one two
foo_one_enable=YES
foo_one_flags=-f -l -a -g
foo_two_enable=YES
foo_two_flags=-F -L -A -G
$foo_instances defines instances by space-separated list of instance
names, and rc.conf variables for them are something like
${name}_${instname}_enable. The following command
# service foo start
starts foo_one and foo_two with the specified flags. Instances can
be specified in the following form:
# service foo start:one
or multiple instances in a particular order:
# service foo start:two,one
Basically, no change is required for the rc.d/foo script itself.
However, there is a problem that default values of the instantiated
variables are not defined.
For example, if an rc.d/script uses $foo_mode, you need to define
$foo_one_mode. The default value of $foo_mode is usually defined in
etc/defaults/rc.conf for rc.d scripts in the base system and :
${foo_mode:=value} idiom in scripts from Ports Collection. So all
of the variables should be defined for each instance, too. As you
noticed, this is not easy without editing the script itself.
To alleviate this, set_rcvar() can be used:
/etc/rc.d/foo:
name=foo
rcvar=foo_enable
set_rcvar foo_enable YES Enable $name
set_rcvar foo_program/tmp/test Command for $name
...
load_rc_command $name
run_rc_command $*
The three arguments are varname, default value, and description. If
a variable is defined by set_rcvar(), default values instantiated
variables will be set automatically---foo_one_program is set by
foo_program if it is not defined.
This approach still has another problem. set_rcvar() is not
supported in all branches, so a script using it does not work in old
supported branches. One solution which can be used for scripts in
Ports Collection is adding both definitions before and after
load_rc_command() until EoL of old branches like this:
/etc/rc.d/foo:
name=foo
rcvar=foo_enable
if type set_rcvar /dev/null 21; then
set_rcvar foo_enableYES Enable $name
set_rcvar foo_program /tmp/test Command for $name
fi
...
load_rc_command $name
# will be removed after all supported branches have set_rcvar().
if ! type set_rcvar /dev/null 21; then
: ${foo_enable:=YES}
: ${foo_program:=/tmp/test}
for _i in $foo_instances; do
for _j in enable program; do
eval : \${foo_${_i}_enable:=\$foo_$_j}
done
done
fi
run_rc_command $*
This is a bit ugly but should work fine.
I am using this patch to invoke multiple named (caching
server/contents server) and syslogd (local only/listens INET/INET6
socket only) daemons. While $foo_instances is designed as a
user-defined knob, this can be applied to software which need to
invoke multiple/different daemons which depend on each other in a
script, too.
I am feeling this patch still needs more careful review from others.
Any comments are welcome. Thank you.
-- Hiroki
Index: etc/rc.subr
===
--- etc/rc.subr (revision 272976)
+++ etc/rc.subr (working copy)
@@ -698,7 +698,10 @@
# start stop restart rcvar status poll ${extra_commands}
# If there's a match, run ${argument}_cmd or the default method
# (see below).
+# _run_rc_command0() is the main routine and run_rc_command() is
+# a wrapper to handle multiple instances.
#
+#
# If argument has a given prefix, then change the operation as follows:
# Prefix Operation
# -- -
@@ -755,6 +758,9 @@
#
# ${name}_nice n Nice level to run ${command} at.
#
+# ${name}_pidfile n This to be used in /etc/rc.conf to override
+#${pidfile}.
+#
# ${name}_user n User to run ${command} as, using su(1) if not
#using ${name}_chroot.
#Requires /usr to be mounted.
@@ -863,6 +869,57 @@
#
run_rc_command()
{
+ local _act _instances _name _desc _rcvar
+
+ _act=$1
+ shift
+ eval _instances=\$${name}_instances
+
+ # Check if instance is specified, e.g. start:instance,
+ case ${_act%:*} in
+ $_act) ;; # no instance specified
+ *)
+ _instances=$(echo ${_act#*:} | tr , )
+ _act=${_act%:*}
+ ;;
+ esac
+
+ # Use