[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Re: init script config files



Martin Bialasinski wrote:
> 
> Christopher> The huge controversy erupted because my initial thought
> Christopher> was two config.d files.  One would have all the variables
> Christopher> with gratuitous comments, the other would be user
> Christopher> overrides.
> 
> This is something I also don't feel comfortable with. Let's see if I
> understand this correctly.
> 
> /etc/init.d/foo would get the maintainers default for the "timeout"
> value from the foo.systemdefaults file, and the user can override it
> in the foo.useroverrides file.

That was my initial thought, yes.  The reasoning was simply to be used
as an example.  That is, foo.systemdefaults would include lots of
comments before each variable, show exactly what the variable is set at,
and enumerate all possible variables.  This could get lengthy, I
thought, and it would be easier to change, if the user could see
everything in one place, and at the same time be able to use that as a
template to create their own file.  It also has the effect that if it
gets lengthy, it doesn't take up a bunch of space in the initscript,
which just seems cleaner to me.  It could, however, be confusing, as
user files would be optional in that case, and the directory would be a
mix of conf and non-conf files.  I personally don't think this is a
problem, but a README file should exist with these config files (I
assume they all go in one directory), just like a README exists in
/etc/init.d.

Part of the reason for two files, is that my support functions will use
variables that aren't really user-configurable.  Right now I've used
several: $NAME, $FORKING, and $XXX where XXX is a kill signal.  If $NAME
is set, then start prints "Starting $NAME..." messages, and XXX prints
$XXX messages.  $FORKING doesn't work.

$NAME and $XXX works like:

NAME=foo
TERM=Stopping $NAME
HUP=Restarting $NAME
USR1=Reloading $NAME databases

These aren't really 'configurable' options and the easiest (most
obvious) way to seperate them would be to put these directly in the
initscript and ``real'' variables into the external config file.

> Christopher> At this point, the script has to do lots of checking, and
> Christopher> maintenance headache to meet goal (3) - consistent error
> Christopher> values.  Then, if these values change (eg, as dictated by
> Christopher> the LSB), it will cause a lot of headache for script
> Christopher> writers, so I created a file (called defaults) that would
> Christopher> ship with sysvinit or some required package, and the
> Christopher> script could simply source that one file and get all the
> Christopher> checking done automatically.
> 
> You have to say what you have in mind with "checking", otherwise, I
> can't follow you.
> 
> Do you mean checking for valid values in the variables? Then I don't
> see how you would automaticaly know the valid interval for a timeout
> variable.

No, of course not.  It does the following checks (taken directly from
the file I posted):

*) Make sure a system config file exists
-) Includes a user config file, if it exists

A failed * entry indicates error; a - does not.

Now, this seemed weak, and insufficient to obtain goal 3: 
"Deterministic error handling by init scripts".  In order to do (3),
every script would have to be coded that way, and any changes to the
codes would be a major headache.  Now, if the initscript's needs are
simple, we can include some functions to make goal (3) simple as well.

Here, start() does the following checks:

*) Ensure daemon is executable
*) Ensure the pid can be written to /var/state/pid/foo

And then does its best to store the pid.  The return status of the
daemon is returned to the initscript (the caller), or the predefined
errorcode returned based on one of the listed failed checks.

The various kill signal functions search for the pid (not written, but
it would be a sequence like the grabbed pid, a predefined pid directory
(I assume something like this already exists in /var/state), using
pidof, and as a last resort ps or maybe killall (is killall an essential
package?).  It checks:

*) Ensure the pid can be found
*) Ensure the signal is delivered

Then, I also included status() as an example of how consistent error
reporting can be used.  It's only check is that it knows about the
status (error) code given it.

> Christopher> If this directory moved,
> 
> What directory? The one containing the user override files?

Yes.  I used /etc/init.d/defaults.d; consensus seems to be that this
should be /etc/config.d.  Whatever directory is chosen, it stands the
possibility of being moved.

> So these are helper scripts a init file could use. Nothing to say
> against this. But for now, they are not of interest.

Ok.  Apologies for rehashing them.  ;-)

> You have a "headers" file like
> 
> OK = 0
> VALUE_OUT_OF_BOUNCE = 1
> VALUE_NOT_NUMERIC = 2
> VALUE_NOT_A_NET_ADDRESS = 3
> VALUE_NOT_AN_EMAIL_ADDRESS = 4
> 
> USEROVERRIDES_DIR = /etc/defaults
> 
> And you have helper functions/programms like
> 
> import_user_overrides("$USEROVERRIDES_DIR/$DAEMON")
> check_interval($timeout, 5, 30)
> check_emailaddress($contact)
> 
> This is also a nice thing, as it helps to cut down unneccassary double
> effords and can help with quality.

:-\

This is actually a lot more ambitious than what I was thinking.

Every function I wrote I described above.  The rest of the script
(somewhat abbreviated) is:

----------
Success=0
UsageError=-1                   # No config specified
NotExecutable=1000              # Removed or chmod -x'd
ReadonlyFilesystem=1001         # Cannot store pid
ProgramNotRunning=1002          # Not running or cannot find pid
KillSignalFailed=1003           # Found it, failed to kill it, death
race?

# Save the package name, pass arguments to the defaults file
PACKAGE=$1
shift

# Ensure proper usage
if [ "X$PACKAGE" = "X" ] ; then
        echo "Invalid use of configuration file"
        return $UsageError
fi

# And any user-defined overrides
if [ -r $DEFDIR/$PACKAGE ] ; then
        . $DEFDIR/$PACKAGE $@
fi
----------

Adding variable check_xxx() functions might be useful, but someone
should check and make sure that it would be before this grows out of
control.

> You also want to offer an alternative to the start-stop-daemon. I have
> no problem with this. If it is better, then people will use it. If it
> is not, they won't :-) Simple as that.

That is my attitude.  However, if I can get the status I want from
start-stop-daemon, I will probably use it, as it can handle non-forking
daemons (which I can't) and already manages pid files.

> Christopher> This drew the ire of RedHat haters, and the thread exploded.
> 
> Maybe it was difficult to understand, as you mix conceptual things
> like seperation user defaults from the script and implementation
> details.

I may have been unclear about some things, but I don't think to the
degree that such retribution would have been acted out upon me.  =)

Then again, maybe I've become marketroid  :-{

> I also think that the fear is, that too much would be done in an
> opaque manner in some lib, so you end up with
> 
> start)
>         do_the_magic()

That is a fear of mine as well, and is why I've kept things simple.

> If one makes "speaking" function- and variable names, and leaves the
> execution logic in the script, then it looks OK to me. It is just an
> alternative. A maintainer could do something like that already. And
> indeed, isdnutils does so.
> 
> Any way, I don't maintain a package that has an init script, so...

Nor do I, nor do I have the isdn script handy to look at, but as an
example, if the junkbuster maintainer wanted to rewrite the initscript
using what I've written, this could be it:

----------
#!/bin/sh

# ``required'' for new-style
NAME="filtering proxy server: junkbuster"
TERM="Stopping $NAME"
UID=nobody

# should maybe go in an external defaults conffile

CONFIG=/etc/junkbuster/config

# get nitfy funcs & user overrides
# could just read overrides & omit funcs if preferred
. /etc/init.d/defaults junkbuster

case "$1" in
  start) start /usr/sbin/junkbuster $CONFIG;;
  stop)  term junkbuster;;
  restart|force-reload)
	term junkbuster
	start junkbuster
	;;
  *)
	echo "Usage: $0 {start|stop|restart|force-reload}"
	exit 1
	;;
esac

status $?	# Print 'success' or the error message
exit $?
----------

and .../junkbuster.configvars:

----------
CONFIG=/etc/localsys/junkbuster.config
----------

Now, I added the "UID" variable because junkbuster requires it, and it
somehow works when run as a user even though start-stop-daemon isn't
suid (if it failed if run as a user, a check for root & 'su' could have
been used).  Interesting...

Christopher



Reply to: