Explain how an rc.d script can use extra command-line arguments.

This commit is contained in:
Yaroslav Tykhiy 2007-03-03 10:11:34 +00:00
parent 863ff33a88
commit 51a90a53d0
Notes: svn2git 2020-12-08 03:00:23 +00:00
svn path=/head/; revision=29697

View file

@ -1164,6 +1164,144 @@ run_rc_command "$1"</programlisting>
</calloutlist>
</sect1>
<sect1 id="rcng-args">
<title>Giving more flexibility to an rc.d script</title>
<para>When invoked during startup or shutdown, an
<filename>rc.d</filename> script is supposed to act on the
entire subsystem it is responsible for. E.g.,
<filename>/etc/rc.d/netif</filename> should start or stop all
network interfaces described by &man.rc.conf.5;. Either task
can be uniquely indicated by a single command argument such
as <option>start</option> or <option>stop</option>. Between
startup and shutdown, <filename>rc.d</filename> scripts help
the admin to control the running system, and it is when the
need for more flexibility and precision arises. For instance,
the admin may want to add the settings of a new network
interface to &man.rc.conf.5; and then to start it without
interfering with the operation of the existing interfaces.
Next time the admin may need to shut down a single network
interface. In the spirit of the command line, the respective
<filename>rc.d</filename> script calls for an extra argument,
the interface name.</para>
<para>Fortunately, &man.rc.subr.8; allows for passing any number
of arguments to script's methods (within the system limits).
Due to that, the changes in the script itself can be minimal.
To illustrate this opportunity, let us modify the primitive
dummy script so that its messages depend on the additional
arguments supplied. Here we go:</para>
<informalexample>
<programlisting>#!/bin/sh
. /etc/rc.subr
name="dummy"
start_cmd="${name}_start"
stop_cmd=":"
kiss_cmd="${name}_kiss"
extra_commands="kiss"
dummy_start()
{
if [ $# -gt 0 ]; then<co id="rcng-args-start">
echo "Greeting message: $*"
else
echo "Nothing started."
fi
}
dummy_kiss()
{
echo -n "A ghost gives you a kiss"
if [ $# -gt 0 ]; then<co id="rcng-args-kiss">
echo -n " and whispers: $*"
fi
case "$*" in
*[.!?])
echo
;;
*)
echo .
;;
esac
}
load_rc_config $name
run_rc_command "$@"<co id="rcng-args-all"></programlisting>
</informalexample>
<para>What essential changes can we notice in the script?</para>
<calloutlist>
<callout arearefs="rcng-args-start">
<para>All arguments you type after <option>start</option>
can end up as positional parameters to the respective
method. We can use them in any way according to our
task, skills, and fancy. In the current example, we just
pass all of them to &man.echo.1; as one string in the
next line &mdash; note <envar>$*</envar> within the double
quotes. Here is how the script can be invoked now:</para>
<screen>&prompt.root; <userinput>/etc/rc.d/dummy start</userinput>
Nothing started.
&prompt.root; <userinput>/etc/rc.d/dummy start Hello world!</userinput>
Greeting message: Hello world!</screen>
</callout>
<callout arearefs="rcng-args-kiss">
<para>The same applies to any method our script provides,
not only to a standard one. We have added a custom method
named <option>kiss</option>, and it can take advantage of
the extra arguments not less than <option>start</option>
does. E.g.:
<screen>&prompt.root; <userinput>/etc/rc.d/dummy kiss</userinput>
A ghost gives you a kiss.
&prompt.root; <userinput>/etc/rc.d/dummy kiss Once I was Etaoin Shrdlu...</userinput>
A ghost gives you a kiss and whispers: Once I was Etaoin Shrdlu...</screen>
</callout>
<callout arearefs="rcng-args-all">
<para>The only question left is how &man.rc.subr.8; can gain
access to the extra command-line arguments. Should it just
grab them directly? Not by any means. Firstly, an &man.sh.1;
function has no access to the positional parameters of
its caller, but &man.rc.subr.8; is just a sack of such
functions. Secondly, the good manner of
<filename>rc.d</filename> dictates that it is for the
main script to decide which arguments are to be passed
to its methods.</para>
<para>So the approach adopted by &man.rc.subr.8; is as follows:
<function>run_rc_command</function> passes on all its
arguments but the first one to the respective method verbatim.
The first, omitted, argument is the name of the method itself.
Consequently, if we want just to pass all extra arguments to
any method, we can merely substitute <literal>"$@"</literal>
for <literal>"$1"</literal> in the last line of our script.</para>
<important>
<para>An &man.sh.1; programmer ought to understand the
subtle difference between <envar>$*</envar> and
<envar>$@</envar> as the ways to designate all positional
parameters. For its in-depth discussion, refer to a
good handbook on &man.sh.1; scripting. <emphasis>Do
not</emphasis> use the expressions until you fully
understand them because their misuse will result in
buggy and insecure scripts.</para>
</important>
<para>The <function>run_rc_command</function> function will
consume its first argument to find the method. Hence an
apparent shift by one argument: What is <envar>$2</envar>
in the command line will be presented as <envar>$1</envar>
to the method, and so on.</para>
</callout>
</calloutlist>
</sect1>
<sect1 id="rcng-furthur">
<title>Further reading</title>