Index | Thread | Search

From:
Klemens Nanni <kn@openbsd.org>
Subject:
rc.subr: hoist OPTIND reset out of service scripts
To:
OpenBSD tech <tech@openbsd.org>
Cc:
Antoine Jacoutot <ajacoutot@bsdfrog.org>, obsd@mulh.net
Date:
Sat, 29 Nov 2025 19:03:07 +0000

Download raw body.

Thread
  • Klemens Nanni:

    rc.subr: hoist OPTIND reset out of service scripts

See bugs@ "etc/rc.d/unbound assumes defaults in rc_pre()"
and the last rc.d/{unbound,spamlogd} commit for context.

In base only those two use getopts, ports I did not check.

Either way, the common "problem" is rc.subr(8)'s getopts;
code flow looks like this:

* '/etc/rc.d/unbound [-dqf] start'
  
  1) dot-sources rc.subr
  2) rc.subr runs getopts;  iff flags are used, OPTIND is >1
  3) runs rc_*() which may use getopts;  if OPTIND is >1,
     daemon_flags is parsed improperly.

* 'rcctl start unbound'

  rcctl(8) has its own getopts, but runs rc.subr as separate
  process and thus does not effect it.

* netstart

  dot-sources rc.subr, but with FUNCS_ONLY=1, i.e. rc.subr
  exits after function definitions and before getopts usage
  and thus does not effect it.


We can simply reset getopts in in 2) instead of 3) such that
any service script will work properly. 

Feedback? OK?

Test:
	# mv /var/unbound/etc/{unbound,foo}.conf
	# rcctl enable unbound
	# rcctl set unbound flags -c /var/unbound/etc/foo.conf -p

  yesterday's -current, i.e. no OPTIND=1 anywhere:

	# /etc/rc.d/unbound -f restart; pgrep unbound; echo $?
	unbound(failed)
	1

  -current or the diff below (no difference):

	# /etc/rc.d/unbound -f restart; pgrep -fl unbound
	unbound(ok)
	84833 /usr/sbin/unbound -c /var/unbound/etc/foo.conf -p

Index: rc.subr
===================================================================
RCS file: /cvs/src/etc/rc.d/rc.subr,v
diff -u -p -r1.166 rc.subr
--- rc.subr	16 Aug 2025 10:23:45 -0000	1.166
+++ rc.subr	29 Nov 2025 17:59:19 -0000
@@ -347,6 +347,7 @@ while getopts "dfq" c; do
 	esac
 done
 shift $((OPTIND-1))
+OPTIND=1
 [[ -n ${_RC_DEBUG} && -n ${_RC_QUIET} ]] && _rc_usage
 
 _RC_RUNDIR=/var/run/rc.d
Index: spamlogd
===================================================================
RCS file: /cvs/src/etc/rc.d/spamlogd,v
diff -u -p -r1.8 spamlogd
--- spamlogd	29 Nov 2025 10:44:46 -0000	1.8
+++ spamlogd	29 Nov 2025 18:11:37 -0000
@@ -9,7 +9,7 @@ daemon="/usr/libexec/spamlogd"
 rc_reload=NO
 
 rc_pre() {
-	local _opt pflog=pflog0 OPTIND=1
+	local _opt pflog=pflog0
 
 	while getopts :l: _opt $daemon_flags; do
 		[[ $_opt == l ]] && pflog=$OPTARG
Index: unbound
===================================================================
RCS file: /cvs/src/etc/rc.d/unbound,v
diff -u -p -r1.11 unbound
--- unbound	29 Nov 2025 10:44:46 -0000	1.11
+++ unbound	29 Nov 2025 17:59:35 -0000
@@ -8,7 +8,7 @@ daemon_flags="-c /var/unbound/etc/unboun
 . /etc/rc.d/rc.subr
 
 rc_pre() {
-	local _anchor _config _opt OPTIND=1
+	local _anchor _config _opt
 
 	while getopts :c: _opt $daemon_flags; do
 		[[ $_opt == c ]] && _config=$OPTARG