Subject: Re: PROPOSAL: /etc/rc, /etc/init.d/*, ...
To: Luke Mewburn <lukem@cs.rmit.edu.au>
From: Nathan J. Williams <nathanw@MIT.EDU>
List: tech-userlevel
Date: 12/01/1999 17:40:15
Luke Mewburn <lukem@cs.rmit.edu.au> writes:

> It has been felt by many people that NetBSD needs a more flexible
> method of system startup scripts. Exactly what model is used to
> improve flexibility is a contentious issue, but we feel that this
> proposal has the benefits of many other systems without the warts,
> whilst retaining backward compatibility for those that require it.

Yay.

> A `traditional' SYSV ``/etc/init.d/'' directory has been suggested,
> but there a various `warts' in that system that can be rectified
> in a new design.

Probably worth expounding on the perceived warts a bit more (such as
the utter lack of compatibility in the various modern SysV
implementations) 

> Scope
> =====
> 
> A new system should have the following attributes:

> 	* The ability to generate /etc/rc and /etc/rc.shutdown
> 	  from `/etc/init.d/*'.

An interesting choice. Is it intended that the generated scripts will
be editable, or should editing be through the original scripts and
re-generation? If the latter, it's probably worth adding some "#
GENERATED BY ... - DO NOT EDIT" lines to the resulting files.

I also see accidental failure to regenerate rc/rc.shutdown as a pretty
annoying misfeature, so a comment or even echo command that says
"Generated at Wed Dec 1 16:49:25 EST 1999" could be useful.


> a) Extra functions in /etc/rc.subr

> 	check_process procname
> 		Ensures that a process (or processes) named procname
> 		is running. Prints a list of matching pids.

This is nigh-impossible to get totally right. It should probably deal
with paged-out processes that show up as (process), and the
documentation should mention problems with setproctitle()-happy
daemons (sendmail, for example) and user-named processes unrelated to
the actual service (it'd be an annoying DoS attack if I could just run
some programs that happened to be named "portmap" and "mountd" that
kept the sysadmin from restarting those). 

Checking the uid might be a win here. 

> d) /etc/rc.shutdown.sh
> 
> 	Similar to /etc/rc.sh, but reverses the order of the output
> 	of `rcorder /etc/init.d/*`, and calls each script with `stop'.

This makes me nervous on a few levels. First, I'm not sure that
backwards order is really right, although a counterexample does not
immediately leap to mind. Second, some of the shutdown steps, while
the opposite of the startup steps, are far from necessary (cf. quota).
Finally, it depends (even more than the startup stuff does) on "foo
stop" actually stopping the service and not continuing until it's
dead. I guess for cases like the classic "Shut down the database
before unmounting the disks" case, the stop script should be smart
enough to wait until it's really gone, not just exiting after sending
the TERM signal.

> e) /etc/rc.sysv.sh
> 
> 	A script which effectively does:
> 		for i in /etc/rc3./S*; do
> 			$i start
> 		done
> 
> 	This is provided for users who like the `Sxx' and `Kxx' style
> 	names.

Ew. Can we get a quick show of hands from people who actually want
this? I don't object, generally, to having tools to do things in
different ways, but I don't want us to really support this, either.

> f) /sbin/mkrc
> 
> 	Depending upon the options, mkrc generates /etc/rc, /etc/rc.shutdown,
> 	/etc/rc0.d and /etc/rc3.d from /etc/init.d/*.
> 
> 	The generated /etc/rc{,.shutdown} is rather readable sh script
> 	(especially for machine generated scripts!)
> 
> 	/etc/rc0.d will contain symlinks from KxxFOO -> ../init.d/FOO.
> 	/etc/rc3.d will contain symlinks from SxxFOO -> ../init.d/FOO.
> 

> g) /etc/netstart
> 
> 	A wrapper for /etc/init.d/network and /etc/init.d/ppp

Purely for historical compatibility for people who ran it directly? It
doesn't seem to be generated; would it make more sense for it to run
network and ppp, rather than including them? More potential
consistency problems otherwise.


> This is one of the more complicated startup scripts. It uses a
> different rc.conf variable than the daemon name, it depends upon
> /etc/exports, a `pre command' operation is run before the actual
> daemon if all the startup conditions are met.

This highlights a weakness in our current setup that the proposed
scheme does not improve on (not that it necessarily should, especially
in an early pass), which is handling failures. Execution based on
rcorder output, without preserving the dependencies and keeping track
of what has been provided and is required, does not give us the
ability to, for example, avoid starting nfsd if mountd failed (perhaps
/etc/exports was toasted in an unclean crash).

This is much more an area for future exploration than a deficiency of
the proposed scheme. 


> Feedback
> ========
> 
> Thoughts/comments?


* There should be well-documented names that scripts should REQUIRE
  (network, daemon, and login are the obvious candidates) for reasonable
  operation that does not depend on the exact set of services
  run. Perhaps those documented points should always be dummies......


> 	* Optional checks that a process isn't running before
> 	  starting, is running before stopping, etc...

> 	* Separate scripts for each function, which support
> 	  `start' and `stop' type functionality.

> 		    a) prior art (cf. SYSV), and people are used to running
> 			/etc/init.d/foo start

* It's not clear whether the purpose of each of the /etc/init.d
  scripts for a service is "perform boot time/shutdown time operation"
  or "start and stop". The implementation looks like the former, since
  all invocations of /etc/init.d/foo {start,stop} call checkyesno();
  but those two bits of the description cover both cases (and it seems
  that the check-before-starting is the default, rather than an
  option).


* Minor bug in rc.subr's check_process() function:

check_process()
{
	_procname=$1
	if [ -z "$_procname" ]; then
		err 3 'USAGE: check_process procname'
	fi
	_procnamept=`basename $_procname`:

That trailing : gets stuck to _procnamept and hence the process
doesn't get found.


* rcorder-related stuff:

I note that the dependency of foo on bar can be expressed by 
"# REQUIRES: bar" in foo or "# BEFORE: foo" in bar. I see the impulse of
generality that led here, but it's not clear which is appropriate for
what cases, and there is one place in the sample where both are used:
local is "# BEFORE: login" and login is "# REQUIRES: local"

Oh, and rcorder(8) mentions but doesn't actually describe BEFORE.


* Documentation is key (but you knew that). If you like I can do some
  writing. 

Thanks for working on this!

        - Nathan