rc.d scripts are used to start
      services on system startup, and to give administrators a
      standard way of stopping, starting and restarting the service.
      Ports integrate into the system rc.d
      framework.  Details on its usage can be found in the
	rc.d Handbook chapter.  Detailed explanation of
      the available commands is provided in rc(8) and
      rc.subr(8).  Finally, there is
      an
	article on practical aspects of
      rc.d scripting.
With a mythical port called
      doorman, which needs to start a
      doormand daemon.  Add the following
      to the Makefile:
USE_RC_SUBR=	doormandMultiple scripts may be listed and will be installed.
      Scripts must be placed in the files
      subdirectory and a .in suffix must be added
      to their filename.  Standard SUB_LIST
      expansions will be ran against this file.  Use of the
      %%PREFIX%% and
      %%LOCALBASE%% expansions is strongly
      encouraged as well.  More on SUB_LIST in
      the relevant
	section.
As of FreeBSD 6.1-RELEASE, local
      rc.d scripts (including those installed
      by ports) are included in the overall rcorder(8) of the
      base system.
An example simple rc.d script to start
      the doormand daemon:
#!/bin/sh # $FreeBSD$ # # PROVIDE:doormand# REQUIRE: LOGIN # KEYWORD: shutdown # # Add these lines to /etc/rc.conf.local or /etc/rc.conf # to enable this service: # #doormand_enable (bool): Set to NO by default. # Set it to YES to enabledoormand. #doormand_config (path): Set to %%PREFIX%%/etc/doormand/doormand.cf# by default. . /etc/rc.subr name=doormandrcvar=doormand_enable load_rc_config $name : ${doormand_enable:="NO"} : ${doormand_config="%%PREFIX%%/etc/doormand/doormand.cf"} command=%%PREFIX%%/sbin/${name} pidfile=/var/run/${name}.pid command_args="-p $pidfile -f $doormand_config" run_rc_command "$1"
Unless there is a very good reason to start the service earlier, or it runs as a particular user (other than root), all ports scripts must use:
REQUIRE: LOGIN
If the startup script launches a daemon that must be shutdown, the following will trigger a stop of the service on system shutdown:
KEYWORD: shutdown
If the script is not starting a persistent service this is not necessary.
For optional configuration elements the "=" style of default variable assignment is preferable to the ":=" style here, since the former sets a default value only if the variable is unset, and the latter sets one if the variable is unset or null. A user might very well include something like:
doormand_flags=""in their rc.conf.local, and a
      variable substitution using ":=" would
      inappropriately override the user's intention.  The
      _enable variable is not optional,
      and must use the ":" for the default.
Ports must not start and stop
	their services when installing and deinstalling.  Do not abuse
	the plist keywords described in Section 8.6.13.2, “@preexec
	  command,
	  @postexec
	  command,
	  @preunexec
	  command,
	  @postunexec
	  command” by running commands
	that modify the currently running system, including starting
	or stopping services.
Before contributing a port with an
	rc.d script, and more importantly,
	before committing one, please consult this
	checklist to be sure that it is ready.
The devel/rclint port can check for most of these, but it is not a substitute for proper review.
If this is a new file, does it have a
	    .sh extension?  If so, that must be
	    changed to just
	    
	    since file.inrc.d files may not end with
	    that extension.
Does the file have a
	    $FreeBSD$ tag?
Do the name of the file (minus
	    .in), the
	    PROVIDE line, and
	    $name
	    all match?  The file name matching
	    PROVIDE makes debugging easier,
	    especially for rcorder(8) issues.  Matching the
	    file name and
	    $name
	    makes it easier to figure out which variables are
	    relevant in rc.conf[.local].  It is
	    also a policy
	    for all new scripts, including those in the base
	    system.
Is the REQUIRE line set to
	    LOGIN?  This is mandatory for scripts
	    that run as a non-root user.  If it runs as root, is
	    there a good reason for it to run prior to
	    LOGIN?  If not, it must run after
	    so that local scrips can be loosely grouped to a point in
	    rcorder(8) after most everything in the base is
	    already running.
Does the script start a persistent service?  If so,
	    it must have KEYWORD:
	      shutdown.
Make sure there is no
	    KEYWORD: FreeBSD present.  This has
	    not been necessary nor desirable for years.  It is also
	    an indication that the new script was copy/pasted from
	    an old script, so extra caution must be given to the
	    review.
If the script uses an interpreted language like
	    perl, python, or
	    ruby, make certain that
	    command_interpreter is set
	    appropriately, for example, for
	    Perl, by adding
	    PERL=${PERL} to
	    SUB_LIST and using
	    %%PERL%%.  Otherwise,
#servicenamestop
will probably not work properly. See service(8) for more information.
Have all occurrences of
	    /usr/local been replaced with
	    %%PREFIX%%?
Do the default variable assignments come after
	    load_rc_config?
Are there default assignments to empty strings? They should be removed, but double-check that the option is documented in the comments at the top of the file.
Are things that are set in variables actually used in the script?
Are options listed in the default
	    name_flags
	    things that are actually mandatory?  If so, they must
	    be in command_args.
	    -d is a red flag (pardon the
	    pun) here, since it is usually the option to
	    “daemonize” the process, and therefore is
	    actually mandatory.
	    must never be included in
	    name_flagscommand_args (and vice versa,
	    although that error is less common).
Does the script execute any code unconditionally?
	    This is frowned on.  Usually these things must be
	    dealt with through a
	    start_precmd.
All boolean tests must use the
	    checkyesno function.  No
	    hand-rolled tests for [Yy][Ee][Ss],
	    etc.
If there is a loop (for example, waiting for something to start) does it have a counter to terminate the loop? We do not want the boot to be stuck forever if there is an error.
Does the script create files or directories that
	    need specific permissions, for example, a
	    pid that needs to be owned by
	    the user that runs the process?  Rather than the
	    traditional touch(1)/chown(8)/chmod(1)
	    routine, consider using install(1) with the proper
	    command line arguments to do the whole procedure with
	    one step.
All FreeBSD documents are available for download at https://download.freebsd.org/ftp/doc/
Questions that are not answered by the
    documentation may be
    sent to <freebsd-questions@FreeBSD.org>.
    Send questions about this document to <freebsd-doc@FreeBSD.org>.