summaryrefslogtreecommitdiff
path: root/src/pidfile.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/pidfile.c')
-rw-r--r--src/pidfile.c245
1 files changed, 245 insertions, 0 deletions
diff --git a/src/pidfile.c b/src/pidfile.c
new file mode 100644
index 0000000..07f7521
--- /dev/null
+++ b/src/pidfile.c
@@ -0,0 +1,245 @@
+#include "port/stdio.h" /* for snprintf */
+#include "port/string.h" /* for strdup, strcspn */
+#include "port/stdbool.h" /* for bool */
+#include "port/unistd.h" /* for getpid, open, write, read,
+ * unlink, lockf */
+
+#include "pidfile.h"
+#include "log.h"
+#include "errors.h"
+
+#include <stdlib.h> /* for strtol */
+#include <errno.h> /* for errno */
+#include <sys/types.h> /* for pid_t, ssize_t, off_t */
+#include <sys/stat.h> /* for umask */
+#include <fcntl.h> /* for O_RDWR */
+#include <signal.h> /* for kill */
+
+/* abstraction of the pid file handling in order to unclutter the
+ * daemon start and stop function
+ */
+
+#define VAR_RUN_DIR "/var/run"
+
+void pidfile_init( struct pidfile_t *pidfile ) {
+ pidfile->filename[0] = '\0';
+ pidfile->fd = -1;
+ pidfile->locked = false;
+ pidfile->running = false;
+}
+
+void pidfile_set_from_daemon_name( struct pidfile_t *pidfile, const char *daemon ) {
+ pidfile_init( pidfile );
+ snprintf( pidfile->filename, PATH_MAX, "%s/%s.pid", VAR_RUN_DIR, daemon );
+}
+
+void pidfile_set_from_filename( struct pidfile_t *pidfile, const char *filename ) {
+ pidfile_init( pidfile );
+ /* make sure the filename is shorter than the POSIX.1 length */
+ snprintf( pidfile->filename, PATH_MAX, "%s", filename );
+}
+
+bool is_daemon_running( struct pidfile_t *pidfile, pid_t *pid, error_t *error ) {
+ int res;
+ ssize_t bytes_read;
+ char buf[256];
+ char *end_ptr;
+
+ /* assume daemon is not running */
+ pidfile->running = false;
+
+ /* open pidfile with correct permissions */
+ pidfile->fd = open( pidfile->filename, O_RDWR, 0644 );
+ if( pidfile->fd < 0 ) {
+ if( errno == ENOENT ) {
+ /* this is good, pid file doesn't exist at all */
+ LOG( LOG_DEBUG, "No pidfile '%s' found, daemon is not running", pidfile->filename );
+ (void)close( pidfile->fd );
+ *error = OK;
+ return pidfile->running;
+ } else {
+ LOG( LOG_EMERG, "Unable to open pidfile '%s' for reading: %s", pidfile->filename, strerror( errno ) );
+ *error = ERR_INTERNAL;
+ return pidfile->running;
+ }
+ }
+
+ /* try to lock the pid file (non-blocking) */
+ res = lockf( pidfile->fd, F_TLOCK, (off_t)0 );
+ if( res < 0 ) {
+ if( errno == EAGAIN ) {
+ /* another process locks the file already */
+ LOG( LOG_DEBUG, "Another process locks the pidfile, daemon already running" );
+ *error = OK;
+ pidfile->locked = true;
+ pidfile->running = true;
+ } else {
+ LOG( LOG_EMERG, "Unable to lock pidfile '%s': %s", pidfile->filename, strerror( errno ) );
+ (void)close( pidfile->fd );
+ *error = ERR_INTERNAL;
+ pidfile->running = false;
+ return pidfile->running;
+ }
+ } else {
+ pidfile->locked = false;
+ }
+
+ /* try to read the pid from the file */
+ bytes_read = read( pidfile->fd, buf, sizeof( buf ) - 1 );
+ if( bytes_read < 0 ) {
+ LOG( LOG_EMERG, "Unable to read pid from pidfile '%s': %s", pidfile->filename, strerror( errno ) );
+ (void)close( pidfile->fd );
+ *error = ERR_INTERNAL;
+ return pidfile->running;
+ }
+
+ /* parse the file and see if we have a valid pid on the first line */
+ buf[bytes_read] = 0;
+ buf[strcspn( buf, "\n" )] = 0;
+
+ errno = 0;
+ *pid = (pid_t)strtol( buf, &end_ptr, 10 );
+ if( ( errno != 0 ) /* ERANGE or valid base */ ||
+ ( end_ptr == NULL ) /* pre-condition for check for '\0'! */ ||
+ ( end_ptr - buf < 1 ) /* too short */ ||
+ ( *pid < 2 ) /* too small value */ ) {
+ LOG( LOG_EMERG, "pidfile '%s' contains invalid data, can't read PID from it!", pidfile->filename );
+ (void)close( pidfile->fd );
+ *error = ERR_INTERNAL;
+ return pidfile->running;
+ }
+ LOG( LOG_DEBUG, "Found PID '%lu' in pidfile", *pid );
+
+ /* the pid is valid, but is there a process with the pid actually running?
+ * (this handles the case of kill -9!)
+ */
+ res = kill( *pid, 0 );
+ if( res < 0 ) {
+ if( errno == ESRCH ) {
+ /* this is fine, process doesn't exist with this PID */
+ LOG( LOG_EMERG, "Found PID '%lu' in pidfile '%s', but no such process is running. Check and manually delete the pidfile!", *pid, pidfile->filename );
+ (void)close( pidfile->fd );
+ *error = ERR_INTERNAL;
+ return pidfile->running;
+ } else {
+ LOG( LOG_EMERG, "Can't check if processor with PID '%lu' is alive: %s", *pid, strerror( errno ) );
+ (void)close( pidfile->fd );
+ *error = ERR_INTERNAL;
+ return pidfile->running;
+ }
+ }
+ LOG( LOG_DEBUG, "A process with PID '%lu' is already running", *pid );
+
+ /* process successfuly signaled, so a process with this PID exists
+ * (worst case, we assume, it's the daemon)
+ */
+ (void)close( pidfile->fd );
+ *error = OK;
+ return pidfile->running;
+}
+
+error_t pidfile_create( struct pidfile_t *pidfile ) {
+ int res;
+ char pid_string[20];
+ ssize_t bytes_writen;
+
+ /* create or open pid file with correct permissions */
+ pidfile->fd = open( pidfile->filename, O_CREAT | O_WRONLY | O_EXCL, 0644 );
+ if( pidfile->fd < 0 ) {
+ LOG( LOG_EMERG, "Unable to open pidfile '%s' for writing: %s", pidfile->filename, strerror( errno ) );
+ return ERR_INTERNAL;
+ }
+
+ /* Try to lock the pid file (non-blocking) */
+ res = lockf( pidfile->fd, F_TLOCK, (off_t)0 );
+ if( res < 0 ) {
+ if( errno == EAGAIN ) {
+ /* another process locks the file already, this should not happen, maybe a
+ * race between to daemons started in parallel?
+ */
+ LOG( LOG_EMERG, "Unable to lock pidfile '%s' after creation, daemon started in parallel?", pidfile->filename );
+ (void)close( pidfile->fd );
+ (void)unlink( pidfile->filename );
+ return ERR_INVALID_STATE;
+ } else {
+ LOG( LOG_EMERG, "Unable to lock pidfile '%s' after creation: %s", pidfile->filename, strerror( errno ) );
+ (void)close( pidfile->fd );
+ (void)unlink( pidfile->filename );
+ return ERR_INTERNAL;
+ }
+ }
+
+ /* Truncate the contents of the file, so we don't get funny values
+ * in the pid file
+ */
+ if( ftruncate( pidfile->fd, (off_t)0 ) < 0 ) {
+ LOG( LOG_EMERG, "Unable to truncate the pidfile '%s' before writing to it", pidfile->filename, strerror( errno ) );
+ (void)close( pidfile->fd );
+ (void)unlink( pidfile->filename );
+ return ERR_INTERNAL;
+ }
+
+ /* We remember the pid in the file for init scripts which rely on the pid
+ * to be stored here.
+ */
+ snprintf( pid_string, 20, "%lu\n", (unsigned long)getpid( ) );
+ bytes_writen = write( pidfile->fd, pid_string, strlen( pid_string ) );
+ if( bytes_writen < 0 ) {
+ LOG( LOG_EMERG, "Unable to write PID into the pidfile '%s': %s", pidfile->filename, strerror( errno ) );
+ (void)close( pidfile->fd );
+ (void)unlink( pidfile->filename );
+ return ERR_INTERNAL;
+ } else if( bytes_writen != (ssize_t)strlen( pid_string ) ) {
+ /* non-atomic write on files with so little data, strange, should never happen! */
+ LOG( LOG_EMERG, "Non-atomic write failed when storing the PID into the pidfile '%s'", pidfile->filename );
+ }
+ LOG( LOG_DEBUG, "Stored '%lu' into the pidfile '%s' and locked it", (unsigned long)getpid( ), pidfile->filename );
+
+ pidfile->locked = true;
+
+ return OK;
+}
+
+error_t pidfile_release( struct pidfile_t *pidfile ) {
+ error_t error = OK;
+
+ LOG( LOG_DEBUG, "Releasing (unlocking/closing) pidfile '%s' (fd: %d, locked: %d)",
+ pidfile->filename, pidfile->fd, pidfile->locked );
+
+ if( pidfile->locked ) {
+ if( lockf( pidfile->fd, F_ULOCK, (off_t)0 ) < 0 ) {
+ LOG( LOG_ALERT, "Unable to unlock the pidfile '%s': %s (%d)",
+ pidfile->filename, strerror( errno ), errno );
+ error = ERR_INTERNAL;
+ }
+ }
+
+ if( pidfile->fd >= 0 ) {
+ if( close( pidfile->fd ) < 0 ) {
+ LOG( LOG_ALERT, "Unable to close the pidfile '%s': %s (%d)",
+ pidfile->filename, strerror( errno ), errno );
+ error = ERR_INTERNAL;
+ }
+ pidfile->fd = -1;
+ }
+
+ return error;
+}
+
+error_t pidfile_remove( struct pidfile_t *pidfile ) {
+ error_t error = OK;
+
+ LOG( LOG_DEBUG, "Removing pidfile '%s' (fd: %d, locked: %d, running: %d)",
+ pidfile->filename, pidfile->fd, pidfile->locked, pidfile->running );
+
+
+ if( !pidfile->running && pidfile->fd != -1 ) {
+ if( unlink( pidfile->filename ) < 0 ) {
+ LOG( LOG_ALERT, "Unable to remove the pidfile '%s': %s (%d)",
+ pidfile->filename, strerror( errno ), errno );
+ error = ERR_INTERNAL;
+ }
+ }
+
+ return error;
+}