rc.subr revision 1.102
1# $NetBSD: rc.subr,v 1.102 2018/04/11 18:51:22 christos Exp $ 2# 3# Copyright (c) 1997-2011 The NetBSD Foundation, Inc. 4# All rights reserved. 5# 6# This code is derived from software contributed to The NetBSD Foundation 7# by Luke Mewburn. 8# 9# Redistribution and use in source and binary forms, with or without 10# modification, are permitted provided that the following conditions 11# are met: 12# 1. Redistributions of source code must retain the above copyright 13# notice, this list of conditions and the following disclaimer. 14# 2. Redistributions in binary form must reproduce the above copyright 15# notice, this list of conditions and the following disclaimer in the 16# documentation and/or other materials provided with the distribution. 17# 18# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 19# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 20# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 21# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 22# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28# POSSIBILITY OF SUCH DAMAGE. 29# 30# rc.subr 31# functions used by various rc scripts 32# 33 34: ${rcvar_manpage:='rc.conf(5)'} 35: ${RC_PID:=$$} ; export RC_PID 36nl=' 37' # a literal newline 38 39# RC variables to clear on start. 40_env_clear_rc_vars=" 41RC_PID= 42_rc_pid= 43_rc_original_stdout_fd= 44_rc_original_stderr_fd= 45_rc_postprocessor_fd= 46" 47 48# 49# functions 50# --------- 51 52# 53# checkyesno var 54# Test $1 variable. 55# Return 0 if it's "yes" (et al), 1 if it's "no" (et al), 2 otherwise. 56# 57checkyesnox() 58{ 59 eval _value=\$${1} 60 case $_value in 61 62 # "yes", "true", "on", or "1" 63 [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) 64 return 0 65 ;; 66 67 # "no", "false", "off", or "0" 68 [Nn][Oo]|[Ff][Aa][Ll][Ss][Ee]|[Oo][Ff][Ff]|0) 69 return 1 70 ;; 71 *) 72 return 2 73 ;; 74 esac 75} 76 77# 78# checkyesno var 79# Test $1 variable, and warn if not set to YES or NO. 80# Return 0 if it's "yes" (et al), nonzero otherwise. 81# 82checkyesno() 83{ 84 local var 85 86 checkyesnox $1 87 var=$? 88 [ $var = 0 -o $var = 1 ] && return $var 89 warn "\$${1} is not set properly - see ${rcvar_manpage}." 90 return 1 91} 92 93# 94# yesno_to_truefalse var 95# Convert the value of a variable from any of the values 96# understood by checkyesno() to "true" or "false". 97# 98yesno_to_truefalse() 99{ 100 local var=$1 101 if checkyesno $var; then 102 eval $var=true 103 return 0 104 else 105 eval $var=false 106 return 1 107 fi 108} 109 110# 111# reverse_list list 112# print the list in reverse order 113# 114reverse_list() 115{ 116 _revlist= 117 for _revfile; do 118 _revlist="$_revfile $_revlist" 119 done 120 echo $_revlist 121} 122 123# 124# If booting directly to multiuser, send SIGTERM to 125# the parent (/etc/rc) to abort the boot. 126# Otherwise just exit. 127# 128stop_boot() 129{ 130 if [ "$autoboot" = yes ]; then 131 echo "ERROR: ABORTING BOOT (sending SIGTERM to parent)!" 132 kill -TERM ${RC_PID} 133 fi 134 exit 1 135} 136 137# 138# mount_critical_filesystems type 139# Go through the list of critical file systems as provided in 140# the rc.conf(5) variable $critical_filesystems_${type}, checking 141# each one to see if it is mounted, and if it is not, mounting it. 142# It's not an error if file systems prefixed with "OPTIONAL:" 143# are not mentioned in /etc/fstab. 144# 145mount_critical_filesystems() 146{ 147 eval _fslist=\$critical_filesystems_${1} 148 _mountcrit_es=0 149 for _fs in $_fslist; do 150 _optional=false 151 case "$_fs" in 152 OPTIONAL:*) 153 _optional=true 154 _fs="${_fs#*:}" 155 ;; 156 esac 157 _ismounted=false 158 # look for a line like "${fs} on * type *" 159 # or "* on ${fs} type *" in the output from mount. 160 case "${nl}$( mount )${nl}" in 161 *" on ${_fs} type "*) 162 _ismounted=true 163 ;; 164 *"${nl}${_fs} on "*) 165 _ismounted=true 166 ;; 167 esac 168 if $_ismounted; then 169 print_rc_metadata \ 170 "note:File system ${_fs} was already mounted" 171 else 172 _mount_output=$( mount $_fs 2>&1 ) 173 _mount_es=$? 174 case "$_mount_output" in 175 *"${nl}"*) 176 # multiple lines can't be good, 177 # not even if $_optional is true 178 ;; 179 *[uU]'nknown special file or file system'*) 180 if $_optional; then 181 # ignore this error 182 print_rc_metadata \ 183 "note:Optional file system ${_fs} is not present" 184 _mount_es=0 185 _mount_output="" 186 fi 187 ;; 188 esac 189 if [ -n "$_mount_output" ]; then 190 printf >&2 "%s\n" "$_mount_output" 191 fi 192 if [ "$_mount_es" != 0 ]; then 193 _mountcrit_es="$_mount_es" 194 fi 195 fi 196 done 197 return $_mountcrit_es 198} 199 200# 201# check_pidfile pidfile procname [interpreter] 202# Parses the first line of pidfile for a PID, and ensures 203# that the process is running and matches procname. 204# Prints the matching PID upon success, nothing otherwise. 205# interpreter is optional; see _find_processes() for details. 206# 207check_pidfile() 208{ 209 _pidfile=$1 210 _procname=$2 211 _interpreter=$3 212 if [ -z "$_pidfile" -o -z "$_procname" ]; then 213 err 3 'USAGE: check_pidfile pidfile procname [interpreter]' 214 fi 215 if [ ! -f $_pidfile ]; then 216 return 217 fi 218 read _pid _junk < $_pidfile 219 if [ -z "$_pid" ]; then 220 return 221 fi 222 _find_processes $_procname ${_interpreter:-.} '-p '"$_pid" 223} 224 225# 226# check_process procname [interpreter] 227# Ensures that a process (or processes) named procname is running. 228# Prints a list of matching PIDs. 229# interpreter is optional; see _find_processes() for details. 230# 231check_process() 232{ 233 _procname=$1 234 _interpreter=$2 235 if [ -z "$_procname" ]; then 236 err 3 'USAGE: check_process procname [interpreter]' 237 fi 238 _find_processes $_procname ${_interpreter:-.} '-A' 239} 240 241# 242# _find_processes procname interpreter psargs 243# Search for procname in the output of ps generated by psargs. 244# Prints the PIDs of any matching processes, space separated. 245# 246# If interpreter == ".", check the following variations of procname 247# against the first word of each command: 248# procname 249# `basename procname` 250# `basename procname` + ":" 251# "(" + `basename procname` + ")" 252# 253# If interpreter != ".", read the first line of procname, remove the 254# leading #!, normalise whitespace, append procname, and attempt to 255# match that against each command, either as is, or with extra words 256# at the end. As an alternative, to deal with interpreted daemons 257# using perl, the basename of the interpreter plus a colon is also 258# tried as the prefix to procname. 259# 260_find_processes() 261{ 262 if [ $# -ne 3 ]; then 263 err 3 'USAGE: _find_processes procname interpreter psargs' 264 fi 265 _procname=$1 266 _interpreter=$2 267 _psargs=$3 268 269 _pref= 270 _procnamebn=${_procname##*/} 271 if [ $_interpreter != "." ]; then # an interpreted script 272 read _interp < ${_chroot:-}/$_procname # read interpreter name 273 _interp=${_interp#\#!} # strip #! 274 set -- $_interp 275 if [ $1 = "/usr/bin/env" ]; then 276 shift 277 set -- $(type $1) 278 shift $(($# - 1)) 279 _interp="${1##*/} $_procname" 280 else 281 _interp="$* $_procname" 282 fi 283 if [ $_interpreter != $1 ]; then 284 warn "\$command_interpreter $_interpreter != $1" 285 fi 286 _interpbn=${1##*/} 287 _fp_args='_argv' 288 _fp_match='case "$_argv" in 289 ${_interp}|"${_interp} "*|"${_interpbn}: "*${_procnamebn}*)' 290 else # a normal daemon 291 _fp_args='_arg0 _argv' 292 _fp_match='case "$_arg0" in 293 $_procname|$_procnamebn|${_procnamebn}:|"(${_procnamebn})")' 294 fi 295 296 _proccheck=' 297 ps -o "pid,args" '"$_psargs"' 2>&1 | 298 while read _npid '"$_fp_args"'; do 299 case "$_npid" in 300 ps:|PID) 301 continue ;; 302 esac ; '"$_fp_match"' 303 echo -n "$_pref$_npid" ; 304 _pref=" " 305 ;; 306 esac 307 done' 308 309#echo 1>&2 "proccheck is :$_proccheck:" 310 eval $_proccheck 311} 312 313# 314# wait_for_pids pid [pid ...] 315# spins until none of the pids exist 316# 317wait_for_pids() 318{ 319 _list="$@" 320 if [ -z "$_list" ]; then 321 return 322 fi 323 _prefix= 324 while true; do 325 _nlist=""; 326 for _j in $_list; do 327 if kill -0 $_j 2>/dev/null; then 328 _nlist="${_nlist}${_nlist:+ }$_j" 329 fi 330 done 331 if [ -z "$_nlist" ]; then 332 break 333 fi 334 if [ "$_list" != "$_nlist" ]; then 335 _list=$_nlist 336 echo -n ${_prefix:-"Waiting for PIDS: "}$_list 337 _prefix=", " 338 fi 339 # We want this to be a tight loop for a fast exit 340 sleep 0.05 341 done 342 if [ -n "$_prefix" ]; then 343 echo "." 344 fi 345} 346 347# 348# run_rc_command argument [parameters] 349# Search for argument in the list of supported commands, which is: 350# "start stop restart rcvar status poll ${extra_commands}" 351# If there's a match, run ${argument}_cmd or the default method 352# (see below), and pass the optional list of parameters to it. 353# 354# If argument has a given prefix, then change the operation as follows: 355# Prefix Operation 356# ------ --------- 357# fast Skip the pid check, and set rc_fast=yes 358# force Set ${rcvar} to YES, and set rc_force=yes 359# one Set ${rcvar} to YES 360# 361# The following globals are used: 362# 363# Name Needed Purpose 364# ---- ------ ------- 365# name y Name of script. 366# 367# command n Full path to command. 368# Not needed if ${rc_arg}_cmd is set for 369# each keyword. 370# 371# command_args n Optional args/shell directives for command. 372# 373# command_interpreter n If not empty, command is interpreted, so 374# call check_{pidfile,process}() appropriately. 375# 376# extra_commands n List of extra commands supported. 377# 378# pidfile n If set, use check_pidfile $pidfile $command, 379# otherwise use check_process $command. 380# In either case, only check if $command is set. 381# 382# procname n Process name to check for instead of $command. 383# 384# rcvar n This is checked with checkyesno to determine 385# if the action should be run. 386# 387# ${name}_chroot n Directory to chroot to before running ${command} 388# Requires /usr to be mounted. 389# 390# ${name}_chdir n Directory to cd to before running ${command} 391# (if not using ${name}_chroot). 392# 393# ${name}_flags n Arguments to call ${command} with. 394# NOTE: $flags from the parent environment 395# can be used to override this. 396# 397# ${name}_env n Additional environment variable settings 398# for running ${command} 399# 400# ${name}_nice n Nice level to run ${command} at. 401# 402# ${name}_user n User to run ${command} as, using su(1) if not 403# using ${name}_chroot. 404# Requires /usr to be mounted. 405# 406# ${name}_group n Group to run chrooted ${command} as. 407# Requires /usr to be mounted. 408# 409# ${name}_groups n Comma separated list of supplementary groups 410# to run the chrooted ${command} with. 411# Requires /usr to be mounted. 412# 413# ${rc_arg}_cmd n If set, use this as the method when invoked; 414# Otherwise, use default command (see below) 415# 416# ${rc_arg}_precmd n If set, run just before performing the 417# ${rc_arg}_cmd method in the default 418# operation (i.e, after checking for required 419# bits and process (non)existence). 420# If this completes with a non-zero exit code, 421# don't run ${rc_arg}_cmd. 422# 423# ${rc_arg}_postcmd n If set, run just after performing the 424# ${rc_arg}_cmd method, if that method 425# returned a zero exit code. 426# 427# required_dirs n If set, check for the existence of the given 428# directories before running the default 429# (re)start command. 430# 431# required_files n If set, check for the readability of the given 432# files before running the default (re)start 433# command. 434# 435# required_vars n If set, perform checkyesno on each of the 436# listed variables before running the default 437# (re)start command. 438# 439# Default behaviour for a given argument, if no override method is 440# provided: 441# 442# Argument Default behaviour 443# -------- ----------------- 444# start if !running && checkyesno ${rcvar} 445# ${command} 446# 447# stop if ${pidfile} 448# rc_pid=$(check_pidfile $pidfile $command) 449# else 450# rc_pid=$(check_process $command) 451# kill $sig_stop $rc_pid 452# wait_for_pids $rc_pid 453# ($sig_stop defaults to TERM.) 454# 455# reload Similar to stop, except use $sig_reload instead, 456# and doesn't wait_for_pids. 457# $sig_reload defaults to HUP. 458# 459# restart Run `stop' then `start'. 460# 461# status Show if ${command} is running, etc. 462# 463# poll Wait for ${command} to exit. 464# 465# rcvar Display what rc.conf variable is used (if any). 466# 467# Variables available to methods, and after run_rc_command() has 468# completed: 469# 470# Variable Purpose 471# -------- ------- 472# rc_arg Argument to command, after fast/force/one processing 473# performed 474# 475# rc_flags Flags to start the default command with. 476# Defaults to ${name}_flags, unless overridden 477# by $flags from the environment. 478# This variable may be changed by the precmd method. 479# 480# rc_pid PID of command (if appropriate) 481# 482# rc_fast Not empty if "fast" was provided (q.v.) 483# 484# rc_force Not empty if "force" was provided (q.v.) 485# 486# 487run_rc_command() 488{ 489 rc_arg=$1 490 if [ -z "$name" ]; then 491 err 3 'run_rc_command: $name is not set.' 492 fi 493 494 _rc_prefix= 495 case "$rc_arg" in 496 fast*) # "fast" prefix; don't check pid 497 rc_arg=${rc_arg#fast} 498 rc_fast=yes 499 ;; 500 force*) # "force" prefix; always run 501 rc_force=yes 502 _rc_prefix=force 503 rc_arg=${rc_arg#${_rc_prefix}} 504 if [ -n "${rcvar}" ]; then 505 eval ${rcvar}=YES 506 fi 507 ;; 508 one*) # "one" prefix; set ${rcvar}=yes 509 _rc_prefix=one 510 rc_arg=${rc_arg#${_rc_prefix}} 511 if [ -n "${rcvar}" ]; then 512 eval ${rcvar}=YES 513 fi 514 ;; 515 esac 516 517 _keywords="start stop restart rcvar" 518 if [ -n "$extra_commands" ]; then 519 _keywords="${_keywords} ${extra_commands}" 520 fi 521 rc_pid= 522 _pidcmd= 523 _procname=${procname:-${command}} 524 525 # setup pid check command if not fast 526 if [ -z "$rc_fast" -a -n "$_procname" ]; then 527 if [ -n "$pidfile" ]; then 528 _pidcmd='rc_pid=$(check_pidfile '"$pidfile $_procname $command_interpreter"')' 529 else 530 _pidcmd='rc_pid=$(check_process '"$_procname $command_interpreter"')' 531 fi 532 if [ -n "$_pidcmd" ]; then 533 _keywords="${_keywords} status poll" 534 fi 535 fi 536 537 if [ -z "$rc_arg" ]; then 538 rc_usage "$_keywords" 539 fi 540 shift # remove $rc_arg from the positional parameters 541 542 if [ -n "$flags" ]; then # allow override from environment 543 rc_flags=$flags 544 else 545 eval rc_flags=\$${name}_flags 546 fi 547 eval _chdir=\$${name}_chdir _chroot=\$${name}_chroot \ 548 _nice=\$${name}_nice _user=\$${name}_user \ 549 _group=\$${name}_group _groups=\$${name}_groups \ 550 _env=\"\$${name}_env\" 551 552 if [ -n "$_user" ]; then # unset $_user if running as that user 553 if [ "$_user" = "$(id -un)" ]; then 554 unset _user 555 fi 556 fi 557 558 # if ${rcvar} is set, and $1 is not 559 # "rcvar", then run 560 # checkyesno ${rcvar} 561 # and return if that failed or warn 562 # user and exit when interactive 563 # 564 if [ -n "${rcvar}" -a "$rc_arg" != "rcvar" ]; then 565 if ! checkyesno ${rcvar}; then 566 # check whether interactive or not 567 if [ -n "$_run_rc_script" ]; then 568 return 0 569 fi 570 for _elem in $_keywords; do 571 if [ "$_elem" = "$rc_arg" ]; then 572 cat 1>&2 <<EOF 573\$${rcvar} is not enabled - see ${rcvar_manpage}. 574Use the following if you wish to perform the operation: 575 $0 one${rc_arg} 576EOF 577 exit 1 578 fi 579 done 580 echo 1>&2 "$0: unknown directive '$rc_arg'." 581 rc_usage "$_keywords" 582 fi 583 fi 584 585 eval $_pidcmd # determine the pid if necessary 586 587 for _elem in $_keywords; do 588 if [ "$_elem" != "$rc_arg" ]; then 589 continue 590 fi 591 592 # if there's a custom ${XXX_cmd}, 593 # run that instead of the default 594 # 595 eval _cmd=\$${rc_arg}_cmd _precmd=\$${rc_arg}_precmd \ 596 _postcmd=\$${rc_arg}_postcmd 597 if [ -n "$_cmd" ]; then 598 # if the precmd failed and force 599 # isn't set, exit 600 # 601 if ! eval $_precmd && [ -z "$rc_force" ]; then 602 return 1 603 fi 604 605 if ! eval $_cmd \"\${@}\" && [ -z "$rc_force" ]; then 606 return 1 607 fi 608 eval $_postcmd 609 return 0 610 fi 611 612 if [ ${#} -gt 0 ]; then 613 err 1 "the $rc_arg command does not take any parameters" 614 fi 615 616 case "$rc_arg" in # default operations... 617 618 status) 619 if [ -n "$rc_pid" ]; then 620 echo "${name} is running as pid $rc_pid." 621 else 622 echo "${name} is not running." 623 return 1 624 fi 625 ;; 626 627 start) 628 if [ -n "$rc_pid" ]; then 629 echo 1>&2 "${name} already running? (pid=$rc_pid)." 630 exit 1 631 fi 632 633 if [ ! -x ${_chroot}${command} ]; then 634 return 0 635 fi 636 637 # check for required variables, 638 # directories, and files 639 # 640 for _f in $required_vars; do 641 if ! checkyesno $_f; then 642 warn "\$${_f} is not enabled." 643 if [ -z "$rc_force" ]; then 644 return 1 645 fi 646 fi 647 done 648 for _f in $required_dirs; do 649 if [ ! -d "${_f}/." ]; then 650 warn "${_f} is not a directory." 651 if [ -z "$rc_force" ]; then 652 return 1 653 fi 654 fi 655 done 656 for _f in $required_files; do 657 if [ ! -r "${_f}" ]; then 658 warn "${_f} is not readable." 659 if [ -z "$rc_force" ]; then 660 return 1 661 fi 662 fi 663 done 664 665 # if the precmd failed and force 666 # isn't set, exit 667 # 668 if ! eval $_precmd && [ -z "$rc_force" ]; then 669 return 1 670 fi 671 672 # setup the command to run, and run it 673 # 674 echo "Starting ${name}." 675 if [ -n "$_chroot" ]; then 676 _doit="\ 677$_env_clear_rc_vars $_env \ 678${_nice:+nice -n $_nice }\ 679chroot ${_user:+-u $_user }${_group:+-g $_group }${_groups:+-G $_groups }\ 680$_chroot $command $rc_flags $command_args" 681 else 682 _doit="\ 683${_chdir:+cd $_chdir; }\ 684$_env_clear_rc_vars $_env \ 685${_nice:+nice -n $_nice }\ 686$command $rc_flags $command_args" 687 if [ -n "$_user" ]; then 688 _doit="su -m $_user -c 'sh -c \"$_doit\"'" 689 fi 690 fi 691 692 # if the cmd failed and force 693 # isn't set, exit 694 # 695 if ! eval $_doit && [ -z "$rc_force" ]; then 696 return 1 697 fi 698 699 # finally, run postcmd 700 # 701 eval $_postcmd 702 ;; 703 704 stop) 705 if [ -z "$rc_pid" ]; then 706 if [ -n "$pidfile" ]; then 707 echo 1>&2 \ 708 "${name} not running? (check $pidfile)." 709 else 710 echo 1>&2 "${name} not running?" 711 fi 712 exit 1 713 fi 714 715 # if the precmd failed and force 716 # isn't set, exit 717 # 718 if ! eval $_precmd && [ -z "$rc_force" ]; then 719 return 1 720 fi 721 722 # send the signal to stop 723 # 724 echo "Stopping ${name}." 725 _doit="kill -${sig_stop:-TERM} $rc_pid" 726 if [ -n "$_user" ]; then 727 _doit="su -m $_user -c 'sh -c \"$_doit\"'" 728 fi 729 730 # if the stop cmd failed and force 731 # isn't set, exit 732 # 733 if ! eval $_doit && [ -z "$rc_force" ]; then 734 return 1 735 fi 736 737 # wait for the command to exit, 738 # and run postcmd. 739 wait_for_pids $rc_pid 740 eval $_postcmd 741 ;; 742 743 reload) 744 if [ -z "$rc_pid" ]; then 745 if [ -n "$pidfile" ]; then 746 echo 1>&2 \ 747 "${name} not running? (check $pidfile)." 748 else 749 echo 1>&2 "${name} not running?" 750 fi 751 exit 1 752 fi 753 echo "Reloading ${name} config files." 754 if ! eval $_precmd && [ -z "$rc_force" ]; then 755 return 1 756 fi 757 _doit="kill -${sig_reload:-HUP} $rc_pid" 758 if [ -n "$_user" ]; then 759 _doit="su -m $_user -c 'sh -c \"$_doit\"'" 760 fi 761 if ! eval $_doit && [ -z "$rc_force" ]; then 762 return 1 763 fi 764 eval $_postcmd 765 ;; 766 767 restart) 768 if ! eval $_precmd && [ -z "$rc_force" ]; then 769 return 1 770 fi 771 # prevent restart being called more 772 # than once by any given script 773 # 774 if ${_rc_restart_done:-false}; then 775 return 0 776 fi 777 _rc_restart_done=true 778 779 ( $0 ${_rc_prefix}stop ) 780 $0 ${_rc_prefix}start 781 782 eval $_postcmd 783 ;; 784 785 poll) 786 if [ -n "$rc_pid" ]; then 787 wait_for_pids $rc_pid 788 fi 789 ;; 790 791 rcvar) 792 echo "# $name" 793 if [ -n "$rcvar" ]; then 794 if checkyesno ${rcvar}; then 795 echo "\$${rcvar}=YES" 796 else 797 echo "\$${rcvar}=NO" 798 fi 799 fi 800 ;; 801 802 *) 803 rc_usage "$_keywords" 804 ;; 805 806 esac 807 return 0 808 done 809 810 echo 1>&2 "$0: unknown directive '$rc_arg'." 811 rc_usage "$_keywords" 812 exit 1 813} 814 815# 816# _have_rc_postprocessor 817# Test whether the current script is running in a context that 818# was invoked from /etc/rc with a postprocessor. 819# 820# If the test fails, some variables may be unset to make 821# such tests more efficient in future. 822# 823_have_rc_postprocessor() 824{ 825 # Cheap tests that fd and pid are set, fd is writable. 826 [ -n "${_rc_pid}" ] || { unset _rc_pid; return 1; } 827 [ -n "${_rc_postprocessor_fd}" ] || { unset _rc_pid; return 1; } 828 eval ": >&${_rc_postprocessor_fd}" 2>/dev/null \ 829 || { unset _rc_pid; return 1; } 830 831 return 0 832} 833 834# 835# run_rc_script file arg 836# Start the script `file' with `arg', and correctly handle the 837# return value from the script. If `file' ends with `.sh', it's 838# sourced into the current environment. If `file' appears to be 839# a backup or scratch file, ignore it. Otherwise if it's 840# executable run as a child process. 841# 842# If `file' contains "KEYWORD: interactive" and if we are 843# running inside /etc/rc with postprocessing, then the script's 844# stdout and stderr are redirected to $_rc_original_stdout_fd and 845# $_rc_original_stderr_fd, so the output will be displayed on the 846# console but not intercepted by /etc/rc's postprocessor. 847# 848run_rc_script() 849{ 850 _file=$1 851 _arg=$2 852 if [ -z "$_file" -o -z "$_arg" ]; then 853 err 3 'USAGE: run_rc_script file arg' 854 fi 855 856 _run_rc_script=true 857 858 unset name command command_args command_interpreter \ 859 extra_commands pidfile procname \ 860 rcvar required_dirs required_files required_vars 861 eval unset ${_arg}_cmd ${_arg}_precmd ${_arg}_postcmd 862 863 _must_redirect=false 864 if _have_rc_postprocessor \ 865 && _has_rcorder_keyword interactive $_file 866 then 867 _must_redirect=true 868 fi 869 870 case "$_file" in 871 *.sh) # run in current shell 872 if $_must_redirect; then 873 print_rc_metadata \ 874 "note:Output from ${_file} is not logged" 875 no_rc_postprocess eval \ 876 'set $_arg ; . $_file' 877 else 878 set $_arg ; . $_file 879 fi 880 ;; 881 *[~#]|*.OLD|*.orig|*,v) # scratch file; skip 882 warn "Ignoring scratch file $_file" 883 ;; 884 *) # run in subshell 885 if [ -x $_file ] && $_must_redirect; then 886 print_rc_metadata \ 887 "note:Output from ${_file} is not logged" 888 if [ -n "$rc_fast_and_loose" ]; then 889 no_rc_postprocess eval \ 890 'set $_arg ; . $_file' 891 else 892 no_rc_postprocess eval \ 893 '( set $_arg ; . $_file )' 894 fi 895 elif [ -x $_file ]; then 896 if [ -n "$rc_fast_and_loose" ]; then 897 set $_arg ; . $_file 898 else 899 ( set $_arg ; . $_file ) 900 fi 901 else 902 warn "Ignoring non-executable file $_file" 903 fi 904 ;; 905 esac 906} 907 908# 909# load_rc_config command 910# Source in the configuration file for a given command. 911# 912load_rc_config() 913{ 914 _command=$1 915 if [ -z "$_command" ]; then 916 err 3 'USAGE: load_rc_config command' 917 fi 918 919 if ${_rc_conf_loaded:-false}; then 920 : 921 else 922 . /etc/rc.conf 923 _rc_conf_loaded=true 924 fi 925 if [ -f /etc/rc.conf.d/"$_command" ]; then 926 . /etc/rc.conf.d/"$_command" 927 fi 928} 929 930# 931# load_rc_config_var cmd var 932# Read the rc.conf(5) var for cmd and set in the 933# current shell, using load_rc_config in a subshell to prevent 934# unwanted side effects from other variable assignments. 935# 936load_rc_config_var() 937{ 938 if [ $# -ne 2 ]; then 939 err 3 'USAGE: load_rc_config_var cmd var' 940 fi 941 eval $(eval '( 942 load_rc_config '$1' >/dev/null; 943 if [ -n "${'$2'}" -o "${'$2'-UNSET}" != "UNSET" ]; then 944 echo '$2'=\'\''${'$2'}\'\''; 945 fi 946 )' ) 947} 948 949# 950# rc_usage commands 951# Print a usage string for $0, with `commands' being a list of 952# valid commands. 953# 954rc_usage() 955{ 956 echo -n 1>&2 "Usage: $0 [fast|force|one](" 957 958 _sep= 959 for _elem; do 960 echo -n 1>&2 "$_sep$_elem" 961 _sep="|" 962 done 963 echo 1>&2 ")" 964 exit 1 965} 966 967# 968# err exitval message 969# Display message to stderr and log to the syslog, and exit with exitval. 970# 971err() 972{ 973 exitval=$1 974 shift 975 976 if [ -x /usr/bin/logger ]; then 977 logger "$0: ERROR: $*" 978 fi 979 echo 1>&2 "$0: ERROR: $*" 980 exit $exitval 981} 982 983# 984# warn message 985# Display message to stderr and log to the syslog. 986# 987warn() 988{ 989 if [ -x /usr/bin/logger ]; then 990 logger "$0: WARNING: $*" 991 fi 992 echo 1>&2 "$0: WARNING: $*" 993} 994 995# 996# backup_file action file cur backup 997# Make a backup copy of `file' into `cur', and save the previous 998# version of `cur' as `backup' or use rcs for archiving. 999# 1000# This routine checks the value of the backup_uses_rcs variable, 1001# which can be either YES or NO. 1002# 1003# The `action' keyword can be one of the following: 1004# 1005# add `file' is now being backed up (and is possibly 1006# being reentered into the backups system). `cur' 1007# is created and RCS files, if necessary, are 1008# created as well. 1009# 1010# update `file' has changed and needs to be backed up. 1011# If `cur' exists, it is copied to to `back' or 1012# checked into RCS (if the repository file is old), 1013# and then `file' is copied to `cur'. Another RCS 1014# check in done here if RCS is being used. 1015# 1016# remove `file' is no longer being tracked by the backups 1017# system. If RCS is not being used, `cur' is moved 1018# to `back', otherwise an empty file is checked in, 1019# and then `cur' is removed. 1020# 1021# 1022backup_file() 1023{ 1024 _action=$1 1025 _file=$2 1026 _cur=$3 1027 _back=$4 1028 1029 if checkyesno backup_uses_rcs; then 1030 _msg0="backup archive" 1031 _msg1="update" 1032 1033 # ensure that history file is not locked 1034 if [ -f $_cur,v ]; then 1035 rcs -q -u -U -M $_cur 1036 fi 1037 1038 # ensure after switching to rcs that the 1039 # current backup is not lost 1040 if [ -f $_cur ]; then 1041 # no archive, or current newer than archive 1042 if [ ! -f $_cur,v -o $_cur -nt $_cur,v ]; then 1043 ci -q -f -u -t-"$_msg0" -m"$_msg1" $_cur 1044 rcs -q -kb -U $_cur 1045 co -q -f -u $_cur 1046 fi 1047 fi 1048 1049 case $_action in 1050 add|update) 1051 cp -p $_file $_cur 1052 ci -q -f -u -t-"$_msg0" -m"$_msg1" $_cur 1053 rcs -q -kb -U $_cur 1054 co -q -f -u $_cur 1055 chown root:wheel $_cur $_cur,v 1056 ;; 1057 remove) 1058 cp /dev/null $_cur 1059 ci -q -f -u -t-"$_msg0" -m"$_msg1" $_cur 1060 rcs -q -kb -U $_cur 1061 chown root:wheel $_cur $_cur,v 1062 rm $_cur 1063 ;; 1064 esac 1065 else 1066 case $_action in 1067 add|update) 1068 if [ -f $_cur ]; then 1069 cp -p $_cur $_back 1070 fi 1071 cp -p $_file $_cur 1072 chown root:wheel $_cur 1073 ;; 1074 remove) 1075 mv -f $_cur $_back 1076 ;; 1077 esac 1078 fi 1079} 1080 1081# 1082# handle_fsck_error fsck_exit_code 1083# Take action depending on the return code from fsck. 1084# 1085handle_fsck_error() 1086{ 1087 case $1 in 1088 0) # OK 1089 return 1090 ;; 1091 2) # Needs re-run, still fs errors 1092 echo "File system still has errors; re-run fsck manually!" 1093 ;; 1094 4) # Root modified 1095 echo "Root file system was modified, rebooting ..." 1096 reboot -n 1097 echo "Reboot failed; help!" 1098 ;; 1099 8) # Check failed 1100 echo "Automatic file system check failed; help!" 1101 ;; 1102 12) # Got signal 1103 echo "Boot interrupted." 1104 ;; 1105 *) 1106 echo "Unknown error $1; help!" 1107 ;; 1108 esac 1109 stop_boot 1110} 1111 1112# 1113# _has_rcorder_keyword word file 1114# Check whether a file contains a "# KEYWORD:" comment with a 1115# specified keyword in the style used by rcorder(8). 1116# 1117_has_rcorder_keyword() 1118{ 1119 local word="$1" 1120 local file="$2" 1121 local line 1122 1123 [ -r "$file" ] || return 1 1124 while read line; do 1125 case "${line} " in 1126 "# KEYWORD:"*[\ \ ]"${word}"[\ \ ]*) 1127 return 0 1128 ;; 1129 "#"*) 1130 continue 1131 ;; 1132 *[A-Za-z0-9]*) 1133 # give up at the first non-empty non-comment line 1134 return 1 1135 ;; 1136 esac 1137 done <"$file" 1138 return 1 1139} 1140 1141# 1142# print_rc_metadata string 1143# Print the specified string in such a way that the post-processor 1144# inside /etc/rc will treat it as meta-data. 1145# 1146# If we are not running inside /etc/rc, do nothing. 1147# 1148# For public use by any rc.d script, the string must begin with 1149# "note:", followed by arbitrary text. The intent is that the text 1150# will appear in a log file but not on the console. 1151# 1152# For private use within /etc/rc, the string must contain a 1153# keyword recognised by the rc_postprocess_metadata() function 1154# defined in /etc/rc, followed by a colon, followed by one or more 1155# colon-separated arguments associated with the keyword. 1156# 1157print_rc_metadata() 1158{ 1159 # _rc_postprocessor fd, if defined, is the fd to which we must 1160 # print, prefixing the output with $_rc_metadata_prefix. 1161 # 1162 if _have_rc_postprocessor; then 1163 command printf "%s%s\n" "$rc_metadata_prefix" "$1" \ 1164 >&${_rc_postprocessor_fd} 1165 fi 1166} 1167 1168# 1169# _flush_rc_output 1170# Arrange for output to be flushed, if we are running 1171# inside /etc/rc with postprocessing. 1172# 1173_flush_rc_output() 1174{ 1175 print_rc_metadata "nop" 1176} 1177 1178# 1179# print_rc_normal [-n] string 1180# Print the specified string in such way that it is treated as 1181# normal output, regardless of whether or not we are running 1182# inside /etc/rc with post-processing. 1183# 1184# If "-n" is specified in $1, then the string in $2 is printed 1185# without a newline; otherwise, the string in $1 is printed 1186# with a newline. 1187# 1188# Intended use cases include: 1189# 1190# o An rc.d script can use ``print_rc_normal -n'' to print a 1191# partial line in such a way that it appears immediately 1192# instead of being buffered by rc(8)'s post-processor. 1193# 1194# o An rc.d script that is run via the no_rc_postprocess 1195# function (so most of its output is invisible to rc(8)'s 1196# post-processor) can use print_rc_normal to force some of its 1197# output to be seen by the post-processor. 1198# 1199# 1200print_rc_normal() 1201{ 1202 # print to stdout or _rc_postprocessor_fd, depending on 1203 # whether not we have an rc postprocessor. 1204 # 1205 local fd=1 1206 _have_rc_postprocessor && fd="${_rc_postprocessor_fd}" 1207 case "$1" in 1208 "-n") 1209 command printf "%s" "$2" >&${fd} 1210 _flush_rc_output 1211 ;; 1212 *) 1213 command printf "%s\n" "$1" >&${fd} 1214 ;; 1215 esac 1216} 1217 1218# 1219# no_rc_postprocess cmd... 1220# Execute the specified command in such a way that its output 1221# bypasses the post-processor that handles the output from 1222# most commands that are run inside /etc/rc. If we are not 1223# inside /etc/rc, then just execute the command without special 1224# treatment. 1225# 1226# The intent is that interactive commands can be run via 1227# no_rc_postprocess(), and their output will apear immediately 1228# on the console instead of being hidden or delayed by the 1229# post-processor. An unfortunate consequence of the output 1230# bypassing the post-processor is that the output will not be 1231# logged. 1232# 1233no_rc_postprocess() 1234{ 1235 if _have_rc_postprocessor; then 1236 "$@" >&${_rc_original_stdout_fd} 2>&${_rc_original_stderr_fd} 1237 else 1238 "$@" 1239 fi 1240} 1241 1242# 1243# twiddle 1244# On each call, print a different one of "/", "-", "\\", "|", 1245# followed by a backspace. The most recently printed value is 1246# saved in $_twiddle_state. 1247# 1248# Output is to /dev/tty, so this function may be useful even inside 1249# a script whose output is redirected. 1250# 1251twiddle() 1252{ 1253 case "$_twiddle_state" in 1254 '/') _next='-' ;; 1255 '-') _next='\' ;; 1256 '\') _next='|' ;; 1257 *) _next='/' ;; 1258 esac 1259 command printf "%s\b" "$_next" >/dev/tty 1260 _twiddle_state="$_next" 1261} 1262 1263# 1264# human_exit_code 1265# Print the a human version of the exit code. 1266# 1267human_exit_code() 1268{ 1269 if [ "$1" -lt 127 ] 1270 then 1271 echo "exited with code $1" 1272 elif [ "$(expr $1 % 256)" -eq 127 ] 1273 then 1274 # This cannot really happen because the shell will not 1275 # pass stopped job status out and the exit code is limited 1276 # to 8 bits. This code is here just for completeness. 1277 echo "stopped with signal $(expr $1 / 256)" 1278 else 1279 echo "terminated with signal $(expr $1 - 128)" 1280 fi 1281} 1282 1283# 1284# collapse_backslash_newline 1285# Copy input to output, collapsing <backslash><newline> 1286# to nothing, but leaving other backslashes alone. 1287# 1288collapse_backslash_newline() 1289{ 1290 local line 1291 while read -r line ; do 1292 case "$line" in 1293 *\\) 1294 # print it, without the backslash or newline 1295 command printf "%s" "${line%?}" 1296 ;; 1297 *) 1298 # print it, with a newline 1299 command printf "%s\n" "${line}" 1300 ;; 1301 esac 1302 done 1303} 1304 1305# Shell implementations of basename and dirname, usable before 1306# the /usr file system is mounted. 1307# 1308basename() 1309{ 1310 local file="$1" 1311 local suffix="$2" 1312 local base 1313 1314 base="${file##*/}" # remove up to and including last '/' 1315 base="${base%${suffix}}" # remove suffix, if any 1316 command printf "%s\n" "${base}" 1317} 1318 1319dirname() 1320{ 1321 local file="$1" 1322 local dir 1323 1324 case "$file" in 1325 /*/*) dir="${file%/*}" ;; # common case: absolute path 1326 /*) dir="/" ;; # special case: name in root dir 1327 */*) dir="${file%/*}" ;; # common case: relative path with '/' 1328 *) dir="." ;; # special case: name without '/' 1329 esac 1330 command printf "%s\n" "${dir}" 1331} 1332 1333# Override the normal "echo" and "printf" commands, so that 1334# partial lines printed by rc.d scripts appear immediately, 1335# instead of being buffered by rc(8)'s post-processor. 1336# 1337# Naive use of the echo or printf commands from rc.d scripts, 1338# elsewhere in rc.subr, or anything else that sources rc.subr, 1339# will call these functions. To call the real echo and printf 1340# commands, use "command echo" or "command printf". 1341# 1342echo() 1343{ 1344 command echo "$@" 1345 case "$1" in 1346 '-n') _flush_rc_output ;; 1347 esac 1348} 1349printf() 1350{ 1351 command printf "$@" 1352 case "$1" in 1353 *'\n') : ;; 1354 *) _flush_rc_output ;; 1355 esac 1356} 1357 1358kat() { 1359 local i 1360 local v 1361 for i; do 1362 while read -r v; do 1363 v="${v%%#*}" 1364 if [ -z "$v" ]; then 1365 continue 1366 fi 1367 echo "$v" 1368 done < "$i" 1369 done 1370} 1371 1372_rc_subr_loaded=: 1373