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