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