diff --git a/Makefile b/Makefile index 58dbbd8..6bc5d06 100644 --- a/Makefile +++ b/Makefile @@ -56,7 +56,8 @@ endif DISTFILES = README LICENSE HEADERS = lite.h OBJS := chomp.o copyfile.o dir.o fexist.o fisdir.o fmode.o fsendfile.o -OBJS += makepath.o pidfile.o rsync.o strlcpy.o strlcat.o strtonum.o tempfile.o +OBJS += makepath.o pidfile.o pidfilefn.o rsync.o +OBJS += strlcpy.o strlcat.o strtonum.o tempfile.o DEPS := $(OBJS:.o=.d) JUNK = *~ *.bak *.map .*.d *.d DEADJOE semantic.cache *.gdb *.elf core core.* diff --git a/README.md b/README.md index d396fc1..6f5b45b 100644 --- a/README.md +++ b/README.md @@ -116,6 +116,14 @@ Generic Functions may still exist even though the program is no longer running. This is only a problem for clients. +- `pidfile_read(pidfile)` + + Read PID from pid file created by `pidfile()`. + +- `pidfile_signal(pidfile, signal)` + + Send signal to PID found in pid file created by `pidfile()`. + - `rsync(src, dst, delete, *filter())` Very simple `rsync()` to copy files files and directories diff --git a/lite.h b/lite.h index 901ba21..d10b3ea 100644 --- a/lite.h +++ b/lite.h @@ -51,7 +51,9 @@ int makepath (char *dir); int dir (const char *dir, const char *type, int (*filter) (const char *file), char ***list, int strip); int rsync (char *src, char *dst, int delete, int (*filter) (const char *file)); -int pidfile (const char *basename); +int pidfile (const char *basename); +int pidfile_signal(const char *pidfile, int signal); +int pidfile_read (const char *pidfile); #ifndef strlcpy size_t strlcpy (char *dst, const char *src, size_t siz); @@ -126,7 +128,9 @@ static inline int string_case_compare (const char *a, const char *b) } /* Compat */ -#define copy_filep(src, dst) fcopyfile(src,dst); +#define copy_filep(src, dst) fcopyfile(src,dst) +#define pidfile_read_pid(file) pifile_read(file) +#define signal_pidfile(file, signo) pidfile_signal(file,signo) #endif /* FINIT_LITE_H_ */ diff --git a/pidfilefn.c b/pidfilefn.c new file mode 100644 index 0000000..e8c686f --- /dev/null +++ b/pidfilefn.c @@ -0,0 +1,131 @@ +/* Functions for dealing with PID files (client side) + * + * Copyright (c) 2009-2015 Joachim Nilsson + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include + +extern char *chomp(char *str); + +/** + * pidfile_read - Reads a PID value from a pidfile. + * @pidfile: File containing PID, usually in /var/run/.pid + * + * This function takes a @pidfile and returns the PID found therein. + * + * Returns: + * On invalid @pidfile -1 and errno set to %EINVAL, when @pidfile does not exist -1 + * and errno set to %ENOENT. When the pidfile is empty or when its contents cannot + * be translated this function returns zero (0), on success this function returns + * a PID value greater than one. PID 1 is reserved for the system init process. + */ +int pidfile_read(const char *pidfile) +{ + int pid = 0; + char buf[16]; + FILE *fp; + + if (!pidfile) { + errno = EINVAL; + return -1; + } + + if (access(pidfile, F_OK) < 0) + return -1; + + fp = fopen(pidfile, "r"); + if (!fp) + return -1; + + if (fgets(buf, sizeof(buf), fp)) { + char *ptr = chomp(buf); + + if (ptr) { + errno = 0; + pid = strtoul(ptr, NULL, 0); + if (errno) + pid = 0; /* Failed conversion. */ + } + } + fclose(fp); + + return pid; +} + +/** + * pidfile_signal - Send signal to a PID and cleanup pidfile afterwards + * @pidfile: File containing PID, usually in /var/run/.pid + * @signal: Signal to send to PID found in @pidfile. + * + * If @signal is any of %SIGTERM, %SIGKILL, or if kill() returns -1 the + * @pidfile is removed. + * + * Returns: + * POSIX OK(0) on success, non-zero otherwise. + */ +int pidfile_signal(const char *pidfile, int signal) +{ + int pid = -1, ret = -1; + + pid = pidfile_read(pidfile); + if (pid <= 0) + return 1; + + ret = kill(pid, signal); + if ((ret == -1) || (signal == SIGTERM) || (signal == SIGKILL)) + remove(pidfile); + + return 0; +} + + +/********************************* UNIT TESTS ************************************/ +#ifdef UTEST_PIDFN +#define PIDFILE "/var/run/unittest.pid" + +static void sig_handler(int signo) +{ + fprintf(stderr, "Exiting ...\n"); +} + +int main(void) +{ + signal(SIGTERM, sig_handler); + + fprintf(stderr, "Testing pidfile() ...\n"); + pidfile(NULL); + system("ls -ld " PIDFILE); + + fprintf(stderr, "Reading pid file, should be %d ...\n", getpid()); + fprintf(stderr, "=> %d\n", pidfile_read(PIDFILE)); + + fprintf(stderr, "Signalling ourselves ...\n"); + + return pidfile_signal(PIDFILE, SIGTERM); +} +#endif /* UTEST_PIDFN */ + +/** + * Local Variables: + * compile-command: "gcc -g -o unittest -DUTEST_PIDFN pidfile.c chomp.c pidfilefn.c && ./unittest" + * version-control: t + * indent-tabs-mode: t + * c-file-style: "linux" + * End: + */