How do I deal with a stale pidfile after a reboot? I'm thinking I can't trust the number in the file, since another process may have taken its place with the same PID.
Can/should I ensure pidfiles don't survive a reboot? What's the proper way of detecting a stale pidfile, that may or may not be left over from a previous boot, when a process with that pid is running?
Often, pidfiles are written into /var/run/ or /run/ (on many systems there is a symlink from /var/run to /run so they are the same). See the Filesystem Hierarchy Standard for more. And that /run/ directory is supposed to be cleared early at boot-time (i.e. because it is mounted as tmpfs), so it won't survive a reboot. See also the Linux Standard Base 4.1 specification.
Hence you should not care about stale pidfiles. This should not happen, and if it does it probably is because the sysadmin messed something up badly. I would just exit with some kind of an error message if that pidfile already exists.
If this is your own daemon, you can detect a stale PID file by scanning /proc/PID/cmdine or by sending a signal to process with that PID.
For a simple example of scanning /proc I show my solution:
#define PROC_BASE "/proc"
#include <stdio.h> // printf, fopen, ...
#include <unistd.h> // getpid
#include <stdio.h> // perror
#include <sys/types.h> // opendir
#include <dirent.h> // opendir
#include <sys/stat.h> // stat
#include <fcntl.h> // fcntl
#include <stdlib.h> // exit
#include <string.h> // memset
/**
* read process name from /proc/PID/cmdline
* @param pid - PID of interesting process
* @return filename or NULL if not found
* don't use this function twice for different names without copying
* its returning by strdup, because `name` contains in static array
*/
char *readname(pid_t pid){
static char name[256];
char *pp = name, byte, path[256];
FILE *file;
int cntr = 0;
size_t sz;
snprintf (path, 255, PROC_BASE "/%d/cmdline", pid);
file = fopen(path, "r");
if(!file) return NULL; // there's no such file
do{ // read basename
sz = fread(&byte, 1, 1, file);
if(sz != 1) break;
if(byte != '/') *pp++ = byte;
else{
pp = name;
cntr = 0;
}
}while(byte && cntr++ < 255);
name[cntr] = 0;
fclose(file);
return name;
}
void iffound_default(pid_t pid){
fprintf(stderr, "\nFound running process (pid=%d), exit.\n", pid);
exit(0);
}
/**
* check wether there is a same running process
* exit if there is a running process or error
* Checking have 3 steps:
* 1) lock executable file
* 2) check pidfile (if you run a copy?)
* 3) check /proc for executables with the same name (no/wrong pidfile)
* @param argv - argument of main() or NULL for non-locking, call this function before getopt()
* @param pidfilename - name of pidfile or NULL if none
* @param iffound - action to run if file found or NULL for exit(0)
*/
void check4running(char **argv, char *pidfilename, void (*iffound)(pid_t pid)){
DIR *dir;
FILE *pidfile, *fself;
struct dirent *de;
struct stat s_buf;
pid_t pid = 0, self;
struct flock fl;
char *name, *myname;
if(!iffound) iffound = iffound_default;
if(argv){ // block self
fself = fopen(argv[0], "r"); // open self binary to lock
memset(&fl, 0, sizeof(struct flock));
fl.l_type = F_WRLCK;
if(fcntl(fileno(fself), F_GETLK, &fl) == -1){ // check locking
perror("fcntl");
exit(1);
}
if(fl.l_type != F_UNLCK){ // file is locking - exit
printf("Found locker, PID = %d!\n", fl.l_pid);
exit(1);
}
fl.l_type = F_RDLCK;
if(fcntl(fileno(fself), F_SETLKW, &fl) == -1){
perror("fcntl");
exit(1);
}
}
self = getpid(); // get self PID
if(!(dir = opendir(PROC_BASE))){ // open /proc directory
perror(PROC_BASE);
exit(1);
}
if(!(name = readname(self))){ // error reading self name
perror("Can't read self name");
exit(1);
}
myname = strdup(name);
if(pidfilename && stat(pidfilename, &s_buf) == 0){ // pidfile exists
pidfile = fopen(pidfilename, "r");
if(pidfile){
fscanf(pidfile, "%d", &pid); // read PID of (possibly) running process
fclose(pidfile);
if((name = readname(pid)) && strncmp(name, myname, 255) == 0)
iffound(pid);
}
}
// There is no pidfile or it consists a wrong record
while((de = readdir(dir))){ // scan /proc
if(!(pid = (pid_t)atoi(de->d_name)) || pid == self) // pass non-PID files and self
continue;
if((name = readname(pid)) && strncmp(name, myname, 255) == 0)
iffound(pid);
}
closedir(dir);
if(pidfilename){
pidfile = fopen(pidfilename, "w");
fprintf(pidfile, "%d\n", self); // write self PID to pidfile
fclose(pidfile);
}
}
If you run function check4running, you would be able to guarantee that your daemon is single: first blocking is locking of self executable, if this isn't possible, it scan /proc/ and check pidfile (if present).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With