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
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:
- the first grep is used to single out the commands that match the given input.
- 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.
- 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" fiThis 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:
- 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.
- 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!
e.g.: invoke-rc.d apache2 restart
[ Parent | Reply to this comment ]
Your example, using invoke-rc.d is less painful to relate to a newbie than using the path /etc/init.d/apache2 like i used to do, but is still way more likely to fail than using the service command.
If you don't ever have to tell a newbie to type these commands, then yes, "big deal" - the service command is not worth much.
[ Parent | Reply to this comment ]
[ Parent | Reply to this comment ]
${var/%pattern/replacementpattern}
I'm sure someone will correct me if I'm wrong - both bash and sed drive me nuts ;-)
PJ
[ Parent | Reply to this comment ]
[ Send Message | View Steve's Scratchpad | View Weblogs ]
While I don't think I'm the target audience for this article I do have to comment you for submitting it. I really enjoyed reading it. You definitely have a point about explaining things to people - something that I have to do over the phone frequently.
Well done, and thanks.
[ Parent | Reply to this comment ]
[ Parent | Reply to this comment ]
[ Send Message | View Steve's Scratchpad | View Weblogs ]
The title reflects the idiom (??): "Going postal" == "Getting very angry".
See wikipedia for more details.
[ Parent | Reply to this comment ]
I've had a similar script for a long while. It works on older UNIX systems that lack pgrep and bash, and it figures out which type of "ps" program is installed. However, it will not hide the grep line if you are using a regex that ends in a special character:
(from a file sourced by .profile)
[ `uname -s` = SunOS ] && [ -x /usr/ucb/ps ] && alias ps=/usr/ucb/ps
if ps auxww >/dev/null 2>&1 # works in linux + bsd (and solaris w/ ucb's ps)
then alias psax='ps auxww'
else alias psax='ps -ef'
fi
psl() {
local grep=grep start end
type egrep >/dev/null 2>&1 && grep=egrep
psax |head -n1 |GREP_COLOR="1;38" $grep . # bold header
if [ $# = 0 ]
then psax
else
start="${*%[!]?*+.)$]}" # cut off last character if its not special
end="${*#$start}" # get just that last character (if !special)
psax |$grep -i "$start${end:+[$end]}" # search for args if present
fi
}For my /usr/local/bin/service script, I use the following. Note that the return value isn't screwed up by my error message hack. If you don't trust those last two lines, use "$RC" "$@" instead.
#!/bin/sh
RC="/etc/init.d/$1"
case $1 in # if we're approximating redhat, httpd == apache(2)
http*|apache*) [ ! -x "$RC" ] && RC="/etc/init.d/apache2" ;;
esac
if [ ! -x "$RC" ]; then
if [ "$1" = "-list" ] || [ "$1" = "--list" ]; then
cd /etc/init.d
find * -type f -perm -u+x \
|egrep -v '^(skeleton|.*\.dpkg|rc|single|reboot|halt|bootclean)' \
|xargs ls
exit 0
fi
echo "Invalid service: '$1'" >&2
echo "You can get a list of services with '`basename $0` --list'" >&2
echo "Get further help with '`basename $0` SERVICE help'" >&2
exit 1
fi
shift
# here's a clever hack where we safely change the "Usage: ..." statement
exec 3>&1
exit `(("$RC" "$@" 4>&-; echo $? >&4)|sed 's:/etc/init.d/:service :' 1>&3) 4>&1`
[ Parent | Reply to this comment ]
...
else
while [ "${1#-}" != "$1" ]; do # skip over arguments to grep
args="$args $1"
shift
done
start="${*%[!]?*+.)$]}"
end="${*#$start}"
psax |$grep -i $args "$start${end:+[$end]}"
fi
}
[ Parent | Reply to this comment ]