1366 lines
56 KiB
XML
1366 lines
56 KiB
XML
<?xml version="1.0" encoding="iso-8859-1"?>
|
|
<!DOCTYPE article PUBLIC "-//FreeBSD//DTD DocBook XML V5.0-Based Extension//EN"
|
|
"http://www.FreeBSD.org/XML/share/xml/freebsd50.dtd">
|
|
<article xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0" xml:lang="en">
|
|
<info><title>Practical rc.d scripting in BSD</title>
|
|
|
|
|
|
<author><personname><firstname>Yar</firstname><surname>Tikhiy</surname></personname><affiliation>
|
|
<address><email>yar@FreeBSD.org</email></address>
|
|
</affiliation></author>
|
|
|
|
<copyright>
|
|
<year>2005</year>
|
|
<year>2006</year>
|
|
<year>2012</year>
|
|
|
|
<holder>The FreeBSD Project</holder>
|
|
</copyright>
|
|
|
|
<legalnotice xml:id="trademarks" role="trademarks">
|
|
&tm-attrib.freebsd;
|
|
&tm-attrib.netbsd;
|
|
&tm-attrib.general;
|
|
</legalnotice>
|
|
|
|
<pubdate>$FreeBSD$</pubdate>
|
|
|
|
<releaseinfo>$FreeBSD$</releaseinfo>
|
|
|
|
<abstract>
|
|
<para>Beginners may find it difficult to relate the
|
|
facts from the formal documentation on the BSD
|
|
<filename>rc.d</filename> framework with the practical tasks
|
|
of <filename>rc.d</filename> scripting. In this article,
|
|
we consider a few typical cases of increasing complexity,
|
|
show <filename>rc.d</filename> features suited for each
|
|
case, and discuss how they work. Such an examination should
|
|
provide reference points for further study of the design
|
|
and efficient application of <filename>rc.d</filename>.</para>
|
|
</abstract>
|
|
</info>
|
|
|
|
<sect1 xml:id="rcng-intro">
|
|
<title>Introduction</title>
|
|
|
|
<para>The historical BSD had a monolithic startup script,
|
|
<filename>/etc/rc</filename>. It was invoked by
|
|
&man.init.8; at system boot time and performed all userland
|
|
tasks required for multi-user operation: checking and
|
|
mounting file systems, setting up the network, starting
|
|
daemons, and so on. The precise list of tasks was not the
|
|
same in every system; admins needed to customize it. With
|
|
few exceptions, <filename>/etc/rc</filename> had to be modified,
|
|
and true hackers liked it.</para>
|
|
|
|
<para>The real problem with the monolithic approach was that
|
|
it provided no control over the individual components started
|
|
from <filename>/etc/rc</filename>. For instance,
|
|
<filename>/etc/rc</filename> could not restart a single daemon.
|
|
The system admin had to find the daemon process by hand, kill it,
|
|
wait until it actually exited, then browse through
|
|
<filename>/etc/rc</filename> for the flags, and finally type
|
|
the full command line to start the daemon again. The task
|
|
would become even more difficult and prone to errors if the
|
|
service to restart consisted of more than one daemon or
|
|
demanded additional actions. In a few words, the single
|
|
script failed to fulfil what scripts are for: to make the
|
|
system admin's life easier.</para>
|
|
|
|
<para>Later there was an attempt to split out some parts of
|
|
<filename>/etc/rc</filename> for the sake of starting the
|
|
most important subsystems separately. The notorious example
|
|
was <filename>/etc/netstart</filename> to bring up networking.
|
|
It did allow for accessing the network from single-user
|
|
mode, but it did not integrate well into the automatic startup
|
|
process because parts of its code needed to interleave with
|
|
actions essentially unrelated to networking. That was why
|
|
<filename>/etc/netstart</filename> mutated into
|
|
<filename>/etc/rc.network</filename>. The latter was no
|
|
longer an ordinary script; it comprised of large, tangled
|
|
&man.sh.1; functions called from <filename>/etc/rc</filename>
|
|
at different stages of system startup. However, as the startup
|
|
tasks grew diverse and sophisticated, the
|
|
<quote>quasi-modular</quote> approach became even more of a
|
|
drag than the monolithic <filename>/etc/rc</filename> had
|
|
been.</para>
|
|
|
|
<para>Without a clean and well-designed framework, the startup
|
|
scripts had to bend over backwards to satisfy the needs of
|
|
rapidly developing BSD-based operating systems. It became
|
|
obvious at last that more steps are necessary on the way to
|
|
a fine-grained and extensible <filename>rc</filename> system.
|
|
Thus BSD <filename>rc.d</filename> was born. Its acknowledged
|
|
fathers were Luke Mewburn and the NetBSD community. Later
|
|
it was imported into &os;. Its name refers to the location
|
|
of system scripts for individual services, which is in
|
|
<filename>/etc/rc.d</filename>. Soon we
|
|
will learn about more components of the <filename>rc.d</filename>
|
|
system and see how the individual scripts are invoked.</para>
|
|
|
|
<para>The basic ideas behind BSD <filename>rc.d</filename> are
|
|
<emphasis>fine modularity</emphasis> and <emphasis>code
|
|
reuse</emphasis>. <emphasis>Fine modularity</emphasis> means
|
|
that each basic <quote>service</quote> such as a system daemon
|
|
or primitive startup task gets its own &man.sh.1; script able
|
|
to start the service, stop it, reload it, check its status.
|
|
A particular action is chosen by the command-line argument
|
|
to the script. The <filename>/etc/rc</filename> script still
|
|
drives system startup, but now it merely invokes the smaller
|
|
scripts one by one with the <option>start</option> argument.
|
|
It is easy to perform shutdown tasks as well by running the
|
|
same set of scripts with the <option>stop</option> argument,
|
|
which is done by <filename>/etc/rc.shutdown</filename>. Note
|
|
how closely this follows the Unix way of having a set of small
|
|
specialized tools, each fulfilling its task as well as possible.
|
|
<emphasis>Code reuse</emphasis> means that common operations
|
|
are implemented as &man.sh.1; functions and collected in
|
|
<filename>/etc/rc.subr</filename>. Now a typical script can
|
|
be just a few lines' worth of &man.sh.1; code. Finally, an
|
|
important part of the <filename>rc.d</filename> framework is
|
|
&man.rcorder.8;, which helps <filename>/etc/rc</filename> to
|
|
run the small scripts orderly with respect to dependencies
|
|
between them. It can help <filename>/etc/rc.shutdown</filename>,
|
|
too, because the proper order for the shutdown sequence is
|
|
opposite to that of startup.</para>
|
|
|
|
<para>The BSD <filename>rc.d</filename> design is described in
|
|
<link linkend="lukem">the original article by Luke Mewburn</link>,
|
|
and the <filename>rc.d</filename> components are documented
|
|
in great detail in <link linkend="manpages">the respective
|
|
manual pages</link>. However, it might not appear obvious
|
|
to an <filename>rc.d</filename> newbie how to tie the numerous
|
|
bits and pieces together in order to create a well-styled
|
|
script for a particular task. Therefore this article will
|
|
try a different approach to describe <filename>rc.d</filename>.
|
|
It will show which features should be used in a number of
|
|
typical cases, and why. Note that this is not a how-to
|
|
document because our aim is not at giving ready-made recipes,
|
|
but at showing a few easy entrances into the
|
|
<filename>rc.d</filename> realm. Neither is this article a
|
|
replacement for the relevant manual pages. Do not hesitate
|
|
to refer to them for more formal and complete documentation
|
|
while reading this article.</para>
|
|
|
|
<para>There are prerequisites to understanding this article.
|
|
First of all, you should be familiar with the &man.sh.1;
|
|
scripting language in order to master <filename>rc.d</filename>.
|
|
In addition, you should know how the system performs
|
|
userland startup and shutdown tasks, which is described in
|
|
&man.rc.8;.</para>
|
|
|
|
<para>This article focuses on the &os; branch of
|
|
<filename>rc.d</filename>. Nevertheless, it may be useful
|
|
to NetBSD developers, too, because the two branches of BSD
|
|
<filename>rc.d</filename> not only share the same design
|
|
but also stay similar in their aspects visible to script
|
|
authors.</para>
|
|
</sect1>
|
|
|
|
<sect1 xml:id="rcng-task">
|
|
<title>Outlining the task</title>
|
|
|
|
<para>A little consideration before starting
|
|
<envar>$EDITOR</envar> will not hurt. In order to write a
|
|
well-tempered <filename>rc.d</filename> script for a system
|
|
service, we should be able to answer the following questions
|
|
first:</para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para>Is the service mandatory or optional?</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>Will the script serve a single program, e.g.,
|
|
a daemon, or perform more complex actions?</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>Which other services will our service depend on,
|
|
and vice versa?</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
|
|
<para>From the examples that follow we will see why it is
|
|
important to know the answers to these questions.</para>
|
|
</sect1>
|
|
|
|
<sect1 xml:id="rcng-dummy">
|
|
<title>A dummy script</title>
|
|
|
|
<para>The following script just emits a message each time the
|
|
system boots up:</para>
|
|
|
|
<informalexample>
|
|
<programlisting>#!/bin/sh<co xml:id="rcng-dummy-shebang"/>
|
|
|
|
. /etc/rc.subr<co xml:id="rcng-dummy-include"/>
|
|
|
|
name="dummy"<co xml:id="rcng-dummy-name"/>
|
|
start_cmd="${name}_start"<co xml:id="rcng-dummy-startcmd"/>
|
|
stop_cmd=":"<co xml:id="rcng-dummy-stopcmd"/>
|
|
|
|
dummy_start()<co xml:id="rcng-dummy-startfn"/>
|
|
{
|
|
echo "Nothing started."
|
|
}
|
|
|
|
load_rc_config $name<co xml:id="rcng-dummy-loadconfig"/>
|
|
run_rc_command "$1"<co xml:id="rcng-dummy-runcommand"/></programlisting>
|
|
</informalexample>
|
|
|
|
<para>Things to note are:</para>
|
|
|
|
<calloutlist>
|
|
<callout arearefs="rcng-dummy-shebang">
|
|
<para>An interpreted script should begin with the magic
|
|
<quote>shebang</quote> line. That line specifies the
|
|
interpreter program for the script. Due to the shebang
|
|
line, the script can be invoked exactly like a binary
|
|
program provided that it has the execute bit set.
|
|
(See &man.chmod.1;.)
|
|
For example, a system admin can run our script manually,
|
|
from the command line:</para>
|
|
|
|
<screen>&prompt.root; <userinput>/etc/rc.d/dummy start</userinput></screen>
|
|
|
|
<note>
|
|
<para>In order to be properly managed by the
|
|
<filename>rc.d</filename> framework, its scripts need
|
|
to be written in the &man.sh.1; language. If you have
|
|
a service or port that uses a binary control utility
|
|
or a startup routine written in another language,
|
|
install that element in <filename>/usr/sbin</filename>
|
|
(for the system) or <filename>/usr/local/sbin</filename>
|
|
(for ports) and call it from a &man.sh.1; script in the
|
|
appropriate <filename>rc.d</filename> directory.</para>
|
|
</note>
|
|
|
|
<tip>
|
|
<para>If you would like to learn the details of why
|
|
<filename>rc.d</filename> scripts must be written in
|
|
the &man.sh.1; language, see how <filename>/etc/rc</filename>
|
|
invokes them by means of <function>run_rc_script</function>,
|
|
then study the implementation of
|
|
<function>run_rc_script</function> in
|
|
<filename>/etc/rc.subr</filename>.</para>
|
|
</tip>
|
|
</callout>
|
|
|
|
<callout arearefs="rcng-dummy-include">
|
|
<para>In <filename>/etc/rc.subr</filename>, a number of
|
|
&man.sh.1; functions are defined for an <filename>rc.d</filename>
|
|
script to use. The functions are documented in
|
|
&man.rc.subr.8;. While it is theoretically possible to
|
|
write an <filename>rc.d</filename> script without ever
|
|
using &man.rc.subr.8;, its functions prove extremely handy
|
|
and make the job an order of magnitude easier. So it is
|
|
no surprise that everybody resorts to &man.rc.subr.8; in
|
|
<filename>rc.d</filename> scripts. We are not going to
|
|
be an exception.</para>
|
|
|
|
<para>An <filename>rc.d</filename> script must
|
|
<quote>source</quote> <filename>/etc/rc.subr</filename>
|
|
(include it using <quote><command>.</command></quote>)
|
|
<emphasis>before</emphasis> it calls &man.rc.subr.8;
|
|
functions so that &man.sh.1; has an opportunity to learn
|
|
the functions. The preferred style is to source
|
|
<filename>/etc/rc.subr</filename> first of all.</para>
|
|
|
|
<note>
|
|
<para>Some useful functions related to networking
|
|
are provided by another include file,
|
|
<filename>/etc/network.subr</filename>.</para>
|
|
</note>
|
|
</callout>
|
|
|
|
<callout arearefs="rcng-dummy-name">
|
|
<para><anchor xml:id="name-var"/>The mandatory variable
|
|
<envar>name</envar> specifies the name of our script. It
|
|
is required by &man.rc.subr.8;. That is, each
|
|
<filename>rc.d</filename> script <emphasis>must</emphasis>
|
|
set <envar>name</envar> before it calls &man.rc.subr.8;
|
|
functions.</para>
|
|
|
|
<para>Now it is the right time to choose a unique name for
|
|
our script once and for all. We will use it in a number
|
|
of places while developing the script. For a start, let
|
|
us give the same name to the script file, too.</para>
|
|
|
|
<note>
|
|
<para>The current style of <filename>rc.d</filename>
|
|
scripting is to enclose values assigned to variables
|
|
in double quotes. Keep in mind that it is just a style
|
|
issue that may not always be applicable. You can
|
|
safely omit quotes from around simple words without
|
|
&man.sh.1; metacharacters in them, while in certain
|
|
cases you will need single quotes to prevent any
|
|
interpretation of the value by &man.sh.1;. A programmer
|
|
should be able to tell the language syntax from style
|
|
conventions and use both of them wisely.</para>
|
|
</note>
|
|
</callout>
|
|
|
|
<callout arearefs="rcng-dummy-startcmd">
|
|
<para>The main idea behind &man.rc.subr.8; is that an
|
|
<filename>rc.d</filename> script provides handlers, or
|
|
methods, for &man.rc.subr.8; to invoke. In particular,
|
|
<option>start</option>, <option>stop</option>, and other
|
|
arguments to an <filename>rc.d</filename> script are
|
|
handled this way. A method is a &man.sh.1; expression
|
|
stored in a variable named
|
|
<envar><replaceable>argument</replaceable>_cmd</envar>,
|
|
where <replaceable>argument</replaceable> corresponds to
|
|
what can be specified on the script's command line. We
|
|
will see later how &man.rc.subr.8; provides default methods
|
|
for the standard arguments.</para>
|
|
|
|
<note>
|
|
<para>To make the code in <filename>rc.d</filename> more
|
|
uniform, it is common to use <envar>${name}</envar>
|
|
wherever appropriate. Thus a number of lines can be just
|
|
copied from one script to another.</para>
|
|
</note>
|
|
</callout>
|
|
|
|
<callout arearefs="rcng-dummy-stopcmd">
|
|
<para>We should keep in mind that &man.rc.subr.8; provides
|
|
default methods for the standard arguments. Consequently,
|
|
we must override a standard method with a no-op &man.sh.1;
|
|
expression if we want it to do nothing.</para>
|
|
</callout>
|
|
|
|
<callout arearefs="rcng-dummy-startfn">
|
|
<para>The body of a sophisticated method can be implemented
|
|
as a function. It is a good idea to make the function
|
|
name meaningful.</para>
|
|
|
|
<important>
|
|
<para>It is strongly recommended to add the prefix
|
|
<envar>${name}</envar> to the names of all functions
|
|
defined in our script so they never clash with the
|
|
functions from &man.rc.subr.8; or another common include
|
|
file.</para>
|
|
</important>
|
|
</callout>
|
|
|
|
<callout arearefs="rcng-dummy-loadconfig">
|
|
<para>This call to &man.rc.subr.8; loads &man.rc.conf.5;
|
|
variables. Our script makes no use of them yet, but it
|
|
still is recommended to load &man.rc.conf.5; because there
|
|
can be &man.rc.conf.5; variables controlling &man.rc.subr.8;
|
|
itself.</para>
|
|
</callout>
|
|
|
|
<callout arearefs="rcng-dummy-runcommand">
|
|
<para>Usually this is the last command in an
|
|
<filename>rc.d</filename> script. It invokes the
|
|
&man.rc.subr.8; machinery to perform the requested action
|
|
using the variables and methods our script has provided.</para>
|
|
</callout>
|
|
</calloutlist>
|
|
</sect1>
|
|
|
|
<sect1 xml:id="rcng-confdummy">
|
|
<title>A configurable dummy script</title>
|
|
|
|
<para>Now let us add some controls to our dummy script. As you
|
|
may know, <filename>rc.d</filename> scripts are controlled
|
|
with &man.rc.conf.5;. Fortunately, &man.rc.subr.8; hides all
|
|
the complications from us. The following script uses
|
|
&man.rc.conf.5; via &man.rc.subr.8; to see whether it is
|
|
enabled in the first place, and to fetch a message to show
|
|
at boot time. These two tasks in fact are independent. On
|
|
the one hand, an <filename>rc.d</filename> script can just
|
|
support enabling and disabling its service. On the other
|
|
hand, a mandatory <filename>rc.d</filename> script can have
|
|
configuration variables. We will do both things in the same
|
|
script though:</para>
|
|
|
|
<informalexample>
|
|
<programlisting>#!/bin/sh
|
|
|
|
. /etc/rc.subr
|
|
|
|
name=dummy
|
|
rcvar=dummy_enable<co xml:id="rcng-confdummy-rcvar"/>
|
|
|
|
start_cmd="${name}_start"
|
|
stop_cmd=":"
|
|
|
|
load_rc_config $name<co xml:id="rcng-confdummy-loadconfig"/>
|
|
: ${dummy_enable:=no} <co xml:id="rcng-confdummy-enable"/>
|
|
: ${dummy_msg="Nothing started."}<co xml:id="rcng-confdummy-opt"/>
|
|
|
|
dummy_start()
|
|
{
|
|
echo "$dummy_msg"<co xml:id="rcng-confdummy-msg"/>
|
|
}
|
|
|
|
run_rc_command "$1"</programlisting>
|
|
</informalexample>
|
|
|
|
<para>What changed in this example?</para>
|
|
|
|
<calloutlist>
|
|
<callout arearefs="rcng-confdummy-rcvar">
|
|
<para>The variable <envar>rcvar</envar> specifies
|
|
the name of the ON/OFF knob variable.</para>
|
|
</callout>
|
|
|
|
<callout arearefs="rcng-confdummy-loadconfig">
|
|
<para>Now <function>load_rc_config</function> is invoked
|
|
earlier in the script, before any &man.rc.conf.5; variables
|
|
are accessed.</para>
|
|
|
|
<note>
|
|
<para>While examining <filename>rc.d</filename> scripts,
|
|
keep in mind that &man.sh.1; defers the evaluation of
|
|
expressions in a function until the latter is called.
|
|
Therefore it is not an error to invoke
|
|
<function>load_rc_config</function> as late as just
|
|
before <function>run_rc_command</function> and still
|
|
access &man.rc.conf.5; variables from the method functions
|
|
exported to <function>run_rc_command</function>. This
|
|
is because the method functions are to be called by
|
|
<function>run_rc_command</function>, which is invoked
|
|
<emphasis>after</emphasis>
|
|
<function>load_rc_config</function>.</para>
|
|
</note>
|
|
</callout>
|
|
|
|
<callout arearefs="rcng-confdummy-enable">
|
|
<para>A warning will be emitted by
|
|
<function>run_rc_command</function> if <envar>rcvar</envar>
|
|
itself is set, but the indicated knob variable is unset.
|
|
If your <filename>rc.d</filename> script is for the base
|
|
system, you should add a default setting for the knob to
|
|
<filename>/etc/defaults/rc.conf</filename> and document
|
|
it in &man.rc.conf.5;. Otherwise it is your script that
|
|
should provide a default setting for the knob. The canonical
|
|
approach to the latter case is shown in the example.</para>
|
|
|
|
<note>
|
|
<para>You can make &man.rc.subr.8; act as though the knob
|
|
is set to <literal>ON</literal>, irrespective of its
|
|
current setting, by prefixing the argument to the script
|
|
with <literal>one</literal> or <literal>force</literal>,
|
|
as in <option>onestart</option> or <option>forcestop</option>.
|
|
Keep in mind though that <literal>force</literal> has
|
|
other dangerous effects we will touch upon below, while
|
|
<literal>one</literal> just overrides the ON/OFF knob.
|
|
E.g., assume that <envar>dummy_enable</envar> is
|
|
<literal>OFF</literal>. The following command will run
|
|
the <option>start</option> method in spite of the
|
|
setting:</para>
|
|
|
|
<screen>&prompt.root; <userinput>/etc/rc.d/dummy onestart</userinput></screen>
|
|
</note>
|
|
</callout>
|
|
|
|
<callout arearefs="rcng-confdummy-opt">
|
|
<para>Now the message to be shown at boot time is no
|
|
longer hard-coded in the script. It is specified by an
|
|
&man.rc.conf.5; variable named <envar>dummy_msg</envar>.
|
|
This is a trivial example of how &man.rc.conf.5; variables
|
|
can control an <filename>rc.d</filename> script.</para>
|
|
|
|
<important>
|
|
<para>The names of all &man.rc.conf.5; variables used
|
|
exclusively by our script <emphasis>must</emphasis>
|
|
have the same prefix: <envar>${name}_</envar>. For
|
|
example: <envar>dummy_mode</envar>,
|
|
<envar>dummy_state_file</envar>, and so on.</para>
|
|
</important>
|
|
|
|
<note>
|
|
<para>While it is possible to use a shorter name internally,
|
|
e.g., just <envar>msg</envar>, adding the unique prefix
|
|
<envar>${name}_</envar> to all global names introduced by
|
|
our script will save us from possible
|
|
collisions with the &man.rc.subr.8; namespace.</para>
|
|
|
|
<para>As a rule, <filename>rc.d</filename> scripts of the
|
|
base system need not provide defaults for their
|
|
&man.rc.conf.5; variables because the defaults should
|
|
be set in <filename>/etc/defaults/rc.conf</filename>
|
|
instead. On the other hand, <filename>rc.d</filename>
|
|
scripts for ports should provide the defaults as shown
|
|
in the example.</para>
|
|
</note>
|
|
</callout>
|
|
|
|
<callout arearefs="rcng-confdummy-msg">
|
|
<para>Here we use <envar>dummy_msg</envar> to actually
|
|
control our script, i.e., to emit a variable message.
|
|
Use of a shell function is overkill here, since it only
|
|
runs a single command; an equally valid alternative is:</para>
|
|
|
|
<programlisting>start_cmd="echo \"$dummy_msg\""</programlisting>
|
|
</callout>
|
|
</calloutlist>
|
|
</sect1>
|
|
|
|
<sect1 xml:id="rcng-daemon">
|
|
<title>Startup and shutdown of a simple daemon</title>
|
|
|
|
<para>We said earlier that &man.rc.subr.8; could provide default
|
|
methods. Obviously, such defaults cannot be too general.
|
|
They are suited for the common case of starting and shutting
|
|
down a simple daemon program. Let us assume now that we need
|
|
to write an <filename>rc.d</filename> script for such a daemon
|
|
called <command>mumbled</command>. Here it is:</para>
|
|
|
|
<informalexample>
|
|
<programlisting>#!/bin/sh
|
|
|
|
. /etc/rc.subr
|
|
|
|
name=mumbled
|
|
rcvar=mumbled_enable
|
|
|
|
command="/usr/sbin/${name}"<co xml:id="rcng-daemon-basic-cmd"/>
|
|
|
|
load_rc_config $name
|
|
run_rc_command "$1"</programlisting>
|
|
</informalexample>
|
|
|
|
<para>Pleasingly simple, isn't it? Let us examine our little
|
|
script. The only new thing to note is as follows:</para>
|
|
|
|
<calloutlist>
|
|
<callout arearefs="rcng-daemon-basic-cmd">
|
|
<para>The <envar>command</envar> variable is meaningful to
|
|
&man.rc.subr.8;. If it is set, &man.rc.subr.8; will act
|
|
according to the scenario of serving a conventional daemon.
|
|
In particular, the default methods will be provided for
|
|
such arguments: <option>start</option>, <option>stop</option>,
|
|
<option>restart</option>, <option>poll</option>, and
|
|
<option>status</option>.</para>
|
|
|
|
<para>The daemon will be started by running
|
|
<envar>$command</envar> with command-line flags specified
|
|
by <envar>$mumbled_flags</envar>. Thus all the input
|
|
data for the default <option>start</option> method are
|
|
available in the variables set by our script. Unlike
|
|
<option>start</option>, other methods may require additional
|
|
information about the process started. For instance,
|
|
<option>stop</option> must know the PID of the process
|
|
to terminate it. In the present case, &man.rc.subr.8;
|
|
will scan through the list of all processes, looking for
|
|
a process with its name equal to <envar>$procname</envar>.
|
|
The latter is another variable of meaning to &man.rc.subr.8;,
|
|
and its value defaults to that of <envar>command</envar>.
|
|
In other words, when we set <envar>command</envar>,
|
|
<envar>procname</envar> is effectively set to the same
|
|
value. This enables our script to kill the daemon and
|
|
to check if it is running in the first place.</para>
|
|
|
|
<note>
|
|
<para>Some programs are in fact executable scripts. The
|
|
system runs such a script by starting its interpreter
|
|
and passing the name of the script to it as a command-line
|
|
argument. This is reflected in the list of processes,
|
|
which can confuse &man.rc.subr.8;. You should additionally
|
|
set <envar>command_interpreter</envar> to let &man.rc.subr.8;
|
|
know the actual name of the process if <envar>$command</envar>
|
|
is a script.</para>
|
|
|
|
<para>For each <filename>rc.d</filename> script, there
|
|
is an optional &man.rc.conf.5; variable that takes
|
|
precedence over <envar>command</envar>. Its name is
|
|
constructed as follows: <envar>${name}_program</envar>,
|
|
where <envar>name</envar> is the mandatory variable we
|
|
discussed <link linkend="name-var">earlier</link>.
|
|
E.g., in this case it will be <envar>mumbled_program</envar>.
|
|
It is &man.rc.subr.8; that arranges
|
|
<envar>${name}_program</envar> to override
|
|
<envar>command</envar>.</para>
|
|
|
|
<para>Of course, &man.sh.1; will permit you to set
|
|
<envar>${name}_program</envar> from &man.rc.conf.5; or
|
|
the script itself even if <envar>command</envar> is
|
|
unset. In that case, the special properties of
|
|
<envar>${name}_program</envar> are lost, and it becomes
|
|
an ordinary variable your script can use for its own
|
|
purposes. However, the sole use of
|
|
<envar>${name}_program</envar> is discouraged because
|
|
using it together with <envar>command</envar> became
|
|
an idiom of <filename>rc.d</filename> scripting.</para>
|
|
</note>
|
|
|
|
<para>For more detailed information on default methods,
|
|
refer to &man.rc.subr.8;.</para>
|
|
</callout>
|
|
</calloutlist>
|
|
</sect1>
|
|
|
|
<sect1 xml:id="rcng-daemon-adv">
|
|
<title>Startup and shutdown of an advanced daemon</title>
|
|
|
|
<para>Let us add some meat onto the bones of the previous
|
|
script and make it more complex and featureful. The default
|
|
methods can do a good job for us, but we may need some of
|
|
their aspects tweaked. Now we will learn how to tune the
|
|
default methods to our needs.</para>
|
|
|
|
<informalexample>
|
|
<programlisting>#!/bin/sh
|
|
|
|
. /etc/rc.subr
|
|
|
|
name=mumbled
|
|
rcvar=mumbled_enable
|
|
|
|
command="/usr/sbin/${name}"
|
|
command_args="mock arguments > /dev/null 2>&1"<co xml:id="rcng-daemon-adv-args"/>
|
|
|
|
pidfile="/var/run/${name}.pid"<co xml:id="rcng-daemon-adv-pid"/>
|
|
|
|
required_files="/etc/${name}.conf /usr/share/misc/${name}.rules"<co xml:id="rcng-daemon-adv-reqfiles"/>
|
|
|
|
sig_reload="USR1"<co xml:id="rcng-daemon-adv-sig"/>
|
|
|
|
start_precmd="${name}_prestart"<co xml:id="rcng-daemon-adv-precmd"/>
|
|
stop_postcmd="echo Bye-bye"<co xml:id="rcng-daemon-adv-postcmd"/>
|
|
|
|
extra_commands="reload plugh xyzzy"<co xml:id="rcng-daemon-adv-extra"/>
|
|
|
|
plugh_cmd="mumbled_plugh"<co xml:id="rcng-daemon-adv-methods"/>
|
|
xyzzy_cmd="echo 'Nothing happens.'"
|
|
|
|
mumbled_prestart()
|
|
{
|
|
if checkyesno mumbled_smart; then<co xml:id="rcng-daemon-adv-yn"/>
|
|
rc_flags="-o smart ${rc_flags}"<co xml:id="rcng-daemon-adv-rcflags"/>
|
|
fi
|
|
case "$mumbled_mode" in
|
|
foo)
|
|
rc_flags="-frotz ${rc_flags}"
|
|
;;
|
|
bar)
|
|
rc_flags="-baz ${rc_flags}"
|
|
;;
|
|
*)
|
|
warn "Invalid value for mumbled_mode"<co xml:id="rcng-daemon-adv-warn"/>
|
|
return 1<co xml:id="rcng-daemon-adv-preret"/>
|
|
;;
|
|
esac
|
|
run_rc_command xyzzy<co xml:id="rcng-daemon-adv-run"/>
|
|
return 0
|
|
}
|
|
|
|
mumbled_plugh()<co xml:id="rcng-daemon-adv-plugh"/>
|
|
{
|
|
echo 'A hollow voice says "plugh".'
|
|
}
|
|
|
|
load_rc_config $name
|
|
run_rc_command "$1"</programlisting>
|
|
</informalexample>
|
|
|
|
<calloutlist>
|
|
<callout arearefs="rcng-daemon-adv-args">
|
|
<para>Additional arguments to <envar>$command</envar> can
|
|
be passed in <envar>command_args</envar>. They will be
|
|
added to the command line after <envar>$mumbled_flags</envar>.
|
|
Since the final command line is passed to <command>eval</command>
|
|
for its actual execution, input and output redirections
|
|
can be specified in <envar>command_args</envar>.</para>
|
|
|
|
<note>
|
|
<para><emphasis>Never</emphasis> include dashed options,
|
|
like <option>-X</option> or <option>--foo</option>, in
|
|
<envar>command_args</envar>.
|
|
The contents of <envar>command_args</envar> will
|
|
appear at the end of the final command line, hence
|
|
they are likely to follow arguments present in
|
|
<envar>${name}_flags</envar>; but most commands will
|
|
not recognize dashed options after ordinary arguments.
|
|
A better way of passing additional options
|
|
to <envar>$command</envar> is to add them
|
|
to the beginning of <envar>${name}_flags</envar>.
|
|
Another way is to modify <envar>rc_flags</envar> <link linkend="rc-flags">as shown later</link>.</para>
|
|
</note>
|
|
</callout>
|
|
|
|
<callout arearefs="rcng-daemon-adv-pid">
|
|
<para>A good-mannered daemon should create a
|
|
<emphasis>pidfile</emphasis> so that its process can be
|
|
found more easily and reliably. The variable
|
|
<envar>pidfile</envar>, if set, tells &man.rc.subr.8;
|
|
where it can find the pidfile for its default methods to
|
|
use.</para>
|
|
|
|
<note>
|
|
<para>In fact, &man.rc.subr.8; will also use the pidfile
|
|
to see if the daemon is already running before starting
|
|
it. This check can be skipped by using the
|
|
<option>faststart</option> argument.</para>
|
|
</note>
|
|
</callout>
|
|
|
|
<callout arearefs="rcng-daemon-adv-reqfiles">
|
|
<para>If the daemon cannot run unless certain files exist,
|
|
just list them in <envar>required_files</envar>, and
|
|
&man.rc.subr.8; will check that those files do exist
|
|
before starting the daemon. There also are
|
|
<envar>required_dirs</envar> and <envar>required_vars</envar>
|
|
for directories and environment variables, respectively.
|
|
They all are described in detail in &man.rc.subr.8;.</para>
|
|
|
|
<note>
|
|
<para>The default method from &man.rc.subr.8; can be
|
|
forced to skip the prerequisite checks by using
|
|
<option>forcestart</option> as the argument to the
|
|
script.</para>
|
|
</note>
|
|
</callout>
|
|
|
|
<callout arearefs="rcng-daemon-adv-sig">
|
|
<para>We can customize signals to send to the daemon in
|
|
case they differ from the well-known ones. In particular,
|
|
<envar>sig_reload</envar> specifies the signal that makes
|
|
the daemon reload its configuration; it is
|
|
<symbol>SIGHUP</symbol> by default. Another signal is
|
|
sent to stop the daemon process; the default is
|
|
<symbol>SIGTERM</symbol>, but this can be changed by
|
|
setting <envar>sig_stop</envar> appropriately.</para>
|
|
|
|
<note>
|
|
<para>The signal names should be specified to &man.rc.subr.8;
|
|
without the <literal>SIG</literal> prefix, as it is
|
|
shown in the example. The &os; version of &man.kill.1;
|
|
can recognize the <literal>SIG</literal> prefix, but
|
|
the versions from other OS types may not.</para>
|
|
</note>
|
|
</callout>
|
|
|
|
<callout arearefs="rcng-daemon-adv-precmd rcng-daemon-adv-postcmd">
|
|
<para>Performing additional tasks before or after the default
|
|
methods is easy. For each command-argument supported by
|
|
our script, we can define
|
|
<envar><replaceable>argument</replaceable>_precmd</envar> and
|
|
<envar><replaceable>argument</replaceable>_postcmd</envar>.
|
|
These &man.sh.1; commands are invoked before and after
|
|
the respective method, as it is evident from their
|
|
names.</para>
|
|
|
|
<note>
|
|
<para>Overriding a default method with a custom
|
|
<envar><replaceable>argument</replaceable>_cmd</envar>
|
|
still does not prevent us from making use of
|
|
<envar><replaceable>argument</replaceable>_precmd</envar> or
|
|
<envar><replaceable>argument</replaceable>_postcmd</envar>
|
|
if we need to. In particular, the former is good for
|
|
checking custom, sophisticated conditions that should
|
|
be met before performing the command itself. Using
|
|
<envar><replaceable>argument</replaceable>_precmd</envar> along
|
|
with <envar><replaceable>argument</replaceable>_cmd</envar>
|
|
lets us logically separate the checks from the
|
|
action.</para>
|
|
|
|
<para>Do not forget that you can cram any valid &man.sh.1;
|
|
expressions into the methods, pre-, and post-commands
|
|
you define. Just invoking a function that makes the
|
|
real job is a good style in most cases, but never let
|
|
style limit your understanding of what is going on
|
|
behind the curtain.</para>
|
|
</note>
|
|
</callout>
|
|
|
|
<callout arearefs="rcng-daemon-adv-extra">
|
|
<para>If we would like to implement custom arguments, which
|
|
can also be thought of as <emphasis>commands</emphasis>
|
|
to our script, we need to list them in
|
|
<envar>extra_commands</envar> and provide methods to
|
|
handle them.</para>
|
|
|
|
<note>
|
|
<para>The <option>reload</option> command is special. On
|
|
the one hand, it has a preset method in &man.rc.subr.8;.
|
|
On the other hand, <option>reload</option> is not offered
|
|
by default. The reason is that not all daemons use the
|
|
same reload mechanism and some have nothing to reload
|
|
at all. So we need to ask explicitly that the builtin
|
|
functionality be provided. We can do so via
|
|
<envar>extra_commands</envar>.</para>
|
|
|
|
<para>What do we get from the default method for
|
|
<option>reload</option>? Quite often daemons reload
|
|
their configuration upon reception of a signal —
|
|
typically, <symbol>SIGHUP</symbol>. Therefore
|
|
&man.rc.subr.8; attempts to reload the daemon by sending
|
|
a signal to it. The signal is preset to
|
|
<symbol>SIGHUP</symbol> but can be customized via
|
|
<envar>sig_reload</envar> if necessary.</para>
|
|
</note>
|
|
</callout>
|
|
|
|
<callout arearefs="rcng-daemon-adv-methods rcng-daemon-adv-plugh">
|
|
<para>Our script supports two non-standard commands,
|
|
<option>plugh</option> and <option>xyzzy</option>. We
|
|
saw them listed in <envar>extra_commands</envar>, and now
|
|
it is time to provide methods for them. The method for
|
|
<option>xyzzy</option> is just inlined while that for
|
|
<option>plugh</option> is implemented as the
|
|
<function>mumbled_plugh</function> function.</para>
|
|
|
|
<para>Non-standard commands are not invoked during startup
|
|
or shutdown. Usually they are for the system admin's
|
|
convenience. They can also be used from other subsystems,
|
|
e.g., &man.devd.8; if specified in &man.devd.conf.5;.</para>
|
|
|
|
<para>The full list of available commands can be found in
|
|
the usage line printed by &man.rc.subr.8; when the script
|
|
is invoked without arguments. For example, here is the
|
|
usage line from the script under study:</para>
|
|
|
|
<screen>&prompt.root; <userinput>/etc/rc.d/mumbled</userinput>
|
|
Usage: /etc/rc.d/mumbled [fast|force|one](start|stop|restart|rcvar|reload|plugh|xyzzy|status|poll)</screen>
|
|
</callout>
|
|
|
|
<callout arearefs="rcng-daemon-adv-run">
|
|
<para>A script can invoke its own standard or non-standard
|
|
commands if needed. This may look similar to calling
|
|
functions, but we know that commands and shell functions
|
|
are not always the same thing. For instance,
|
|
<command>xyzzy</command> is not implemented as a function
|
|
here. In addition, there can be a pre-command and
|
|
post-command, which should be invoked orderly. So the
|
|
proper way for a script to run its own command is by means
|
|
of &man.rc.subr.8;, as shown in the example.</para>
|
|
</callout>
|
|
|
|
<callout arearefs="rcng-daemon-adv-yn">
|
|
<para>A handy function named <function>checkyesno</function>
|
|
is provided by &man.rc.subr.8;. It takes a variable name
|
|
as its argument and returns a zero exit code if and only
|
|
if the variable is set to <literal>YES</literal>, or
|
|
<literal>TRUE</literal>, or <literal>ON</literal>, or
|
|
<literal>1</literal>, case insensitive; a non-zero exit
|
|
code is returned otherwise. In the latter case, the
|
|
function tests the variable for being set to
|
|
<literal>NO</literal>, <literal>FALSE</literal>,
|
|
<literal>OFF</literal>, or <literal>0</literal>, case
|
|
insensitive; it prints a warning message if the variable
|
|
contains anything else, i.e., junk.</para>
|
|
|
|
<para>Keep in mind that for &man.sh.1; a zero exit code
|
|
means true and a non-zero exit code means false.</para>
|
|
|
|
<important>
|
|
<para>The <function>checkyesno</function> function takes
|
|
a <emphasis>variable name</emphasis>. Do not pass the
|
|
expanded <emphasis>value</emphasis> of a variable to
|
|
it; it will not work as expected.</para>
|
|
|
|
<para>The following is the correct usage of
|
|
<function>checkyesno</function>:</para>
|
|
|
|
<programlisting>if checkyesno mumbled_enable; then
|
|
foo
|
|
fi</programlisting>
|
|
|
|
<para>On the contrary, calling <function>checkyesno</function>
|
|
as shown below will not work — at least not as
|
|
expected:</para>
|
|
|
|
<programlisting>if checkyesno "${mumbled_enable}"; then
|
|
foo
|
|
fi</programlisting>
|
|
</important>
|
|
</callout>
|
|
|
|
<callout arearefs="rcng-daemon-adv-rcflags">
|
|
<para><anchor xml:id="rc-flags"/>We can affect the flags to be
|
|
passed to <envar>$command</envar> by modifying
|
|
<envar>rc_flags</envar> in <envar>$start_precmd</envar>.</para>
|
|
</callout>
|
|
|
|
<callout arearefs="rcng-daemon-adv-warn">
|
|
<para>In certain cases we may need to emit an important
|
|
message that should go to <application>syslog</application>
|
|
as well. This can be done easily with the following
|
|
&man.rc.subr.8; functions: <function>debug</function>,
|
|
<function>info</function>, <function>warn</function>, and
|
|
<function>err</function>. The latter function then exits
|
|
the script with the code specified.</para>
|
|
</callout>
|
|
|
|
<callout arearefs="rcng-daemon-adv-preret">
|
|
<para>The exit codes from methods and their pre-commands
|
|
are not just ignored by default. If
|
|
<envar><replaceable>argument</replaceable>_precmd</envar> returns
|
|
a non-zero exit code, the main method will not be performed.
|
|
In turn,
|
|
<envar><replaceable>argument</replaceable>_postcmd</envar> will
|
|
not be invoked unless the main method returns a zero exit
|
|
code.</para>
|
|
|
|
<note>
|
|
<para>However, &man.rc.subr.8; can be instructed from the
|
|
command line to ignore those exit codes and invoke all
|
|
commands anyway by prefixing an argument with
|
|
<literal>force</literal>, as in
|
|
<option>forcestart</option>.</para>
|
|
</note>
|
|
</callout>
|
|
</calloutlist>
|
|
</sect1>
|
|
|
|
<sect1 xml:id="rcng-hookup">
|
|
<title>Connecting a script to the rc.d framework</title>
|
|
|
|
<para>After a script has been written, it needs to be integrated
|
|
into <filename>rc.d</filename>. The crucial step is to install
|
|
the script in <filename>/etc/rc.d</filename> (for the base
|
|
system) or <filename>/usr/local/etc/rc.d</filename> (for
|
|
ports). Both <<filename>bsd.prog.mk</filename>> and
|
|
<<filename>bsd.port.mk</filename>> provide convenient
|
|
hooks for that, and usually you do not have to worry about
|
|
the proper ownership and mode. System scripts should be
|
|
installed from <filename>src/etc/rc.d</filename> through the
|
|
<filename>Makefile</filename> found there. Port scripts can
|
|
be installed using <varname>USE_RC_SUBR</varname> as described
|
|
<link xlink:href="&url.books.porters-handbook;/rc-scripts.html">in
|
|
the Porter's Handbook</link>.</para>
|
|
|
|
<para>However, we should consider beforehand the place of
|
|
our script in the system startup sequence. The service handled
|
|
by our script is likely to depend on other services. For
|
|
instance, a network daemon cannot function without the network
|
|
interfaces and routing up and running. Even if a service
|
|
seems to demand nothing, it can hardly start before the basic
|
|
filesystems have been checked and mounted.</para>
|
|
|
|
<para>We mentioned &man.rcorder.8; already. Now it is time to
|
|
have a close look at it. In a nutshell, &man.rcorder.8; takes
|
|
a set of files, examines their contents, and prints a
|
|
dependency-ordered list of files from the set to
|
|
<varname>stdout</varname>. The point is to keep dependency
|
|
information <emphasis>inside</emphasis> the files so that
|
|
each file can speak for itself only. A file can specify the
|
|
following information:</para>
|
|
|
|
<itemizedlist>
|
|
<listitem>
|
|
<para>the names of the <quote>conditions</quote> (which means
|
|
services to us) it <emphasis>provides</emphasis>;</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>the names of the <quote>conditions</quote>
|
|
it <emphasis>requires</emphasis>;</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>the names of the <quote>conditions</quote> this file
|
|
should run <emphasis>before</emphasis>;</para>
|
|
</listitem>
|
|
|
|
<listitem>
|
|
<para>additional <emphasis>keywords</emphasis> that can be
|
|
used to select a subset from the whole set of files
|
|
(&man.rcorder.8; can be instructed via options to include
|
|
or omit the files having particular keywords listed.)</para>
|
|
</listitem>
|
|
</itemizedlist>
|
|
|
|
<para>It is no surprise that &man.rcorder.8; can handle only
|
|
text files with a syntax close to that of &man.sh.1;. That
|
|
is, special lines understood by &man.rcorder.8; look like
|
|
&man.sh.1; comments. The syntax of such special lines is
|
|
rather rigid to simplify their processing. See &man.rcorder.8;
|
|
for details.</para>
|
|
|
|
<para>Besides using &man.rcorder.8; special lines, a script can
|
|
insist on its dependency upon another service by just starting
|
|
it forcibly. This can be needed when the other service is
|
|
optional and will not start by itself because the system admin
|
|
has disabled it mistakenly in &man.rc.conf.5;.</para>
|
|
|
|
<para>With this general knowledge in mind, let us consider the
|
|
simple daemon script enhanced with dependency stuff:</para>
|
|
|
|
<informalexample>
|
|
<programlisting>#!/bin/sh
|
|
|
|
# PROVIDE: mumbled oldmumble <co xml:id="rcng-hookup-provide"/>
|
|
# REQUIRE: DAEMON cleanvar frotz<co xml:id="rcng-hookup-require"/>
|
|
# BEFORE: LOGIN<co xml:id="rcng-hookup-before"/>
|
|
# KEYWORD: nojail shutdown<co xml:id="rcng-hookup-keyword"/>
|
|
|
|
. /etc/rc.subr
|
|
|
|
name=mumbled
|
|
rcvar=mumbled_enable
|
|
|
|
command="/usr/sbin/${name}"
|
|
start_precmd="${name}_prestart"
|
|
|
|
mumbled_prestart()
|
|
{
|
|
if ! checkyesno frotz_enable && \
|
|
! /etc/rc.d/frotz forcestatus 1>/dev/null 2>&1; then
|
|
force_depend frotz || return 1<co xml:id="rcng-hookup-force"/>
|
|
fi
|
|
return 0
|
|
}
|
|
|
|
load_rc_config $name
|
|
run_rc_command "$1"</programlisting>
|
|
</informalexample>
|
|
|
|
<para>As before, detailed analysis follows:</para>
|
|
|
|
<calloutlist>
|
|
<callout arearefs="rcng-hookup-provide">
|
|
<para>That line declares the names of <quote>conditions</quote>
|
|
our script provides. Now other scripts can record a
|
|
dependency on our script by those names.</para>
|
|
|
|
<note>
|
|
<para>Usually a script specifies a single condition
|
|
provided. However, nothing prevents us from listing
|
|
several conditions there, e.g., for compatibility
|
|
reasons.</para>
|
|
|
|
<para>In any case, the name of the main, or the only,
|
|
<literal>PROVIDE:</literal> condition should be the
|
|
same as <envar>${name}</envar>.</para>
|
|
</note>
|
|
</callout>
|
|
|
|
<callout arearefs="rcng-hookup-require rcng-hookup-before">
|
|
<para>So our script indicates which <quote>conditions</quote>
|
|
provided by other scripts it depends on. According to
|
|
the lines, our script asks &man.rcorder.8; to put it after
|
|
the script(s) providing <filename>DAEMON</filename> and
|
|
<filename>cleanvar</filename>, but before that providing
|
|
<filename>LOGIN</filename>.</para>
|
|
|
|
<note>
|
|
<para>The <literal>BEFORE:</literal> line should not be
|
|
abused to work around an incomplete dependency list in
|
|
the other script. The appropriate case for using
|
|
<literal>BEFORE:</literal> is when the other script
|
|
does not care about ours, but our script can do its
|
|
task better if run before the other one. A typical
|
|
real-life example is the network interfaces vs. the
|
|
firewall: While the interfaces do not depend on the
|
|
firewall in doing their job, the system security will
|
|
benefit from the firewall being ready before there is
|
|
any network traffic.</para>
|
|
|
|
<para>Besides conditions corresponding to a single service
|
|
each, there are meta-conditions and their
|
|
<quote>placeholder</quote> scripts used to ensure that
|
|
certain groups of operations are performed before others.
|
|
These are denoted by
|
|
<filename>UPPERCASE</filename>
|
|
names. Their list and purposes can be found in
|
|
&man.rc.8;.</para>
|
|
|
|
<para>Keep in mind that putting a service name in the
|
|
<literal>REQUIRE:</literal> line does not guarantee
|
|
that the service will actually be running by the time
|
|
our script starts. The required service may fail to
|
|
start or just be disabled in &man.rc.conf.5;. Obviously,
|
|
&man.rcorder.8; cannot track such details, and &man.rc.8;
|
|
will not do that either. Consequently, the application
|
|
started by our script should be able to cope with any
|
|
required services being unavailable. In certain cases,
|
|
we can help it as discussed <link linkend="forcedep">below.</link></para>
|
|
</note>
|
|
</callout>
|
|
|
|
<callout arearefs="rcng-hookup-keyword">
|
|
<para><anchor xml:id="keywords"/>As we remember from the above text,
|
|
&man.rcorder.8; keywords can be used to select or leave
|
|
out some scripts. Namely any &man.rcorder.8; consumer
|
|
can specify through <option>-k</option> and <option>-s</option>
|
|
options which keywords are on the <quote>keep list</quote> and
|
|
<quote>skip list</quote>, respectively. From all the
|
|
files to be dependency sorted, &man.rcorder.8; will pick
|
|
only those having a keyword from the keep list (unless empty)
|
|
and not having a keyword from the skip list.</para>
|
|
|
|
<para>In &os;, &man.rcorder.8; is used by
|
|
<filename>/etc/rc</filename> and
|
|
<filename>/etc/rc.shutdown</filename>. These two scripts
|
|
define the standard list of &os; <filename>rc.d</filename>
|
|
keywords and their meanings as follows:</para>
|
|
|
|
<variablelist>
|
|
<varlistentry>
|
|
<term><literal>nojail</literal></term>
|
|
|
|
<listitem>
|
|
<para>The service is not for &man.jail.8; environment.
|
|
The automatic startup and shutdown procedures will
|
|
ignore the script if inside a jail.</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
|
|
<varlistentry>
|
|
<term><literal>nostart</literal></term>
|
|
|
|
<listitem>
|
|
<para>The service is to be started manually or not
|
|
started at all. The automatic startup procedure
|
|
will ignore the script. In conjunction with the
|
|
<literal>shutdown</literal> keyword, this can be
|
|
used to write scripts that do something only at
|
|
system shutdown.</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
|
|
<varlistentry>
|
|
<term><literal>shutdown</literal></term>
|
|
|
|
<listitem>
|
|
<para>This keyword is to be listed
|
|
<emphasis>explicitly</emphasis> if the service needs
|
|
to be stopped before system shutdown.</para>
|
|
|
|
<note>
|
|
<para>When the system is going to shut down,
|
|
<filename>/etc/rc.shutdown</filename> runs. It
|
|
assumes that most <filename>rc.d</filename> scripts
|
|
have nothing to do at that time. Therefore
|
|
<filename>/etc/rc.shutdown</filename>
|
|
selectively invokes <filename>rc.d</filename>
|
|
scripts with the <literal>shutdown</literal>
|
|
keyword, effectively ignoring the
|
|
rest of the scripts. For even faster shutdown,
|
|
<filename>/etc/rc.shutdown</filename> passes
|
|
the <option>faststop</option> command to the scripts
|
|
it runs so that they skip preliminary checks, e.g.,
|
|
the pidfile check. As dependent services should be
|
|
stopped before their prerequisites,
|
|
<filename>/etc/rc.shutdown</filename> runs the scripts
|
|
in reverse dependency order.</para>
|
|
|
|
<para>If writing a real
|
|
<filename>rc.d</filename> script, you should
|
|
consider whether it is relevant at system shutdown
|
|
time. E.g., if your script does its work in
|
|
response to the <option>start</option> command
|
|
only, then you need not include this keyword.
|
|
However, if your script manages a service, it is
|
|
probably a good idea to stop it before the system
|
|
proceeds to the final stage of its shutdown
|
|
sequence described in &man.halt.8;. In particular,
|
|
a service should be stopped explicitly if it needs
|
|
considerable time or special actions to shut down
|
|
cleanly. A typical example of such a service is
|
|
a database engine.</para>
|
|
</note>
|
|
</listitem>
|
|
</varlistentry>
|
|
</variablelist>
|
|
</callout>
|
|
|
|
<callout arearefs="rcng-hookup-force">
|
|
<para><anchor xml:id="forcedep"/>To begin with,
|
|
<function>force_depend</function> should be used with
|
|
much care. It is generally better to revise the hierarchy
|
|
of configuration variables for your <filename>rc.d</filename>
|
|
scripts if they are interdependent.</para>
|
|
|
|
<para>If you still cannot do without
|
|
<function>force_depend</function>, the example offers an
|
|
idiom of how to invoke it conditionally. In the example,
|
|
our <command>mumbled</command> daemon requires that another
|
|
one, <command>frotz</command>, be started in advance.
|
|
However, <command>frotz</command> is optional, too; and
|
|
&man.rcorder.8; knows nothing about such details.
|
|
Fortunately, our script has access to all &man.rc.conf.5;
|
|
variables. If <envar>frotz_enable</envar> is true, we
|
|
hope for the best and rely on <filename>rc.d</filename>
|
|
to have started <command>frotz</command>. Otherwise we
|
|
forcibly check the status of <command>frotz</command>.
|
|
Finally, we enforce our dependency on <command>frotz</command>
|
|
if it is found to be not running. A warning message will
|
|
be emitted by <function>force_depend</function> because
|
|
it should be invoked only if a misconfiguration has been
|
|
detected.</para>
|
|
</callout>
|
|
</calloutlist>
|
|
</sect1>
|
|
|
|
<sect1 xml: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.</para>
|
|
|
|
<para>How can &man.rc.subr.8; 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:
|
|
<option>start</option>, <option>stop</option>, etc. It will
|
|
be shifted out by <function>run_rc_command</function>,
|
|
so what is <envar>$2</envar> in the original command line will
|
|
be presented as <envar>$1</envar> to the method, and so on.</para>
|
|
|
|
<para>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 xml: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 xml:id="rcng-args-kiss"/>
|
|
echo -n " and whispers: $*"
|
|
fi
|
|
case "$*" in
|
|
*[.!?])
|
|
echo
|
|
;;
|
|
*)
|
|
echo .
|
|
;;
|
|
esac
|
|
}
|
|
|
|
load_rc_config $name
|
|
run_rc_command "$@"<co xml: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 — 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.:</para>
|
|
|
|
<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>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,
|
|
where we invoke <function>run_rc_command</function>.</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>
|
|
|
|
<note>
|
|
<para>Currently <function>run_rc_command</function> may
|
|
have a bug that prevents it from keeping the original
|
|
boundaries between arguments. That is, arguments with
|
|
embedded whitespace may not be processed correctly.
|
|
The bug stems from <envar>$*</envar> misuse.</para>
|
|
</note>
|
|
</callout>
|
|
</calloutlist>
|
|
</sect1>
|
|
|
|
<sect1 xml:id="rcng-furthur">
|
|
<title>Further reading</title>
|
|
|
|
<para><anchor xml:id="lukem"/><link xlink:href="http://www.mewburn.net/luke/papers/rc.d.pdf">The original
|
|
article by Luke Mewburn</link> offers a general overview of
|
|
<filename>rc.d</filename> and detailed rationale for its
|
|
design decisions. It provides insight on the whole
|
|
<filename>rc.d</filename> framework and its place in a modern
|
|
BSD operating system.</para>
|
|
|
|
<para><anchor xml:id="manpages"/>The manual pages &man.rc.8;,
|
|
&man.rc.subr.8;, and &man.rcorder.8; document the
|
|
<filename>rc.d</filename> components in great detail. You
|
|
cannot fully use the <filename>rc.d</filename> power without
|
|
studying the manual pages and referring to them while writing
|
|
your own scripts.</para>
|
|
|
|
<para>The major source of working, real-life examples is
|
|
<filename>/etc/rc.d</filename> in a live system. Its contents
|
|
are easy and pleasant to read because most rough corners are
|
|
hidden deep in &man.rc.subr.8;. Keep in mind though that the
|
|
<filename>/etc/rc.d</filename> scripts were not written by
|
|
angels, so they might suffer from bugs and suboptimal design
|
|
decisions. Now you can improve them!</para>
|
|
</sect1>
|
|
</article>
|