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.
#!/bin/sh . /etc/rc.subr name=mumbled rcvar=mumbled_enable command="/usr/sbin/${name}" command_args="mock arguments > /dev/null 2>&1" pidfile="/var/run/${name}.pid" required_files="/etc/${name}.conf /usr/share/misc/${name}.rules" sig_reload="USR1" start_precmd="${name}_prestart" stop_postcmd="echo Bye-bye" extra_commands="reload plugh xyzzy" plugh_cmd="mumbled_plugh" xyzzy_cmd="echo 'Nothing happens.'" mumbled_prestart() { if checkyesno mumbled_smart; then rc_flags="-o smart ${rc_flags}" fi case "$mumbled_mode" in foo) rc_flags="-frotz ${rc_flags}" ;; bar) rc_flags="-baz ${rc_flags}" ;; *) warn "Invalid value for mumbled_mode" return 1 ;; esac run_rc_command xyzzy return 0 } mumbled_plugh() { echo 'A hollow voice says "plugh".' } load_rc_config $name run_rc_command "$1"
Note: Never include dashed options, like
-X
or--foo
, in command_args. The contents of command_args will appear at the end of the final command line, hence they are likely to follow arguments present in ${name}_flags; but most commands will not recognize dashed options after ordinary arguments. A better way of passing additional options to $command is to add them to the beginning of ${name}_flags. Another way is to modify rc_flags as shown later.
Note: In fact, 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
faststart
argument.
Note: The default method from rc.subr(8) can be forced to skip the prerequisite checks by using
forcestart
as the argument to the script.
SIGHUP
by default. Another signal is sent to stop the daemon
process; the default is SIGTERM
, but this can be
changed by setting sig_stop appropriately.Note: The signal names should be specified to rc.subr(8) without the SIG prefix, as it is shown in the example. The FreeBSD version of kill(1) can recognize the SIG prefix, but the versions from other OS types may not.
Note: Overriding a default method with a custom argument_cmd still does not prevent us from making use of argument_precmd or argument_postcmd 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 argument_precmd along with argument_cmd lets us logically separate the checks from the action.
Do not forget that you can cram any valid 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.
Note: The
reload
command is special. On the one hand, it has a preset method in rc.subr(8). On the other hand,reload
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 extra_commands.What do we get from the default method for
reload
? Quite often daemons reload their configuration upon reception of a signal — typically,SIGHUP
. Therefore rc.subr(8) attempts to reload the daemon by sending a signal to it. The signal is preset toSIGHUP
but can be customized via sig_reload if necessary.
plugh
and xyzzy
. We saw them
listed in extra_commands, and now it is time to provide
methods for them. The method for xyzzy
is just inlined
while that for plugh
is implemented as the mumbled_plugh
function.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., devd(8) if specified in devd.conf(5).
The full list of available commands can be found in the usage line printed by rc.subr(8) when the script is invoked without arguments. For example, here is the usage line from the script under study:
# /etc/rc.d/mumbled Usage: /etc/rc.d/mumbled [fast|force|one](start|stop|restart|rcvar|reload|plugh|xyzzy|status|poll)
checkyesno
is provided by
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 YES, or TRUE,
or ON, or 1, case insensitive; a
non-zero exit code is returned otherwise. In the latter case, the function
tests the variable for being set to NO, FALSE, OFF, or 0, case insensitive; it prints a warning message if the
variable contains anything else, i.e., junk.Keep in mind that for sh(1) a zero exit code means true and a non-zero exit code means false.
Important: The
checkyesno
function takes a variable name. Do not pass the expanded value of a variable to it; it will not work as expected.The following is the correct usage of
checkyesno
:if checkyesno mumbled_enable; then foo fiOn the contrary, calling
checkyesno
as shown below will not work — at least not as expected:if checkyesno "${mumbled_enable}"; then foo fi
debug
, info
, warn
, and err
. The latter
function then exits the script with the code specified.Note: However, 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 force, as in
forcestart
.