sysrc revision 292833
1#!/bin/sh
2#-
3# Copyright (c) 2010-2015 Devin Teske
4# All rights reserved.
5#
6# Redistribution and use in source and binary forms, with or without
7# modification, are permitted provided that the following conditions
8# are met:
9# 1. Redistributions of source code must retain the above copyright
10#    notice, this list of conditions and the following disclaimer.
11# 2. Redistributions in binary form must reproduce the above copyright
12#    notice, this list of conditions and the following disclaimer in the
13#    documentation and/or other materials provided with the distribution.
14#
15# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25# SUCH DAMAGE.
26#
27# $FreeBSD: stable/10/usr.sbin/sysrc/sysrc 292833 2015-12-28 17:50:31Z dteske $
28#
29############################################################ INCLUDES
30
31# Prevent `-d' from being interpreted as a debug flag by common.subr
32DEBUG_SELF_INITIALIZE=
33
34BSDCFG_SHARE="/usr/share/bsdconfig"
35[ "$_COMMON_SUBR" ] || . $BSDCFG_SHARE/common.subr || exit 1
36[ "$_SYSRC_SUBR"  ] || f_include $BSDCFG_SHARE/sysrc.subr
37
38############################################################ GLOBALS
39
40#
41# Version information
42#
43SYSRC_VERSION="7.0 Sep-13,2015"
44
45#
46# Options
47#
48CHECK_ONLY=
49DEFAULT=
50DELETE=
51DESCRIBE=
52EXISTING_ONLY=
53IGNORE_UNKNOWNS=
54JAIL=
55LIST_SERVICE_CONFS=
56LIST_CONFS=
57QUIET=
58ROOTDIR=
59SERVICE=
60SHOW_ALL=
61SHOW_EQUALS=
62SHOW_FILE=
63SHOW_NAME=1
64SHOW_VALUE=1
65VERBOSE=
66
67############################################################ FUNCTIONS
68
69# die [$fmt [$opts ...]]
70#
71# Optionally print a message to stderr before exiting with failure status.
72#
73die()
74{
75	local fmt="$1"
76	[ $# -gt 0 ] && shift 1
77	[  "$fmt"  ] && f_err "$fmt\n" "$@"
78
79	exit $FAILURE
80}
81
82# usage
83#
84# Prints a short syntax statement and exits.
85#
86usage()
87{
88	f_err "Usage: %s [OPTIONS] %s\n" "$pgm" \
89		"{name[[+|-]=value] ... | -a | -A | -l | -L [name ...]}"
90	f_err "Try \`%s --help' for more information.\n" "$pgm"
91	die
92}
93
94# help
95#
96# Prints a full syntax statement and exits.
97#
98help()
99{
100	local optfmt="\t%-11s%s\n"
101	local envfmt="\t%-17s%s\n"
102
103	f_err "Usage: %s [OPTIONS] name[[+|-]=value] ...\n" "$pgm"
104	f_err "Usage: %s [OPTIONS] -a | -A\n" "$pgm"
105	f_err "Usage: %s [OPTIONS] -l | -L [name ...]\n" "$pgm"
106
107	f_err "OPTIONS:\n"
108	f_err "$optfmt" "-a" \
109	      "Dump a list of all non-default configuration variables."
110	f_err "$optfmt" "-A" \
111	      "Dump a list of all configuration variables (incl. defaults)."
112	f_err "$optfmt" "-c" \
113	      "Check. Return success if set or no changes, else error."
114	f_err "$optfmt" "-d" \
115	      "Print a description of the given variable."
116	f_err "$optfmt" "-D" \
117	      "Show default value(s) only (this is the same as setting"
118	f_err "$optfmt" "" \
119	      "RC_CONFS to NULL or passing \`-f' with a NULL file-argument)."
120	f_err "$optfmt" "-e" \
121	      "Print query results as \`var=value' (useful for producing"
122	f_err "$optfmt" "" \
123	      "output to be fed back in). Ignored if \`-n' is specified."
124	f_err "$optfmt" "-E" \
125	      "Existing files only with \`-[lL]' or when changing a setting."
126	f_err "$optfmt" "-f file" \
127	      "Operate on the specified file(s) instead of rc_conf_files."
128	f_err "$optfmt" "" \
129	      "Can be specified multiple times for additional files."
130	f_err "$optfmt" "-F" \
131	      "Show only the last rc.conf(5) file each directive is in."
132	f_err "$optfmt" "-h" \
133	      "Print a short usage statement to stderr and exit."
134	f_err "$optfmt" "--help" \
135	      "Print this message to stderr and exit."
136	f_err "$optfmt" "-i" \
137	      "Ignore unknown variables."
138	f_err "$optfmt" "-j jail" \
139	      "The jid or name of the jail to operate within (overrides"
140	f_err "$optfmt" "" \
141	      "\`-R dir'; requires jexec(8))."
142	f_err "$optfmt" "-l" \
143	      "List configuration files used at startup on stdout and exit."
144	f_err "$optfmt" "-L" \
145	      "List all configuration files including rc.conf.d entries."
146	f_err "$optfmt" "-n" \
147	      "Show only variable values, not their names."
148	f_err "$optfmt" "-N" \
149	      "Show only variable names, not their values."
150	f_err "$optfmt" "-q" \
151	      "Quiet. Disable verbose and hide certain errors."
152	f_err "$optfmt" "-s name" \
153	      "Process additional \`rc.conf.d' entries for service name."
154	f_err "$optfmt" "" \
155	      "Ignored if \`-f file' is given."
156	f_err "$optfmt" "-R dir" \
157	      "Operate within the root directory \`dir' rather than \`/'."
158	f_err "$optfmt" "-v" \
159	      "Verbose. Print the pathname of the specific rc.conf(5)"
160	f_err "$optfmt" "" \
161	      "file where the directive was found."
162	f_err "$optfmt" "--version" \
163	      "Print version information to stdout and exit."
164	f_err "$optfmt" "-x" \
165	      "Remove variable(s) from specified file(s)."
166	f_err "\n"
167
168	f_err "ENVIRONMENT:\n"
169	f_err "$envfmt" "RC_CONFS" \
170	      "Override default rc_conf_files (even if set to NULL)."
171	f_err "$envfmt" "RC_DEFAULTS" \
172	      "Location of \`/etc/defaults/rc.conf' file."
173
174	die
175}
176
177# jail_depend
178#
179# Dump dependencies such as language-file variables and include files to stdout
180# to be piped-into sh(1) running via jexec(8)/chroot(8). As a security measure,
181# this prevents existing language files and library files from being loaded in
182# the jail. This also relaxes the requirement to have these files in every jail
183# before sysrc can be used on said jail.
184#
185jail_depend()
186{
187	#
188	# Indicate that we are jailed
189	#
190	echo export _SYSRC_JAILED=1
191
192	#
193	# Print i18n language variables (their current values are sanitized
194	# and re-printed for interpretation so that the i18n language files
195	# do not need to exist within the jail).
196	#
197	local var val
198	for var in \
199		msg_cannot_create_permission_denied \
200		msg_permission_denied \
201		msg_previous_syntax_errors \
202	; do
203		val=$( eval echo \"\$$var\" |
204			awk '{ gsub(/'\''/, "'\''\\'\'\''"); print }' )
205		echo $var="'$val'"
206	done
207
208	#
209	# Print include dependencies
210	#
211	echo DEBUG_SELF_INITIALIZE=
212	cat $BSDCFG_SHARE/common.subr
213	cat $BSDCFG_SHARE/sysrc.subr
214}
215
216# escape $string [$var_to_set]
217#
218# Escape $string contents so that the contents can be properly encapsulated in
219# single-quotes (making for safe evaluation).
220#
221# NB: See `bsdconfig includes -dF escape' for relevant information/discussion.
222# NB: Abridged version of `f_shell_escape()' from bsdconfig(8) `strings.subr'.
223#
224escape()
225{
226	local __start="$1" __var_to_set="$2" __string=
227	while [ "$__start" ]; do
228		case "$__start" in *\'*)
229			__string="$__string${__start%%\'*}'\\''"
230			__start="${__start#*\'}" continue
231		esac
232		break
233	done
234	__string="$__string$__start"
235	if [ "$__var_to_set" ]; then
236		setvar "$__var_to_set" "$__string"
237	else
238		echo "$__string"
239	fi
240}
241
242############################################################ MAIN SOURCE
243
244#
245# Perform sanity checks
246#
247[ $# -gt 0 ] || usage # NOTREACHED
248
249#
250# Check for `--help' and `--version' command-line option
251#
252for arg in "$@"; do
253	case "$arg" in
254	--) break ;;
255	--help) help ;; # NOTREACHED
256	--version) # see GLOBALS
257		echo "$SYSRC_VERSION"
258		exit $FAILURE ;;
259	esac
260done
261unset arg
262
263#
264# Process command-line flags
265#
266while getopts aAcdDeEf:Fhij:lLnNqR:s:vxX flag; do
267	case "$flag" in
268	a) SHOW_ALL=${SHOW_ALL:-1} ;;
269	A) SHOW_ALL=2 ;;
270	c) CHECK_ONLY=1 ;;
271	d) DESCRIBE=1 ;;
272	D) DEFAULT=1 RC_CONFS= ;;
273	e) SHOW_EQUALS=1 ;;
274	E) EXISTING_ONLY=1 ;;
275	f) DEFAULT= RC_CONFS="$RC_CONFS${RC_CONFS:+ }$OPTARG" ;;
276	F) SHOW_FILE=1 ;;
277	h) usage ;; # NOTREACHED
278	i) IGNORE_UNKNOWNS=1 ;;
279	j) [ "$OPTARG" ] ||
280		die "%s: Missing or null argument to \`-j' flag" "$pgm"
281	   JAIL="$OPTARG" ;;
282	l) LIST_CONFS=1 ;;
283	L) LIST_SERVICE_CONFS=1 ;;
284	n) SHOW_NAME= ;;
285	N) SHOW_VALUE= ;;
286	q) QUIET=1 VERBOSE= ;;
287	R) [ "$OPTARG" ] ||
288		die "%s: Missing or null argument to \`-R' flag" "$pgm"
289	   ROOTDIR="$OPTARG" ;;
290	s) [ "$OPTARG" ] ||
291		die "%s: Missing or null argument to \`-s' flag" "$pgm"
292	   SERVICE="$OPTARG" ;;
293	v) VERBOSE=1 QUIET= ;;
294	x) DELETE=${DELETE:-1} ;;
295	X) DELETE=2 ;;
296	\?) usage ;; # NOTREACHED
297	esac
298done
299shift $(( $OPTIND - 1 ))
300
301#
302# Process `-L' flag
303#
304if [ "$LIST_SERVICE_CONFS" ]; then
305	list= 
306
307	#
308	# List rc_conf_files if no service names given
309	#
310	files=
311	[ $# -eq 0 ] && files=$( f_sysrc_get rc_conf_files )
312	for file in $files; do
313		if [ "$EXISTING_ONLY" ]; then
314			[ -e "$file" -a ! -d "$file" ] || continue
315		fi
316		case "$list" in
317		"$file"|*" $file"|"$file "*|*" $file "*) continue ;;
318		esac
319		list="$list $file"
320	done
321	list="${list# }"
322	if [ $# -eq 0 ]; then
323		if [ "$VERBOSE" ]; then
324			echo rc_conf_files: $list
325		elif [ "$SHOW_EQUALS" ]; then
326			echo "rc_conf_files=\"$list\""
327		fi
328	fi
329
330	#
331	# List rc.conf.d entries
332	#
333	retval=$SUCCESS
334	for service in ${*:-$( service -l )}; do
335		slist=
336		f_sysrc_service_configs $service files || retval=$? continue
337		for file in $files; do
338			if [ "$EXISTING_ONLY" ]; then
339				[ -e "$file" -a ! -d "$file" ] || continue
340			fi
341			if [ ! "$VERBOSE" -a ! "$SHOW_EQUALS" ]; then
342				case "$list" in
343				"$file"|*" $file"|"$file "*|*" $file "*)
344					continue ;;
345				esac
346			fi
347			slist="$slist $file"
348		done
349		slist="${slist# }"
350		if [ $# -gt 0 ]; then
351			[ "$slist" ] || retval=$?
352		fi
353		if [ "$VERBOSE" ]; then
354			[ "$slist" ] && echo "$service: $slist"
355			continue
356		elif [ "$SHOW_EQUALS" ]; then
357			[ "$slist" ] && echo "$service=\"$slist\""
358			continue
359		fi
360		list="$list${slist:+ }$slist"
361	done
362	if [ ! "$VERBOSE" -a ! "$SHOW_EQUALS" ]; then
363		if [ $# -eq 0 -o ! "$QUIET" ]; then
364			list="${list# }"
365			[ "$list" ] && echo $list
366		fi
367	fi
368
369	exit $retval
370fi
371
372#
373# Process `-s name' argument
374#
375if [ "$SERVICE" -a ! "${RC_CONFS+set}" ]; then
376	if f_sysrc_service_configs "$SERVICE" RC_CONFS; then
377		rc_conf_files=$( f_sysrc_get rc_conf_files )
378		RC_CONFS="$rc_conf_files${RC_CONFS:+ }$RC_CONFS"
379		unset rc_conf_files
380	else
381		unset RC_CONFS
382	fi
383fi
384
385#
386# Process `-E' option flag
387#
388if [ "$EXISTING_ONLY" ]; then
389	#
390	# To get f_sysrc_*() to ignore missing rc_conf_files, we have to use
391	# RC_CONFS to override the unpreened value. If RC_CONFS already has a
392	# value (`-D', `-f file', `-s name', or inherited from parent), use it.
393	# Otherwise, include filtered contents of rc_conf_files.
394	# 
395	RC_CONFS=$(
396		if [ "${RC_CONFS+set}" ]; then
397			set -- $RC_CONFS
398		else
399			set -- $( f_sysrc_get rc_conf_files )
400		fi
401		while [ $# -gt 0 ]; do
402			[ -f "$1" ] && echo -n " $1"
403			shift
404		done
405	)
406	RC_CONFS="${RC_CONFS# }"
407fi
408
409#
410# Process `-l' option flag
411#
412if [ "$LIST_CONFS" ]; then
413	[ $# -eq 0 ] || usage
414	if [ "$DEFAULT" ]; then
415		echo "$RC_DEFAULTS"
416	elif [ "${RC_CONFS+set}" ]; then
417		echo "$RC_CONFS"
418	else
419		f_sysrc_get rc_conf_files
420	fi
421	exit $SUCCESS
422fi
423
424#
425# [More] Sanity checks (e.g., "sysrc --")
426#
427[ $# -eq 0 -a ! "$SHOW_ALL" ] && usage # NOTREACHED
428
429#
430# Taint-check all rc.conf(5) files
431#
432errmsg="$pgm: Exiting due to previous syntax errors"
433if [ "${RC_CONFS+set}" ]; then
434	( for i in $RC_CONFS; do
435	  	[ -e "$i" ] || continue
436	  	/bin/sh -n "$i" || exit $FAILURE
437	  done
438	  exit $SUCCESS
439	) || die "$errmsg"
440else
441	/bin/sh -n "$RC_DEFAULTS" || die "$errmsg"
442	( . "$RC_DEFAULTS"
443	  for i in $rc_conf_files; do
444	  	[ -e "$i" ] || continue
445	  	/bin/sh -n "$i" || exit $FAILURE
446	  done
447	  exit $SUCCESS
448	) || die "$errmsg"
449fi
450
451#
452# Process `-x' (and secret `-X') command-line options
453#
454errmsg="$pgm: \`-x' option incompatible with \`-a'/\`-A' options"
455errmsg="$errmsg (use \`-X' to override)"
456if [ "$DELETE" -a "$SHOW_ALL" ]; then
457	[ "$DELETE" = "2" ] || die "$errmsg"
458fi
459
460#
461# Pre-flight for `-c' command-line option
462#
463[ "$CHECK_ONLY" -a "$SHOW_ALL" ] &&
464	die "$pgm: \`-c' option incompatible with \`-a'/\`-A' options"
465
466#
467# Process `-e', `-n', and `-N' command-line options
468#
469SEP=': '
470[ "$SHOW_FILE" ] && SHOW_EQUALS=
471[ "$SHOW_NAME" ] || SHOW_EQUALS=
472[ "$VERBOSE" = "0" ] && VERBOSE=
473if [ ! "$SHOW_VALUE" ]; then
474	SHOW_NAME=1
475	SHOW_EQUALS=
476fi
477[ "$SHOW_EQUALS" ] && SEP='="'
478
479#
480# Process `-j jail' and `-R dir' command-line options
481#
482if [ "$JAIL" -o "$ROOTDIR" ]; then
483	#
484	# Reconstruct the arguments that we want to carry-over
485	#
486	args="
487		${VERBOSE:+-v}
488		${QUIET:+-q}
489		$( [ "$DELETE" = "1" ] && echo \ -x )
490		$( [ "$DELETE" = "2" ] && echo \ -X )
491		$( [ "$SHOW_ALL" = "1" ] && echo \ -a )
492		$( [ "$SHOW_ALL" = "2" ] && echo \ -A )
493		${CHECK_ONLY:+-c}
494		${DEFAULT:+-D}
495		${EXISTING_ONLY:+-E}
496		${LIST_CONFS:+-l}
497		${LIST_SERVICE_CONFS:+-L}
498		${DESCRIBE:+-d}
499		${SHOW_EQUALS:+-e}
500		${IGNORE_UNKNOWNS:+-i}
501		$( [ "$SHOW_NAME"  ] || echo \ -n )
502		$( [ "$SHOW_VALUE" ] || echo \ -N )
503		$( [ "$SHOW_FILE"  ] && echo \ -F )
504	"
505	if [ "$SERVICE" ]; then
506		escape "$SERVICE" _SERVICE
507		args="$args -s '$_SERVICE'"
508		unset _SERVICE
509	fi
510	if [ "${RC_CONFS+set}" ]; then
511		escape "$RC_CONFS" _RC_CONFS
512		args="$args -f '$_RC_CONFS'"
513		unset _RC_CONFS
514	fi
515	for arg in "$@"; do
516		escape "$arg" arg
517		args="$args '$arg'"
518	done
519
520	#
521	# If both are supplied, `-j jail' supercedes `-R dir'
522	#
523	if [ "$JAIL" ]; then
524		#
525		# Re-execute ourselves with sh(1) via jexec(8)
526		#
527		( echo set -- $args
528		  jail_depend
529		  cat $0
530		) | env - RC_DEFAULTS="$RC_DEFAULTS" \
531		    	/usr/sbin/jexec "$JAIL" /bin/sh
532		exit $?
533	elif [ "$ROOTDIR" ]; then
534		#
535		# Make sure that the root directory specified is not to any
536		# running jails.
537		#
538		# NOTE: To maintain backward compatibility with older jails on
539		# older systems, we will not perform this check if either the
540		# jls(1) or jexec(8) utilities are missing.
541		#
542		if f_have jexec && f_have jls; then
543			jid=$( jls jid path |
544				while read JID JROOT; do
545					[ "$JROOT" = "$ROOTDIR" ] || continue
546					echo $JID
547				done
548			)
549
550			#
551			# If multiple running jails match the specified root
552			# directory, exit with error.
553			#
554			if [ "$jid" -a "${jid%[$IFS]*}" != "$jid" ]; then
555				die "%s: %s: %s" "$pgm" "$ROOTDIR" \
556				    "$( echo "Multiple jails claim this" \
557				             "directory as their root." \
558				             "(use \`-j jail' instead)" )"
559			fi
560
561			#
562			# If only a single running jail matches the specified
563			# root directory, implicitly use `-j jail'.
564			#
565			if [ "$jid" ]; then
566				#
567				# Re-execute outselves with sh(1) via jexec(8)
568				#
569				( echo set -- $args
570				  jail_depend
571				  cat $0
572				) | env - RC_DEFAULTS="$RC_DEFAULTS" \
573					/usr/sbin/jexec "$jid" /bin/sh
574				exit $?
575			fi
576
577			# Otherwise, fall through and allow chroot(8)
578		fi
579
580		#
581		# Re-execute ourselves with sh(1) via chroot(8)
582		#
583		( echo set -- $args
584		  jail_depend
585		  cat $0
586		) | env - RC_DEFAULTS="$RC_DEFAULTS" \
587		    	/usr/sbin/chroot "$ROOTDIR" /bin/sh
588		exit $?
589	fi
590fi
591
592#
593# Process `-a' or `-A' command-line options
594#
595if [ "$SHOW_ALL" ]; then
596	#
597	# Get a list of variables that are currently set in the rc.conf(5)
598	# files (included `/etc/defaults/rc.conf') by performing a call to
599	# source_rc_confs() in a clean environment.
600	#
601	( # Operate in a sub-shell to protect the parent environment
602		#
603		# Set which variables we want to preserve in the environment.
604		# Append the pipe-character (|) to the list of internal field
605		# separation (IFS) characters, allowing us to use the below
606		# list both as an extended grep (-E) pattern and argument list
607		# (required to first get f_clean_env() to preserve these in the
608		# environment and then later to prune them from the list of
609		# variables produced by set(1)).
610		#
611		IFS="$IFS|"
612		EXCEPT="IFS|EXCEPT|PATH|RC_DEFAULTS|OPTIND|DESCRIBE|SEP"
613		EXCEPT="$EXCEPT|DELETE|SHOW_ALL|SHOW_EQUALS|SHOW_NAME|DEFAULT"
614		EXCEPT="$EXCEPT|SHOW_VALUE|SHOW_FILE|VERBOSE|RC_CONFS|SERVICE"
615		EXCEPT="$EXCEPT|pgm|SUCCESS|FAILURE|CHECK_ONLY|EXISTING_ONLY"
616		EXCEPT="$EXCEPT|LIST_CONFS|LIST_SERVICE_CONFS"
617		EXCEPT="$EXCEPT|f_sysrc_desc_awk|f_sysrc_delete_awk"
618
619		#
620		# Clean the environment (except for our required variables)
621		# and then source the required files.
622		#
623		f_clean_env --except $EXCEPT
624		if [ -f "$RC_DEFAULTS" -a -r "$RC_DEFAULTS" ]; then
625			. "$RC_DEFAULTS"
626
627			#
628			# If passed `-a' (rather than `-A'), re-purge the
629			# environment, removing the rc.conf(5) defaults.
630			#
631			[ "$SHOW_ALL" = "1" ] &&
632				f_clean_env --except rc_conf_files $EXCEPT
633
634			#
635			# If `-f file' was passed, set $rc_conf_files to an
636			# explicit value, modifying the default behavior of
637			# source_rc_confs().
638			#
639			if [ "${RC_CONFS+set}" ]; then
640				[ "$SHOW_ALL" = "1" -a "$SERVICE" -a \
641					! "$DEFAULT" ] || rc_conf_files=
642				rc_conf_files="$rc_conf_files $RC_CONFS"
643				rc_conf_files="${rc_conf_files# }"
644				rc_conf_files="${rc_conf_files% }"
645			fi
646
647			source_rc_confs
648
649			#
650			# If passed `-a' (rather than `-A'), remove
651			# `rc_conf_files' unless it was defined somewhere
652			# other than rc.conf(5) defaults.
653			#
654			[ "$SHOW_ALL" = "1" -a \
655			  "$( f_sysrc_find rc_conf_files )" = "$RC_DEFAULTS" \
656			] && unset rc_conf_files
657		fi
658
659		for NAME in $( set |
660			awk -F= '/^[[:alpha:]_][[:alnum:]_]*=/ {print $1}' |
661			grep -Ev "^($EXCEPT)$"
662		); do
663			#
664			# If enabled, describe rather than expand value
665			#
666			if [ "$DESCRIBE" ]; then
667				echo "$NAME: $( f_sysrc_desc "$NAME" )"
668				continue
669			fi
670
671			#
672			# If `-F' is passed, find it and move on
673			#
674			if [ "$SHOW_FILE" ]; then
675				[ "$SHOW_NAME" ] && echo -n "$NAME: "
676				f_sysrc_find "$NAME"
677				continue
678			fi
679
680			#
681			# If `-X' is passed, delete the variables
682			#
683			if [ "$DELETE" = "2" ]; then
684				f_sysrc_delete "$NAME"
685				continue
686			fi
687
688			[ "$VERBOSE" ] &&
689				echo -n "$( f_sysrc_find "$NAME" ): "
690
691			#
692			# If `-N' is passed, simplify the output
693			#
694			if [ ! "$SHOW_VALUE" ]; then
695				echo "$NAME"
696				continue
697			fi
698
699			echo "${SHOW_NAME:+$NAME$SEP}$(
700			      f_sysrc_get "$NAME" )${SHOW_EQUALS:+\"}"
701
702		done
703	)
704
705	#
706	# Ignore the remainder of positional arguments.
707	#
708	exit $SUCCESS
709fi
710
711#
712# Process command-line arguments
713#
714status=$SUCCESS
715while [ $# -gt 0 ]; do
716	NAME="${1%%=*}"
717
718	case "$NAME" in
719	*+) mode=APPEND NAME="${NAME%+}" ;;
720	*-) mode=REMOVE NAME="${NAME%-}" ;;
721	 *) mode=ASSIGN
722	esac
723
724	[ "$DESCRIBE" ] &&
725		echo "$NAME: $( f_sysrc_desc "$NAME" )"
726
727	case "$1" in
728	*=*)
729		#
730		# Like sysctl(8), if both `-d' AND "name=value" is passed,
731		# first describe (done above), then attempt to set
732		#
733
734		# If verbose, prefix line with where the directive lives
735		if [ "$VERBOSE" -a ! "$CHECK_ONLY" ]; then
736			file=$( f_sysrc_find "$NAME" )
737			[ "$file" = "$RC_DEFAULTS" -o ! "$file" ] &&
738				file=$( f_sysrc_get 'rc_conf_files%%[$IFS]*' )
739			if [ "$SHOW_EQUALS" ]; then
740				echo -n ": $file; "
741			else
742				echo -n "$file: "
743			fi
744		fi
745
746		#
747		# If `-x' or `-X' is passed, delete the variable and ignore the
748		# desire to set some value
749		#
750		if [ "$DELETE" ]; then
751			f_sysrc_delete "$NAME" || status=$FAILURE
752			shift 1
753			continue
754		fi
755
756		#
757		# If `-c' is passed, simply compare and move on
758		#
759		if [ "$CHECK_ONLY" ]; then
760			if ! IGNORED=$( f_sysrc_get "$NAME?" ); then
761				status=$FAILURE
762				[ "$VERBOSE" ] &&
763					echo "$NAME: not currently set"
764				shift 1
765				continue
766			fi
767			value=$( f_sysrc_get "$NAME" )
768			if [ "$value" != "${1#*=}" ]; then
769				status=$FAILURE
770				if [ "$VERBOSE" ]; then
771					echo -n "$( f_sysrc_find "$NAME" ): "
772					echo -n "$NAME: would change from "
773					echo "\`$value' to \`${1#*=}'"
774				fi
775			elif [ "$VERBOSE" ]; then
776				echo -n "$( f_sysrc_find "$NAME" ): "
777				echo "$NAME: already set to \`$value'"
778			fi
779			shift 1
780			continue
781		fi
782
783		#
784		# Determine both `before' value and appropriate `new' value
785		#
786		case "$mode" in
787		APPEND)
788			before=$( f_sysrc_get "$NAME" )
789			add="${1#*=}"
790			delim="${add%"${add#?}"}" # first character
791			oldIFS="$IFS"
792			case "$delim" in
793			""|[$IFS]|[a-zA-Z0-9]) delim=" " ;;
794			*) IFS="$delim"
795			esac
796			new="$before"
797			for a in $add; do
798				[ "$a" ] || continue
799				skip=
800				for b in $before; do
801					[ "$b" = "$a" ] && skip=1 break
802				done
803				[ "$skip" ] || new="$new$delim$a"
804			done
805			new="${new#"$delim"}" IFS="$oldIFS"
806			unset add delim oldIFS a skip b
807			[ "$SHOW_FILE" ] && before=$( f_sysrc_find "$NAME" )
808			;;
809		REMOVE)
810			before=$( f_sysrc_get "$NAME" )
811			remove="${1#*=}"
812			delim="${remove%"${remove#?}"}" # first character
813			oldIFS="$IFS"
814			case "$delim" in
815			""|[$IFS]|[a-zA-Z0-9]) delim=" " ;;
816			*) IFS="$delim"
817			esac
818			new=
819			for b in $before; do
820				[ "$b" ] || continue
821				add=1
822				for r in $remove; do
823					[ "$r" = "$b" ] && add= break
824				done
825				[ "$add" ] && new="$new$delim$b"
826			done
827			new="${new#"$delim"}" IFS="$oldIFS"
828			unset remove delim oldIFS b add r
829			[ "$SHOW_FILE" ] && before=$( f_sysrc_find "$NAME" )
830			;;
831		*) # ASSIGN
832			if [ "$SHOW_FILE" ]; then
833				before=$( f_sysrc_find "$NAME" )
834			else
835				before=$( f_sysrc_get "$NAME" )
836			fi
837			new="${1#*=}"
838		esac
839
840		#
841		# If `-N' is passed, simplify the output
842		#
843		if [ ! "$SHOW_VALUE" ]; then
844			echo "$NAME"
845			f_sysrc_set "$NAME" "$new"
846		else
847			if f_sysrc_set "$NAME" "$new"; then
848				if [ "$SHOW_FILE" ]; then
849					after=$( f_sysrc_find "$NAME" )
850				else
851					after=$( f_sysrc_get "$NAME" )
852				fi
853				echo -n "${SHOW_NAME:+$NAME$SEP}"
854				echo -n "$before${SHOW_EQUALS:+\" #}"
855				echo -n " -> ${SHOW_EQUALS:+\"}$after"
856				echo "${SHOW_EQUALS:+\"}"
857			fi
858		fi
859		;;
860	*)
861		if ! IGNORED=$( f_sysrc_get "$NAME?" ); then
862			[ "$IGNORE_UNKNOWNS" -o "$QUIET" ] ||
863				echo "$pgm: unknown variable '$NAME'"
864			shift 1
865			status=$FAILURE
866			continue
867		fi
868
869		# The above check told us what we needed for `-c'
870		if [ "$CHECK_ONLY" ]; then
871			shift 1
872			continue
873		fi
874
875		#
876		# Like sysctl(8), when `-d' is passed, desribe it
877		# (already done above) rather than expanding it
878		#
879
880		if [ "$DESCRIBE" ]; then
881			shift 1
882			continue
883		fi
884
885		#
886		# If `-x' or `-X' is passed, delete the variable
887		#
888		if [ "$DELETE" ]; then
889			f_sysrc_delete "$NAME" || status=$FAILURE
890			shift 1
891			continue
892		fi
893
894		#
895		# If `-F' is passed, find it and move on
896		#
897		if [ "$SHOW_FILE" ]; then
898			[ "$SHOW_NAME" ] && echo -n "$NAME: "
899			f_sysrc_find "$NAME"
900			shift 1
901			continue
902		fi
903
904		if [ "$VERBOSE" ]; then
905			if [ "$SHOW_EQUALS" ]; then
906				echo -n ": $( f_sysrc_find "$NAME" ); "
907			else
908				echo -n "$( f_sysrc_find "$NAME" ): "
909			fi
910		fi
911
912		#
913		# If `-N' is passed, simplify the output
914		#
915		if [ ! "$SHOW_VALUE" ]; then
916			echo "$NAME"
917		else
918			echo "${SHOW_NAME:+$NAME$SEP}$(
919			      f_sysrc_get "$NAME" )${SHOW_EQUALS:+\"}"
920		fi
921	esac
922	shift 1
923done
924
925exit $status # $SUCCESS unless error occurred with either `-c' or `-x'
926
927################################################################################
928# END
929################################################################################
930