/* Copyright (C) 2008 Andreas Baumann This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "port/sys_internal.h" #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 */ #define DEFAULT_TEXT_DOMAIN "libwolf" #include "i18n/gettext.h" /* for i18n */ #include "daemon/pidfile.h" #include "errors.h" #include "log/log.h" #include "log/messages.h" /* for i18n */ #include /* for strtol */ #include /* for errno */ #include /* for pid_t, ssize_t, off_t */ #include /* for umask */ #include /* for O_RDWR */ #include /* 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 *name ) { pidfile_init( pidfile ); snprintf( pidfile->filename, PATH_MAX, "%s/%s.pid", VAR_RUN_DIR, name ); } 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, wolf_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 */ wolf_log( WOLF_LOG_DEBUG, WOLF_CATEGORY_DAEMON, WOLF_MSG_DAEMON_NO_PIDFILE, _( "No pidfile '%s' found, daemon is not running" ), pidfile->filename ); (void)close( pidfile->fd ); *error = WOLF_OK; return( pidfile->running = false ); } else { wolf_log( WOLF_LOG_EMERG, WOLF_CATEGORY_DAEMON, WOLF_MSG_DAEMON_CANT_OPEN_PIDFILE_FOR_READING, _( "Unable to open pidfile '%s' for reading: %s" ), pidfile->filename, strerror( errno ) ); *error = WOLF_ERR_INTERNAL; return( pidfile->running = false ); } } /* 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 */ wolf_log( WOLF_LOG_DEBUG, WOLF_CATEGORY_DAEMON, WOLF_MSG_DAEMON_ANOTHER_IS_RUNNING, _( "Another process locks the pidfile, daemon already running" ) ); *error = WOLF_OK; pidfile->locked = true; pidfile->running = true; } else { wolf_log( WOLF_LOG_EMERG, WOLF_CATEGORY_DAEMON, WOLF_MSG_DAEMON_CANT_LOCK_PIDFILE, _( "Unable to lock pidfile '%s': %s" ), pidfile->filename, strerror( errno ) ); (void)close( pidfile->fd ); *error = WOLF_ERR_INTERNAL; return( pidfile->running = false ); } } else { /* we can lock the file but it could be a relict, so we are not sure * yet if the daemon is already running or not */ pidfile->locked = true; } /* try to read the pid from the file */ bytes_read = read( pidfile->fd, buf, sizeof( buf ) - 1 ); if( bytes_read < 0 ) { wolf_log( WOLF_LOG_EMERG, WOLF_CATEGORY_DAEMON, WOLF_MSG_DAEMON_CANT_READ_PID_FROM_PIDFILE, _( "Unable to read pid from pidfile '%s': %s" ), pidfile->filename, strerror( errno ) ); (void)close( pidfile->fd ); *error = WOLF_ERR_INTERNAL; return( pidfile->running = false ); } /* 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 */ ) { wolf_log( WOLF_LOG_EMERG, WOLF_CATEGORY_DAEMON, WOLF_MSG_DAEMON_PIDFILE_WITH_ILLEGAL_DATA, _( "pidfile '%s' contains invalid data, can't read PID from it!" ), pidfile->filename ); (void)close( pidfile->fd ); *error = WOLF_ERR_INTERNAL; return( pidfile->running = false ); } wolf_log( WOLF_LOG_DEBUG, WOLF_CATEGORY_DAEMON, WOLF_MSG_DAEMON_FOUND_PID_IN_PIDFILE, _( "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 */ wolf_log( WOLF_LOG_EMERG, WOLF_CATEGORY_DAEMON, WOLF_MSG_DAEMON_FOUND_PID_BUT_NO_PROCESS_RUNNING, _( "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 = WOLF_ERR_INTERNAL; return( pidfile->running = false ); } else { wolf_log( WOLF_LOG_EMERG, WOLF_CATEGORY_DAEMON, WOLF_MSG_DAEMON_PROCESS_ALIVE_CHECK_FAILED, _( "Can't check if process with PID '%lu' is alive: %s" ), *pid, strerror( errno ) ); (void)close( pidfile->fd ); *error = WOLF_ERR_INTERNAL; return( pidfile->running = false ); } } wolf_log( WOLF_LOG_DEBUG, WOLF_CATEGORY_DAEMON, WOLF_MSG_DAEMON_ALREADY_RUNNING_WITH_PID, _( "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 = WOLF_OK; return( pidfile->running = true ); } wolf_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 ) { wolf_log( WOLF_LOG_EMERG, WOLF_CATEGORY_DAEMON, WOLF_MSG_DAEMON_CANT_OPEN_PIDFILE_FOR_WRITTING, _( "Unable to open pidfile '%s' for writing: %s" ), pidfile->filename, strerror( errno ) ); return WOLF_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? */ wolf_log( WOLF_LOG_EMERG, WOLF_CATEGORY_DAEMON, WOLF_MSG_DAEMON_PIDFILE_IN_USE, _( "Unable to lock pidfile '%s' after creation, daemon started in parallel?" ), pidfile->filename ); (void)close( pidfile->fd ); (void)unlink( pidfile->filename ); return WOLF_ERR_INVALID_STATE; } else { wolf_log( WOLF_LOG_EMERG, WOLF_CATEGORY_DAEMON, WOLF_MSG_DAEMON_CANT_LOCK_PIDFILE_AFTER_CREATION, _( "Unable to lock pidfile '%s' after creation: %s" ), pidfile->filename, strerror( errno ) ); (void)close( pidfile->fd ); (void)unlink( pidfile->filename ); return WOLF_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 ) { wolf_log( WOLF_LOG_EMERG, WOLF_CATEGORY_DAEMON, WOLF_MSG_DAEMON_CANT_TRUNCATE_PIDFILE, _( "Unable to truncate the pidfile '%s' before writing to it" ), pidfile->filename, strerror( errno ) ); (void)close( pidfile->fd ); (void)unlink( pidfile->filename ); return WOLF_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 ) { wolf_log( WOLF_LOG_EMERG, WOLF_CATEGORY_DAEMON, WOLF_MSG_DAEMON_CANT_WRITE_PID_INTO_PIDFILE, _( "Unable to write PID into the pidfile '%s': %s" ), pidfile->filename, strerror( errno ) ); (void)close( pidfile->fd ); (void)unlink( pidfile->filename ); return WOLF_ERR_INTERNAL; } else if( bytes_writen != (ssize_t)strlen( pid_string ) ) { /* non-atomic write on files with so little data, strange, should never happen! */ wolf_log( WOLF_LOG_EMERG, WOLF_CATEGORY_DAEMON, WOLF_MSG_DAEMON_ATOMIC_PIDFILE_WRITE_FAILED, _( "Non-atomic write failed when storing the PID into the pidfile '%s'" ), pidfile->filename ); } wolf_log( WOLF_LOG_DEBUG, WOLF_CATEGORY_DAEMON, WOLF_MSG_DAEMON_STORED_PIDFILE, _( "Stored '%lu' into the pidfile '%s' and locked it" ), (unsigned long)getpid( ), pidfile->filename ); pidfile->locked = true; return WOLF_OK; } wolf_error_t pidfile_release( struct pidfile_t *pidfile ) { wolf_error_t error = WOLF_OK; wolf_log( WOLF_LOG_DEBUG, WOLF_CATEGORY_DAEMON, WOLF_MSG_DAEMON_RELEASING_PIDFILE, _( "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 ) { wolf_log( WOLF_LOG_ALERT, WOLF_CATEGORY_DAEMON, WOLF_MSG_DAEMON_CANT_UNLOCK_PIDFILE, _( "Unable to unlock the pidfile '%s': %s" ), pidfile->filename, strerror( errno ) ); error = WOLF_ERR_INTERNAL; } } if( pidfile->fd >= 0 ) { if( close( pidfile->fd ) < 0 ) { wolf_log( WOLF_LOG_ALERT, WOLF_CATEGORY_DAEMON, WOLF_MSG_DAEMON_CANT_CLOSE_PIDFILE, _( "Unable to close the pidfile '%s': %s" ), pidfile->filename, strerror( errno ) ); error = WOLF_ERR_INTERNAL; } pidfile->fd = -1; } return error; } wolf_error_t pidfile_remove( struct pidfile_t *pidfile ) { wolf_error_t error = WOLF_OK; wolf_log( WOLF_LOG_DEBUG, WOLF_CATEGORY_DAEMON, WOLF_MSG_DAEMON_REMOVING_PIDFILE, _( "Removing pidfile '%s' (fd: %d, locked: %d, running: %d)" ), pidfile->filename, pidfile->fd, pidfile->locked, pidfile->running ); /* if we know that another process is running, then we also know that we are * not allowed to remove the pidfile */ if( pidfile->running ) { return WOLF_OK; } /* unconditionally remove the pidfile */ if( unlink( pidfile->filename ) < 0 ) { wolf_log( WOLF_LOG_ALERT, WOLF_CATEGORY_DAEMON, WOLF_MSG_DAEMON_CANT_REMOVE_PIDFILE, _( "Unable to remove the pidfile '%s': %s" ), pidfile->filename, strerror( errno ) ); error = WOLF_ERR_INTERNAL; } return error; }