Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 01 Jul 2013 06:29:53 +0900 (JST)
From:      Hiroki Sato <hrs@FreeBSD.org>
To:        freebsd-rc@FreeBSD.org, freebsd-current@FreeBSD.org
Subject:   Proposal: multi-instance and self-contained rc.d script
Message-ID:  <20130701.062953.1443190655468739608.hrs@allbsd.org>

next in thread | raw e-mail | index | archive | help
----Security_Multipart0(Mon_Jul__1_06_29_53_2013_545)--
Content-Type: Multipart/Mixed;
 boundary="--Next_Part(Mon_Jul__1_06_29_53_2013_150)--"
Content-Transfer-Encoding: 7bit

----Next_Part(Mon_Jul__1_06_29_53_2013_150)--
Content-Type: Text/Plain; charset=us-ascii
Content-Transfer-Encoding: 7bit

Hi,

 I am working on rc.d script improvements in terms of the following
 two points.  A prototype is attached.  This still includes some rough
 edges but should be enough to understand the concepts.  I would like
 your comments about them.  (This is posted to -current@ and -rc@, but
 please reply to freebsd-rc@ only)

 1. Multi-instance support

  As you know, an rc.d script invokes ${name}_program with
  configuration variables in /etc/rc.conf.  It works, but it is
  difficult to run multiple instances of a program.  A typical example
  is named---a sysadmin often wants to run two instances of named as a
  caching server and a content DNS server by using different
  named.conf.  In the current framework, two rc.d scripts are needed
  by copying /etc/rc.d/named to /etc/rc.d/named_cache and rewriting
  name= and rcvar=.

  The attached patch allows the following:

  named_enable="YES"
  named_instances="cache1 cache2"
  named_conf="/etc/namedb/named.conf"
  named_cache1_enable="YES"
  named_cache1_conf="/etc/namedb/named_cache1.conf"
  named_cache2_enable="NO"
  named_cache2_conf="/etc/namedb/named_cache2.conf"

  A new variable "{name}_instances" defines instances.  In this
  example, it is named_instances="cache1 cache2".  All of the default
  values of $named_{instname}_foo are automatically set to the same as
  $named_foo.

  In the implementation, load_rc_config() reads variables for all
  instances and run_rc_command() runs each instance in order.  When
  doing "rc.d/foo stop", run_rc_command() stops the instances in
  reverse order.

  In the patch, killing the processes without pid file does not work
  well yet.  This can be improved.

 2. Self-contained rc.d script

  rc.d scripts depend on /etc/default/rc.conf for the default
  configuration and rc.conf(5) manual page describes the knobs.
  However, it is difficult to understand which variable is related to
  which script.  In addition, /etc/defaults/rc.conf is often out of
  sync with the rc.d scripts.  So, my proposal is as follows:

   a) Define rc.conf variables and the default values in the rc.d
      script which uses them.  "rc.d/foo rcvar" shows the variables
      and the default values.

   b) Make rc.d/foo always have rc.d/foo(8) manual page.

  The attached patch includes an example of rc.d/routed.  The primary
  difference is declaration part of rc.conf variables:

  set_rcvar enable       NO
  set_rcvar program      /sbin/routed
  set_rcvar flags        -q

  These sets the default value of $routed_{enable,program,flags} at
  load_rc_config().  The reason why a simple ": ${routed_enable="NO"}"
  does not work is that it does not work with multi-instance support.

  This is backward-compatible with the current /etc/defaults/rc.conf.
  load_rc_config() sets these values first, and then reads
  /etc/defaults/rc.conf and /etc/rc.conf.d/$name.

  "rc.d/route rcvar" displays the current configuration and available
  variables briefly like the following:

   # routed: network RIP and router discovery routing daemon
   #
   routed_enable="NO"      # (default: "NO")
   routed_program="/sbin/routed"   # (default: "/sbin/routed")
   routed_flags="-q"       # (default: "-q")

  When multi-instance is enabled in rc.conf like this:

   routed_enable="YES"
   routed_instances="hoge fuga"
   routed_hoge_desc="hogehoge"
   routed_fuga_enable="NO"
   routed_fuga_flags=""

  The results of rcvar will be the following:

   # routed: network RIP and router discovery routing daemon
   #
   routed_enable="YES"      # (default: "NO")
   routed_program="/sbin/routed"   # (default: "/sbin/routed")
   routed_flags="-q"       # (default: "-q")

   # routed_hoge: network RIP and router discovery routing daemon: hogehoge
   #
   routed_hoge_enable="YES" # (default: "NO")
   routed_hoge_program="/sbin/routed"      # (default: "/sbin/routed")
   routed_hoge_flags="-q"  # (default: "-q")

   # routed_fuga: network RIP and router discovery routing daemon
   #
   routed_fuga_enable="NO" # (default: "NO")
   routed_fuga_program="/sbin/routed"      # (default: "/sbin/routed")
   routed_fuga_flags=""    # (default: "-q")

 We can remove or comment out all of lines in /etc/defaults/rc.conf,
 and mismatch between /etc/defaults/rc.conf and scripts does not
 occur.  Running "rc.d/foo rcvar" can be used to generate
 /etc/defaults/rc.conf if needed.

 That's all.  Both changes are fully backward compatible and I believe
 they improve flexibility and manageability of rc.d scripts.

 An example of rc.d/routed(8) manual page is also attached.  If these
 changes are acceptable, I would like to split the current (lengthy)
 rc.conf(5) manual page into rc.d/foo(8).

-- Hiroki

----Next_Part(Mon_Jul__1_06_29_53_2013_150)--
Content-Type: Text/Plain; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Disposition: inline; filename="routed.8.catman.txt"

RC.D/ROUTED(8)		FreeBSD System Manager's Manual 	RC.D/ROUTED(8)

NAME
     routed -- rc.d script for routed(8) daemon

SYNOPSIS
     routed [fast|force|one]start|stop|restart|rcvar|status|poll

DESCRIPTION
     routed is an rc.d(8) script to control routed(8) daemon.

COMMAND LINE ARGUMENTS
     The standard rc.d(8) arguments are supported.  For more information, see
     the section of run_rc_command() in the rc(8) manual page.

CONFIGURATION VARIABLES
     The following rc.conf(5) variables are supported.	To show all of the
     supported variables and the default values, use rcvar argument:

     routed_enable	   (bool) Set to ``YES'' to start the routed(8) daemon
			   at boot time.

     routed_flags	   (str) Specify command line flags to the routed(8)
			   daemon.

SEE ALSO
     rc.conf(5), rc(8), routed(8)

HISTORY
     The routed script appeared in FreeBSD 8.0.

AUTHORS
     This manual page was written by Hiroki Sato <hrs@FreeBSD.org>.

FreeBSD 10.0		       January 26, 2012 		  FreeBSD 10.0

----Next_Part(Mon_Jul__1_06_29_53_2013_150)--
Content-Type: Text/X-Patch; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Disposition: inline; filename="rc.d_multi_20130701-1.diff"

Index: etc/rc.d/routed
===================================================================
--- etc/rc.d/routed	(revision 252378)
+++ etc/rc.d/routed	(working copy)
@@ -13,6 +13,10 @@ name="routed"
 rcvar="routed_enable"
 desc="network RIP and router discovery routing daemon"

+set_rcvar enable	NO
+set_rcvar program	/sbin/routed
+set_rcvar flags		-q
+
 set_rcvar_obsolete router_enable routed_enable
 set_rcvar_obsolete router routed_program
 set_rcvar_obsolete router_flags	routed_flags
Index: etc/rc.subr
===================================================================
--- etc/rc.subr	(revision 252378)
+++ etc/rc.subr	(working copy)
@@ -54,6 +54,21 @@ JID=`$PS -p $$ -o jid=`
 #	functions
 #	---------

+# set_rcvar [var] [defval]
+#	Define rc.conf variable.
+#
+set_rcvar()
+{
+	case $# in
+	0)	echo $name
+	;;
+	1)	eval rcvars=\"${rcvars# } $1\"
+	;;
+	2)	eval rcvars=\"${rcvars# } $1\"
+		eval ${rcvar%_enable}_${1}_defval=\"$2\"
+	;;
+	esac
+}
 # set_rcvar_obsolete oldvar [newvar] [msg]
 #	Define obsolete variable.
 #	Global variable $rcvars_obsolete is used.
@@ -570,6 +585,39 @@ check_startmsgs()
 #
 run_rc_command()
 {
+	local _act _instances _inst _name _desc _rcvar
+
+	_act=$1
+	shift
+	eval _instances=\"DEFAULT \$${name}_instances\"
+	_name=$name
+	_desc=$desc
+	_rcvar=$rcvar
+
+	# Use reverse order for stop.
+	case $_act in
+	*stop)	_instances=$(reverse_list $_instances) ;;
+	esac
+
+	for _inst in $_instances; do
+		case $_inst in
+		DEFAULT)
+			name=$_name
+			desc=$_desc
+			rcvar=$_rcvar
+		;;
+		*)
+			name=${_name}_${_inst}
+			eval desc=\"$_desc\${${_name}_${_inst}_desc+:\ }\$${_name}_${_inst}_desc\"
+			rcvar=${_rcvar%_enable}_${_inst}_enable
+		;;
+		esac
+		_run_rc_command0 $_act "$@"
+	done
+}
+
+_run_rc_command0()
+{
 	_return=0
 	rc_arg=$1
 	if [ -z "$name" ]; then
@@ -823,47 +871,25 @@ $command $rc_flags $command_args"
 			;;

 		rcvar)
-			echo -n "# $name"
-			if [ -n "$desc" ]; then
-				echo " : $desc"
-			else
-				echo ""
-			fi
+			echo "# $name${desc+: }${desc}"
 			echo "#"
 			# Get unique vars in $rcvar
-			for _v in $rcvar; do
-				case $v in
-				$_v\ *|\ *$_v|*\ $_v\ *) ;;
-				*)	v="${v# } $_v" ;;
-				esac
+			v=
+			for _v in $(uniqlist ${rcvar%_enable} $rcvars $_rc_namevarlist); do
+				if [ "$_v" = "instances" ]; then
+					continue
+				fi
+				v="${v# } ${name}_$_v"
 			done

 			# Display variables.
 			for _v in $v; do
-				if [ -z "$_v" ]; then
+				eval __v=\$$_v
+				eval _defval=\$${_v}_defval
+				if [ -z "$__v" -a "$__v" = "$_defval" ]; then
 					continue
 				fi
-
-				eval _desc=\$${_v}_desc
-				eval _defval=\$${_v}_defval
-				_h="-"
-
-				eval echo \"$_v=\\\"\$$_v\\\"\"
-				# decode multiple lines of _desc
-				while [ -n "$_desc" ]; do
-					case $_desc in
-					*^^*)
-						echo "# $_h ${_desc%%^^*}"
-						_desc=${_desc#*^^}
-						_h=" "
-						;;
-					*)
-						echo "# $_h ${_desc}"
-						break
-						;;
-					esac
-				done
-				echo "#   (default: \"$_defval\")"
+				echo ${_v}=\"$__v\"${_defval:+\	# (default: \"$_defval\")}
 			done
 			echo ""
 			;;
@@ -1004,11 +1030,55 @@ run_rc_script()
 }

 #
+# uniqlist
+#	Return a list with duplicate words removed.
+#
+uniqlist()
+{
+	local v _v
+
+	v=
+	for _v in "$@"; do
+		case $v in
+		$_v\ *|\ *$_v|*\ $_v\ *) ;;
+		*)	v="${v# } $_v" ;;
+		esac
+	done
+	echo $v
+}
+
+#
 # load_rc_config name
 #	Source in the configuration file for a given name.
 #
 load_rc_config()
 {
+	local _instances _inst _name _k _v
+
+	_name=$1
+	_load_rc_config0 $_name
+
+	eval _instances=\$${_name}_instances
+
+	for _inst in $_instances; do
+		_load_rc_config0 ${_name}_${_inst}
+
+		# Set default values by using $name.
+		for _k in $(uniqlist $rcvars $_rc_namevarlist); do
+			if [ "$_k" = "instances" ]; then
+				continue
+			fi
+			eval : \${${_name}_${_inst}_${_k}="\$${_name}_${_k}"}
+			eval : \${${_name}_${_inst}_${_k}_defval="\$${_name}_${_k}_defval"}
+#			eval echo DEBUG ${_name}_${_inst}_${_k}=\$${_name}_${_inst}_${_k}
+		done
+#		eval echo DEBUG ${rcvar%_enable}_${_inst}_enable=\$${rcvar%_enable}_${_inst}_enable
+		eval : \${${rcvar%_enable}_${_inst}_enable="\$${rcvar}"}
+	done
+}
+
+_load_rc_config0()
+{
 	local _name _rcvar_val _var _defval _v _msg _new
 	_name=$1
 	if [ -z "$_name" ]; then
@@ -1034,10 +1104,10 @@ load_rc_config()
 	fi

 	# Set defaults if defined.
-	for _var in $rcvar; do
-		eval _defval=\$${_var}_defval
+	for _var in $rcvars; do
+		eval _defval=\$${rcvar%_enable}_${_var}_defval
 		if [ -n "$_defval" ]; then
-			eval : \${$_var:=\$${_var}_defval}
+			eval : \${${rcvar%_enable}_${_var}=\$${rcvar%_enable}_${_var}_defval}
 		fi
 	done

@@ -1051,7 +1121,7 @@ load_rc_config()
 			;;
 		*)
 			if [ -z "$_new" ]; then
-				_msg="Ignored."
+				: ${_msg="Ignored."}
 			else
 				eval $_new=\"\$$_var\"
 				if [ -z "$_msg" ]; then
@@ -1736,7 +1806,7 @@ check_kern_features()
 # check_namevarlist var
 #	Return "0" if ${name}_var is reserved in rc.subr.

-_rc_namevarlist="program chroot chdir flags fib nice user group groups"
+_rc_namevarlist="program chroot chdir flags fib nice user group groups instances"
 check_namevarlist()
 {
 	local _v
Index: etc/mtree/BSD.usr.dist
===================================================================
--- etc/mtree/BSD.usr.dist	(revision 252378)
+++ etc/mtree/BSD.usr.dist	(working copy)
@@ -938,6 +938,8 @@
                 ..
                 sparc64
                 ..
+		rc.d
+		..
             ..
             man9
             ..
Index: share/man/man8/Makefile
===================================================================
--- share/man/man8/Makefile	(revision 252378)
+++ share/man/man8/Makefile	(working copy)
@@ -24,4 +24,6 @@ MLINKS+=yp.8 NIS.8 \
 	yp.8 nis.8 \
 	yp.8 YP.8

+SUBDIR=	man8.rc.d
+
 .include <bsd.prog.mk>
Index: share/man/man8/man8.rc.d/Makefile
===================================================================
--- share/man/man8/man8.rc.d/Makefile	(revision 0)
+++ share/man/man8/man8.rc.d/Makefile	(working copy)
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+MAN=	routed.8
+
+MANSUBDIR=/rc.d
+
+.include <bsd.prog.mk>
Index: share/man/man8/man8.rc.d/routed.8
===================================================================
--- share/man/man8/man8.rc.d/routed.8	(revision 0)
+++ share/man/man8/man8.rc.d/routed.8	(working copy)
@@ -0,0 +1,90 @@
+.\" Copyright (c) 2012,2013
+.\"	Hiroki Sato <hrs@FreeBSD.org>.	All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd January 26, 2012
+.Dt RC.D/ROUTED 8
+.Os
+.Sh NAME
+.Nm routed
+.Nd rc.d script for
+.Xr routed 8
+daemon
+.Sh SYNOPSIS
+.Nm
+.Oo fast|force|one Oc Ns Ar start|stop|restart|rcvar|status|poll
+.Sh DESCRIPTION
+.Nm
+is an
+.Xr rc.d 8
+script to control
+.Xr routed 8
+daemon.
+.Sh COMMAND LINE ARGUMENTS
+The standard
+.Xr rc.d 8
+arguments are supported.
+For more information, see the section of
+.Fn run_rc_command
+in the
+.Xr rc 8
+manual page.
+.Sh CONFIGURATION VARIABLES
+The following
+.Xr rc.conf 5
+variables are supported.
+To show all of the supported variables and the default values,
+use
+.Li rcvar
+argument:
+.Pp
+.Bl -tag -width "01234567890123456789" -compact
+.It Va routed_enable
+.Pq Vt bool
+Set to
+.Dq Li YES
+to start the
+.Xr routed 8
+daemon at boot time.
+.Pp
+.It Va routed_flags
+.Pq Vt str
+Specify command line flags to the
+.Xr routed 8
+daemon.
+.El
+.Pp
+.Sh SEE ALSO
+.Xr rc.conf 5 ,
+.Xr rc 8 ,
+.Xr routed 8
+.Sh HISTORY
+The
+.Nm
+script appeared in
+.Fx 8.0 .
+.Sh AUTHORS
+This manual page was written by
+.An Hiroki Sato Aq hrs@FreeBSD.org .

----Next_Part(Mon_Jul__1_06_29_53_2013_150)----

----Security_Multipart0(Mon_Jul__1_06_29_53_2013_545)--
Content-Type: application/pgp-signature
Content-Transfer-Encoding: 7bit

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.13 (FreeBSD)

iEYEABECAAYFAlHQo1EACgkQTyzT2CeTzy2+NwCdF1ZRsxibXcFZg9cn7/OLNcAJ
b4EAnikHdj5+1bz0TegXrHXQCKdg/5FV
=e8U+
-----END PGP SIGNATURE-----

----Security_Multipart0(Mon_Jul__1_06_29_53_2013_545)----



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20130701.062953.1443190655468739608.hrs>