Skip to content
Browse files

Carga inicial do artigo rc-scripting r38826 para inicio de traducao

  • Loading branch information...
1 parent e0524fb commit e0e86d783bbbb1a07512a54c412e8cd1edc30fd1 @ebrandi ebrandi committed Aug 24, 2012
Showing with 1,427 additions and 0 deletions.
  1. +40 −0 pt_BR.ISO8859-1/articles/rc-scripting/Makefile
  2. +1,387 −0 pt_BR.ISO8859-1/articles/rc-scripting/article.sgml
View
40 pt_BR.ISO8859-1/articles/rc-scripting/Makefile
@@ -0,0 +1,40 @@
+#
+# The FreeBSD Documentation Project
+# The FreeBSD Brazilian Portuguese Documentation Project
+#
+# $FreeBSD$
+#
+# Original revision: r38826
+#
+# Article: Practical rc.d scripting in BSD
+
+DOC?= article
+
+NO_TIDY=YES
+
+FORMATS?= html html-split
+WITH_ARTICLE_TOC?= YES
+
+INSTALL_COMPRESSED?= gz
+INSTALL_ONLY_COMPRESSED?=
+
+SRCS= article.sgml
+
+IMAGES_LIB+= callouts/1.png
+IMAGES_LIB+= callouts/2.png
+IMAGES_LIB+= callouts/3.png
+IMAGES_LIB+= callouts/4.png
+IMAGES_LIB+= callouts/5.png
+IMAGES_LIB+= callouts/6.png
+IMAGES_LIB+= callouts/7.png
+IMAGES_LIB+= callouts/8.png
+IMAGES_LIB+= callouts/9.png
+IMAGES_LIB+= callouts/10.png
+IMAGES_LIB+= callouts/11.png
+IMAGES_LIB+= callouts/12.png
+IMAGES_LIB+= callouts/13.png
+IMAGES_LIB+= callouts/14.png
+
+URL_RELPREFIX?= ../../../..
+DOC_PREFIX?= ${.CURDIR}/../../..
+.include "${DOC_PREFIX}/share/mk/doc.project.mk"
View
1,387 pt_BR.ISO8859-1/articles/rc-scripting/article.sgml
@@ -0,0 +1,1387 @@
+<!--
+ The FreeBSD Documentation Project
+ The FreeBSD Brazilian Portuguese Documentation Project
+
+ Original revision: r38826
+-->
+
+<!DOCTYPE article PUBLIC "-//FreeBSD//DTD DocBook V4.1-Based Extension//EN" [
+<!ENTITY % articles.ent PUBLIC "-//FreeBSD//ENTITIES DocBook FreeBSD Articles Entity Set//PTBR">
+%articles.ent;
+]>
+
+<article>
+ <articleinfo>
+ <title>Practical rc.d scripting in BSD</title>
+
+ <author>
+ <firstname>Yar</firstname>
+
+ <surname>Tikhiy</surname>
+
+ <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>
+
+ <pubdate>$FreeBSD$</pubdate>
+
+ <legalnotice id="trademarks" role="trademarks">
+ &tm-attrib.freebsd;
+ &tm-attrib.netbsd;
+ &tm-attrib.general;
+ </legalnotice>
+
+ <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>
+ </articleinfo>
+
+ <sect1 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 fulfill 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 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 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 id="rcng-dummy-shebang">
+
+. /etc/rc.subr<co id="rcng-dummy-include">
+
+name="dummy"<co id="rcng-dummy-name">
+start_cmd="${name}_start"<co id="rcng-dummy-startcmd">
+stop_cmd=":"<co id="rcng-dummy-stopcmd">
+
+dummy_start()<co id="rcng-dummy-startfn">
+{
+ echo "Nothing started."
+}
+
+load_rc_config $name<co id="rcng-dummy-loadconfig">
+run_rc_command "$1"<co 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 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 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 id="rcng-confdummy-rcvar">
+
+start_cmd="${name}_start"
+stop_cmd=":"
+
+load_rc_config $name<co id="rcng-confdummy-loadconfig">
+eval "${rcvar}=\${${rcvar}:-'NO'}"<co id="rcng-confdummy-enable">
+dummy_msg=${dummy_msg:-"Nothing started."}<co id="rcng-confdummy-opt">
+
+dummy_start()
+{
+ echo "$dummy_msg"<co 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. A portable
+ 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 long as an &man.rc.conf.5; variable and its
+ internal equivalent are the same, we can use a more
+ compact expression to set the default value:</para>
+
+ <programlisting>: ${dummy_msg:="Nothing started."}</programlisting>
+
+ <para>The current style is to use the more verbose form
+ though.</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.</para>
+ </callout>
+ </calloutlist>
+ </sect1>
+
+ <sect1 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 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 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 &gt; /dev/null 2&gt;&amp;1"<co id="rcng-daemon-adv-args">
+
+pidfile="/var/run/${name}.pid"<co id="rcng-daemon-adv-pid">
+
+required_files="/etc/${name}.conf /usr/share/misc/${name}.rules"<co id="rcng-daemon-adv-reqfiles">
+
+sig_reload="USR1"<co id="rcng-daemon-adv-sig">
+
+start_precmd="${name}_prestart"<co id="rcng-daemon-adv-precmd">
+stop_postcmd="echo Bye-bye"<co id="rcng-daemon-adv-postcmd">
+
+extra_commands="reload plugh xyzzy"<co id="rcng-daemon-adv-extra">
+
+plugh_cmd="mumbled_plugh"<co id="rcng-daemon-adv-methods">
+xyzzy_cmd="echo 'Nothing happens.'"
+
+mumbled_prestart()
+{
+ if checkyesno mumbled_smart; then<co id="rcng-daemon-adv-yn">
+ rc_flags="-o smart ${rc_flags}"<co 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 id="rcng-daemon-adv-warn">
+ return 1<co id="rcng-daemon-adv-preret">
+ ;;
+ esac
+ run_rc_command xyzzy<co id="rcng-daemon-adv-run">
+ return 0
+}
+
+mumbled_plugh()<co 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 &mdash;
+ 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 &mdash; 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 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 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 &lt;<filename>bsd.prog.mk</filename>&gt; and
+ &lt;<filename>bsd.port.mk</filename>&gt; 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 <makevar>USE_RC_SUBR</makevar> as described
+ <ulink url="&url.books.porters-handbook;/rc-scripts.html">in
+ the Porter's Handbook</ulink>.</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 id="rcng-hookup-provide">
+# REQUIRE: DAEMON cleanvar frotz<co id="rcng-hookup-require">
+# BEFORE: LOGIN<co id="rcng-hookup-before">
+# KEYWORD: nojail shutdown<co 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 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><replaceable>UPPERCASE</replaceable></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 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 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 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 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.:</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 id="rcng-furthur">
+ <title>Further reading</title>
+
+ <para><anchor id="lukem"><ulink
+ url="http://www.mewburn.net/luke/papers/rc.d.pdf">The original
+ article by Luke Mewburn</ulink> 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 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>

0 comments on commit e0e86d7

Please sign in to comment.
Something went wrong with that request. Please try again.