Add PID file support

This commit is contained in:
Laurence Withers 2008-07-26 16:11:34 +00:00
parent 92606e58e2
commit 529c50212b
3 changed files with 230 additions and 0 deletions

View File

@ -6,11 +6,16 @@
*/
/* Below are all the includes used throughout the application. */
#include <time.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <getopt.h>
#include <limits.h>
#include <signal.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>

View File

@ -26,11 +26,20 @@ ssize_t safe_write_fixed(int fd, const char* buf, size_t amt);
/* safe_sleep_fixed()
* Sleeps for a fixed period of seconds and nanoseconds. Restarts sleep with correct time if
* interrupted by signal delivery.
*/
void safe_sleep_fixed(int s, int ns);
/* IMPLEMENTATION ********************************************************************************/
/* safe_write_fixed()
* Copied from libgslutil (GPL-3).
*/
ssize_t safe_write_fixed(int fd, const char* buf, size_t amt) __attribute__((nonnull));
ssize_t safe_write_fixed(int fd, const char* buf, size_t amt)
@ -54,6 +63,28 @@ ssize_t safe_write_fixed(int fd, const char* buf, size_t amt)
/* safe_sleep_fixed()
* Adapted from libgslutil (GPL-3).
*/
void safe_sleep_fixed(int s, int ns)
{
struct timespec req, rem;
req.tv_sec = s;
req.tv_nsec = ns;
restart:
if(nanosleep(&req, &rem) == -1) {
if(errno == EINTR) {
memcpy(&req, &rem, sizeof(struct timespec));
goto restart;
}
return;
}
}
/* options for text editors
kate: replace-trailing-space-save true; space-indent true; tab-width 4;
vim: expandtab:ts=4:sw=4

194
src/daemonitor/102_pid.c Normal file
View File

@ -0,0 +1,194 @@
/* daemonitor/src/daemonitor/102_pid.c
*
* (c)2007, Laurence Withers, <l@lwithers.me.uk>.
* Released under the GNU GPLv3. See file COPYING or
* http://www.gnu.org/copyleft/gpl.html for details.
*/
/* PID FILE ROUTINES ******************************************************************************
*
* This file contains routines for writing and removing PID files. A PID file may be created by
* daemonitor if it is requested. The PID file contains the PID of the daemonitor process, so that
* openrc etc. does not detect crashed services and allow them to be restarted, as daemonitor
* itself will restart them. This also allows the PID file to be used to send a termination signal
* to the daemonitor process.
*/
/* PUBLIC API ************************************************************************************/
/* pidfile_set_filename()
* Sets the filename of the PID file. A copy of the data is made.
*/
void pidfile_set_filename(const char* filename);
/* pidfile_write()
* Writes a PID file, logging errors to syslog. If an existing PID file containing a valid PID
* of a process whose /proc/<PID>/exe matches /proc/self/exe is found, then that process is first
* killed with a SIGTERM and then a SIGKILL. On success, 0 is returned. On error, a log message is
* written, and -1 is returned. On success, an atexit() handler which unlinks the PID file is
* registered.
*/
int pidfile_write(void);
/* IMPLEMENTATION ********************************************************************************/
/* pidfile_filename
* The filename of the PID file. This is copied with strdup(), because the original may have been
* an argument to main(), and may not be valid in the atexit() handler.
*/
char* pidfile_filename;
/* pidfile_set_filename()
* Sets the PID file filename. On out of memory error, logs to syslog and aborts the process.
*/
void pidfile_set_filename(const char* filename) __attribute__((nonnull));
void pidfile_set_filename(const char* filename)
{
pidfile_filename = strdup(filename);
if(!pidfile_filename) {
LOG(LOG_CRIT, "Out of memory allocating %lu bytes.",
(unsigned long)strlen(filename));
exit(1);
}
}
/* pidfile_unlink()
* Unlinks the PID file. Called from atexit().
*/
void pidfile_unlink(void)
{
if(unlink(pidfile_filename)) {
LOG(LOG_ERR, "Error removing PID file `%s' (%m).", pidfile_filename);
}
}
/* pidfile_write()
* Kills any existing PID file. Writes a new PID file. Returns 0 on success, -1 on error.
*/
int pidfile_write(void)
{
char buf[40], * endp, link1[PATH_MAX + 1], link2[PATH_MAX + 1];
int fd, i;
ssize_t ret;
unsigned long pid = 0;
/* check for an existing PID file */
fd = open(pidfile_filename, O_RDONLY);
if(fd != -1) {
ret = read(fd, buf, sizeof(buf) - 20);
if(ret == -1) {
LOG(LOG_WARNING, "Error reading from existing PID file `%s' (%m).",
pidfile_filename);
} else {
buf[ret] = 0;
pid = strtoul(buf, &endp, 10);
if(*endp && !isspace(*endp)) {
LOG(LOG_WARNING, "Invalid value `%s' in existing PID file `%s' (%m).",
buf, pidfile_filename);
pid = 0;
}
}
TEMP_FAILURE_RETRY( close(fd) );
}
/* check if there is an existing process matching /proc/self/exe with given PID */
if(!pid) goto write_pidfile;
ret = readlink("/proc/self/exe", link1, sizeof(link1) - 1);
if(ret == -1) {
LOG(LOG_WARNING, "Couldn't read /proc/self/exe (%m).");
goto write_pidfile;
}
link1[ret] = 0;
snprintf(buf, sizeof(buf), "/proc/%lu/exe", pid);
ret = readlink(buf, link2, sizeof(link2) - 1);
if(ret == -1) {
LOG(LOG_WARNING, "Stale PID file `%s' (PID %lu), removing it.",
pidfile_filename, pid);
goto write_pidfile;
}
link2[ret] = 0;
if(strcmp(link1, link2)) {
LOG(LOG_WARNING, "Stale PID file `%s' (PID %lu), removing it.",
pidfile_filename, pid);
goto write_pidfile;
}
/* send it a sigterm */
LOG(LOG_WARNING, "Found an existing running process with PID %lu, killing it.", pid);
if(kill(pid, SIGTERM)) {
LOG(LOG_ERR, "Couldn't send SIGTERM to PID %lu (%m). Not starting yet.", pid);
return -1; /* come back later */
}
/* check it every 50ms until it's gone or 5s have elapsed */
for(i = 0; i < 100; ++i) {
safe_sleep_fixed(0, 50 * 1000 * 1000);
if(kill(pid, 0) == -1) break;
}
/* possibly send it a sigkill */
if(kill(pid, 0) == 0) {
LOG(LOG_WARNING, "Old process with PID %lu still running. Sending it SIGKILL.", pid);
if(kill(pid, SIGKILL) == -1 && errno != ESRCH) {
LOG(LOG_ERR, "Couldn't send SIGKILL to PID %lu (%m). Not starting yet.", pid);
return -1; /* come back later */
}
}
LOG(LOG_NOTICE, "Old process gone.");
/* actually write the PID file */
write_pidfile:
fd = creat(pidfile_filename, 0666);
if(fd == -1) {
LOG(LOG_ERR, "Couldn't create PID file `%s' (%m).", pidfile_filename);
return -1; /* come back later */
}
snprintf(buf, sizeof(buf), "%lu\n", (unsigned long)getpid());
ret = safe_write_fixed(fd, buf, strlen(buf));
if((size_t)ret != strlen(buf)) {
LOG(LOG_ERR, "Couldn't write to PID file `%s' (%m).", pidfile_filename);
TEMP_FAILURE_RETRY( close(fd) );
return -1; /* come back later */
}
if( TEMP_FAILURE_RETRY( close(fd) ) ) {
LOG(LOG_ERR, "Couldn't write to PID file `%s' (%m).", pidfile_filename);
return -1; /* come back later */
}
/* success -- log, register handler, return */
LOG(LOG_NOTICE, "PID %s written to `%s'.", buf, pidfile_filename);
atexit(pidfile_unlink);
return 0;
}
/* options for text editors
kate: replace-trailing-space-save true; space-indent true; tab-width 4;
vim: expandtab:ts=4:sw=4
*/