Sysvconfig: How not to go postal over a service

Posted by detertj on Wed 13 Feb 2008 at 15:52

Demons? Etsee in it? Dot-D? Those are just jargon sounds to laymen, but you might recognize them to mean daemons, and /etc/init.d. If you've ever tried to tell a layman to stop, start, or restart a daemon, you know that it's exasperating. It may take several minutes - the layman doesn't know how to spell etc, he doesn't know a forward slash from a backslash, and he doesn't know what you're saying when you say init.d. Lastly, if he is doing this because you need him to (i.e. he didn't ask you, you asked him), then he probably doesn't know what the command line he just typed accomplishes. It may not be necessary for him to know, but if he's curious, he may ask, which just adds to your exasperation and time needed to get the task done.

This is why I love the service command . It is so much easier to tell the layman "Type, 'service postfix stop'", than to tell him to type "/etc/init.d/postfix stop".

He gets it typed correctly, knows what the command is meant to achieve, and, if he's a Microsoft Windows admin, he almost feels at home (because it uses his lingo).

The 1st argument to service is the name of a script in /etc/init.d, and the 2nd argument can typically be any of the arguments that the init script understands (e.g. "start", "restart", "stop", "reload", "status", etc.).

One more benefit of the service command is that, assuming you are using a shell that has tab-completion (such as bash), singling out the daemon you want is easier than using paths to the daemon scripts. The reason why is that there are fewer tab-completion conquests on the way. With service there are parameters. One to identify the service command, and a second to identify the daemon you want. With just paths to the daemon scripts, unless your pwd happens to be /etc or /etc/init.d, there are 3 : On to specify etc, a second to specify init.d, and a 3rd to identify the daemon (plus you have to type the leading /).

Where to get service command

The service command is available in Debian from the sysvconfig package and in Ubuntu from either the debian-helper-scripts package or the sysvconfig package.

If you're running Debian, you probably would not want to bother installing the debian-helper-scripts Ubuntu package, since it's not a part of your normal package repositories.

If you're running Ubuntu, you need to decide which of the two packages you wish to provide the service command. The rest of this article describes the other trinkets that come with each of these packages, and the differences between them (for the sake of Ubuntu users, who will have to make a choice).

The service command

In both packages, service is a Bourne shell script. The sysvconfig version is basically a wrapper around invoke-rc.d (part of the sysv-rc package), but it hard-codes which actions it considers valid to be passed to invoke-rc.d.

Here's a relevant snippet of the sysvconfig service script to illustrate that point:

# Handle two argument cases.
case "$ACTION" in
    stop | start | restart | status | reload | force-stop | force-reload )
        $INVOKERC "$SERVICE" "$ACTION" && exit $?
        ;;
    --full-restart )
        $INVOKERC "$SERVICE" stop || exit $?
        $INVOKERC "$SERVICE" start || exit $?
        exit 0
        ;;
    * )
        echo "${ACTION}: no such action" >&2
        exit 1
        ;;
esac

If a script in /etc/init.d had some other non-conventional action argument, you couldn't use sysvconfig's service command to invoke it. e.g. Suppose you have /etc/init.d/garbage, and it takes cleanup as an action. Then you could not invoke the cleanup action with the sysvconfig version of service.

The debian-helper-script version is less sophisticated, but to my mind, more elegant:

#!/bin/sh

set -e

/etc/init.d/$1 $2 $3

This script can be used to invoke the hypothetical unconventional action cleanup, in the example given above.

Package Dependencies

sysvconfig depends on dialog and sysv-rc, but the debian-helper-scripts package doesn't depend on any other package. (Of course neither is usable without bash).

The other Trinkets

sysvconfig comes with just one other executable - sysvconfig, which is written in perl, and which enables you to view, edit, save, and restore runlevel configurations from a curses menu interface. I've never used it other than to take a quick look, to be able to mention it here.

It seems to me that update-rc.d would be easier to use, but maybe the layman would find sysvconfig easier.

debian-helper-scripts package comes with 5 other trinkets, of which I'll only comment on 2:

psp

This looks pretty useful to me. It gives you the pid, tty, status, time, and commandline for just the process whose name you give it. e.g.

# psp init
    1 ?        Ss     0:01 /sbin/init
#

Many system admins make their own alias or function to do this kind of thing. It makes sense that it should be part of an installable package, since most system admins want/need this functionality. pgrep is close, but the problem with it is that it can only output the pid and the pname.

psp is a simple shell script:

#!/bin/sh

set -e

ps ax | grep $1 | grep -v $0 | grep -v "grep $1"

I'm not impressed with this implementation. It could have been more efficient. There are 4 commands in the pipeline, each with a specific purpose, and each creating a separate process. The purpose of the 3 grep commands is as follows, moving from left to right:

  1. the first grep is used to single out the commands that match the given input.
  2. the second grep is used to filter out the process that is running the psp shell script. It matches because it's single argument is the same as what is being grep'ed in the 1st grep. However, we don't want to know about psp - we only want to know about processes.
  3. the third and last grep is needed to filter out the process that is the second grep in the pipeline.

Consider this as more efficient approach:

#!/bin/sh

set -e

pids=$(pgrep $1)

if [ "${pids}X" != "X" ];then
        ps -p "$pids"
fi

This approach has only two subprocess forks, instead of four. However, it depends on pgrep (part of the procps package.), which is probably not as commonly installed as the grep package. This is not necessarily a bad thing, it just means you need it installed as well, and we all know that less is more.

Another approach, almost as efficient, likely without an extra dependency, but more complex to read, would be to replace the command that gathers the pids with this:

pids=$(ps -e -o pid,comm | sed -n '/'$1'/ s/^ *\([0-9]*\).*/\1/p')

Of course, this approach requires sed be installed, but it is by default on most linux systems.

system-update

This is a simple shell script that goes most of the way towards making your box auotmatically check for, and install, relevant security updates. It requires you to do 2 things before it will happen automatically:

  1. you must enable the job to be scheduled. Installation of it puts a file in /etc/cron.daily that will schedule the script to run on a daily basis, but only after you edit the file, and in so doing, confirm you want it to run as a scheduled job.
  2. you must create the file /etc/apt/sources.list.security, in which you list only the sources that contain security updates.

This seems like a decent attempt to automate getting security updates automatically. On Ubuntu, I use the following script instead, because it doesn't require me to modify or maintain multiple copies of sources.list :

#!/bin/bash

# script to get just the security updates and 'trivial' upgrades

hostName=$(uname -n)
osVersion=$(lsb_release -c -s)
myName=$(basename $0)
tempFile=/tmp/${myName}.$$

if [[ "${osVersion}X" = "X" ]];then
        echo "Unknown osVersion.  Exitting."
        exit 1
fi

trap "rm $tempFile && exit" SIGKILL SIGINT SIGABRT SIGTERM

declare -a logFiles=(/var/log/apt/security.log /var/log/apt/trivial.log)
declare -a aptGetCmd=("apt-get upgrade --assume-yes --target-release $
{osVersion}-security" "apt-get upgrade --trivial-only"
)
declare -a aptLabel=("security update" "trivial update")

apt-get update >& $tempFile || { echo "apt-get update failed as follows:"; cat
$tempFile; }

for (( i=0; i < ${#aptGetCmd[*]}; i++ ));do
        ${aptGetCmd[$i]} >& $tempFile
        if [ $? -ne 0 ];then
                echo "Error : ${aptGetCmd[$i]} :"
                cat $tempFile
        fi

        logFile=${logFiles[$i]}
        { echo -n "Start ${aptLabel[$i]} "; date; cat $tempFile; echo -n "End $
{aptLabel[$i]} "; date; } >> $logFile
done

apt-get autoclean >& /dev/null
rm $tempFile >&/dev/null

exit 0

Conclusion

I hope the service command makes your life easier as it has mine. Enjoy!


This article can be found online at the Debian Administration website at the following bookmarkable URL (along with associated comments):

This article is copyright 2008 detertj - please ask for permission to republish or translate.