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><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 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>
 |