5

In my linux startup scripts, when I start a process like (openvpn used for this example, but the question is general to any process);

openvpn --config /etc/myserver.conf

what is the best way to find it and be 100% certain it's the right process and kill it in the stop section? I normally use stuff like:

pid=$(ps -efww | grep -v grep | grep openvpn | grep /etc/myserver.conf | awk '{print $2}')

Sure it works nearly all of the time, but sometimes there are issues with accidentally matching processes with nearly the same name (e.g. myserver.conf-new), so I'm looking for a better approach.

  • Some processes have a way to store the pid somewhere, that's fine, but generally I'm skeptical of killing processes solely based on a pid sitting in a file somewhere.
  • Solaris has projects, it's not all roses in my limited experience because you have to set up /etc/projects first, but it does make it easy to tag and later find processes.
  • Maybe using the environment, like setting an environment variable like (MYID=myserver) and then looking for it with ps e -ef | grep MYID=myserver? Still may suffer the same problem with accidental matching.

I wish there were something easy like:

launch --tag myserver openvpn --config /etc/myserver.conf

and

pgrep --tag myserver
Steve Kehlet
  • 1,055
  • 1
  • 10
  • 16

4 Answers4

7

Thank you @Iain, @KyleSmith, and @M_1 for your answers, and for helping me get started on Server Fault. If I had more rep here I'd +1 you guys. (Edit: now I have rep, +1s all around).

I'm going to answer my own question because I've found something that does what I was looking for: a general solution that avoids imprecise pattern matching with ps, and doesn't use pid files. It's completely subjective that this would be the "best" way because there's obviously a long and successful history in unix of using pid files, however this was something I explicitly said I didn't like for various reasons, and those are: they can be tricky to create properly, different for every piece of software, done differently on every distro, can be stale/overwritten, and inherently don't necessarily represent what's actually going on. I'd rather use some kind of process tagging, ask the kernel and get real answers.

Trimmed down example:

#!/bin/sh
_TAG=d726cc7fa57a308afdc057b228a13f6d
case "$1" in
start)
  _TAG=$_TAG ./self-backgrounding-process
  _TAG=$_TAG ./non-self-backgrounding-process &
  ;;
stop)
  pids=$(grep -l "\b_TAG=$_TAG\b" /proc/*/environ | cut -d/ -f3)
  [ -n "$pids" ] && kill $pids
  ;;
esac

The key points are:

  • using an md5sum (something very unlikely to be accidentally matched) for the tag
  • using a different tag in each service's startup script
  • querying /proc/*/environ to get an accurate list of running/relevant/tagged processes
  • using grep with \b to match word boundaries to ensure an exact match

I'm not sure I like polluting the environment, but I don't know of any other way (e.g. Solaris projects) to tag a linux process in an arbitrary way that I can ask the kernel for later. At least, /proc/<pid>/environ appears to reflect the environment at startup, and not be affected by any changes the process might make after, which suggests this should be reliable, however, there's the chance this could change unexpectedly. This may or may not work outside Linux, depending on the OS's /proc and grep implementation.

I think I'll give it a try for a while and see how it goes.

Steve Kehlet
  • 1,055
  • 1
  • 10
  • 16
  • One thing that occurred to me later is this technique doesn't find processes started by hand. Not a big deal maybe, but not good either. – Steve Kehlet Jul 15 '11 at 17:50
3

Don't be afraid of pidfiles, of they're tried and true and typically owned by root! :)

Most distributions use a standard function or binary to launch daemons and store the resultant PID in a file. In Debian, for example, you have start-stop-daemon with the --pidfile option. Other distributions have /etc/rc.d/init.d/functions (or similiar) that are used for starting daemons. Check some of the more generic startup scripts included with your distribution.

Kyle Smith
  • 9,563
  • 1
  • 30
  • 32
  • You're probably right on the point don't be afraid of pidfiles. I'll check out my system startup script libs for some more ideas. Thanks. – Steve Kehlet May 26 '11 at 18:58
2

A nice idea would be to use a simple bash wrapper script that writes a lock/pid file of that application (commonly used in webservers or databases) and then use that file to kill exactly this one process if needed.

M_1
  • 363
  • 2
  • 10
  • I like this idea, but sometimes the programs I call fork themselves into the background, so it's tough to keep track of the right pid. – Steve Kehlet May 26 '11 at 18:59
2

For openvpn in particular you can use the --writepid /path/to/file command line option e.g.

/usr/sbin/openvpn --writepid /var/run/openvpn/server.pid  --config /etc/myserver.conf ...

Which will write the PID of the openvpn main process to the named file.

In general you can wrap your program in a small script and use the bash $! builtin variable to write the pid to a file

#!/bin/bash
yourcommand &
echo $! >/path/to/pid.file
user9517
  • 114,104
  • 20
  • 206
  • 289
  • Good point on --writepid for openvppn, I may use that for openvpn in particular. I'll have to find another program to use as an example, but openvpn forks into the background, so if I launch it with & ($! only appears to work when you background something), then you end up with the wrong pid. – Steve Kehlet May 26 '11 at 18:58
  • @SteveKehlet: Having something write a PID file is generally the way to do this. Most daemons will write a PID file so you don't need to wrap them you just need to know (or tell) where the PID file is. You only need to write a wrapper for something that doesn't and can't be made to create it's own PID file. Beyond that everything becomes a special case. – user9517 May 26 '11 at 19:09
  • Good point. I may think I have an unusually high number of special cases, but I shouldn't forget that some of them are actually not. Thank you for your answers Iain. – Steve Kehlet May 26 '11 at 19:41