Add PID file support
This commit is contained in:
parent
92606e58e2
commit
529c50212b
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
*/
|
Loading…
Reference in New Issue