From 3119fb1ec2aa6ce7aca87844ca0581cbf3c0d193 Mon Sep 17 00:00:00 2001 From: Mattias Andrée Date: Mon, 11 Jul 2016 13:35:11 +0200 Subject: Implement use of PID file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mattias Andrée --- src/gammad.c | 176 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- src/util.c | 98 +++++++++++++++++++++++++++++++++ src/util.h | 23 ++++---- 3 files changed, 286 insertions(+), 11 deletions(-) create mode 100644 src/util.c (limited to 'src') diff --git a/src/gammad.c b/src/gammad.c index 28411cf..ad1882e 100644 --- a/src/gammad.c +++ b/src/gammad.c @@ -17,7 +17,9 @@ */ #include +#include #include +#include #include #include #include @@ -63,6 +65,7 @@ static char* get_pathname(libgamma_site_state_t* site, const char* suffix) char* rc; struct passwd* pw; size_t n; + int saved_errno; if (site->site) { @@ -104,7 +107,9 @@ static char* get_pathname(libgamma_site_state_t* site, const char* suffix) return rc; fail: + saved_errno = errno; free(name); + errno = saved_errno; return NULL; } @@ -197,6 +202,156 @@ static char* get_crtc_name(libgamma_crtc_information_t* info, libgamma_crtc_stat } +/** + * Check whether a PID file is outdated + * + * @param pidfile The PID file + * @param token An environment variable (including both key and value) + * that must exist in the process if it is a gammad process + * @return -1: An error occurred + * 0: The service is already running + * 1: The PID file is outdated + */ +static int is_pidfile_reusable(const char* pidfile, const char* token) +{ + /* PORTERS: /proc/$PID/environ is Linux specific */ + + char temp[sizeof("/proc//environ") + 3 * sizeof(pid_t)]; + int fd = -1, saved_errno; + char* content = NULL; + char* p; + char* end; + pid_t pid = 0; + size_t n; + + /* Get PID */ + fd = open(pidfile, O_RDONLY); + if (fd < 0) + return -1; + content = nread(fd, &n); + if (content == NULL) + goto fail; + close(fd), fd = -1; + + if (('0' > content[0]) || (content[0] > '9')) + goto bad; + if ((content[0] == '0') && ('0' <= content[1]) && (content[1] <= '9')) + goto bad; + for (p = content; *p; p++) + if (('0' <= *p) && (*p <= '9')) + pid = pid * 10 + (*p & 15); + else + break; + if (*p++ != '\n') + goto bad; + if (*p) + goto bad; + if ((size_t)(content - p) != n) + goto bad; + sprintf(temp, "%llu", (unsigned long long)pid); + if (strcmp(content, temp)) + goto bad; + + /* Validate PID */ + sprintf(temp, "/proc/%llu/environ", (unsigned long long)pid); + fd = open(temp, O_RDONLY); + if (fd < 0) + return ((errno == ENOENT) || (errno == EACCES)) ? 1 : -1; + content = nread(fd, &n); + if (content == NULL) + goto fail; + close(fd), fd = -1; + + for (end = (p = content) + n; p != end; p = strchr(p, '\0') + 1) + if (!strcmp(p, token)) + return 0; + + return 1; + bad: + fprintf(stderr, "%s: pid file contain invalid content: %s\n", argv0, pidfile); + errno = 0; + return -1; + fail: + saved_errno = errno; + free(content); + if (fd >= 0) + close(fd); + errno = saved_errno; + return -1; +} + + +/** + * Create PID file + * + * @param pidfile The pathname of the PID file + * @return Zero on success, -1 on error + */ +static int create_pidfile(char* pidfile) +{ + int fd, r, saved_errno; + char* p; + char* token; + + /* Create token used to validate the service. */ + token = malloc(sizeof("GAMMAD_PIDFILE_TOKEN=") + strlen(pidfile)); + if (token == NULL) + return -1; + sprintf(token, "GAMMAD_PIDFILE_TOKEN=%s", pidfile); + if (putenv(token)) + return -1; + + /* Create PID file's directory. */ + for (p = pidfile; *p == '/'; p++); + while ((p = strchr(p, '/'))) + { + *p = '\0'; + if (mkdir(pidfile, 0644) < 0) + if (errno != EEXIST) + return -1; + *p++ = '/'; + } + + /* Create PID file. */ + retry: + fd = open(pidfile, O_CREAT | O_EXCL, 0644); + if (fd < 0) + { + if (errno == EINTR) + goto retry; + if (errno != EEXIST) + return -1; + r = is_pidfile_reusable(pidfile, token); + if (r > 0) + { + unlink(pidfile); + goto retry; + } + else if (r < 0) + goto fail; + fprintf(stderr, "%s: service is already running\n", argv0); + errno = 0; + return -1; + } + + /* Write PID to PID file. */ + if (dprintf(fd, "%llu\n", (unsigned long long)getpid()) < 0) + goto fail; + + /* Done */ + if (close(fd) < 0) + if (errno != EINTR) + return -1; + return 0; + fail: + saved_errno = errno; + close(fd); + unlink(pidfile); + errno = saved_errno; + return -1; +} + + /** * Print usage information and exit */ @@ -210,11 +365,13 @@ static void usage(void) int main(int argc, char** argv) { int method = -1, gerror, rc = 1, preserve = 0; - char *sitename = NULL; + char* sitename = NULL; libgamma_site_state_t site; libgamma_partition_state_t* partitions = NULL; libgamma_crtc_state_t* crtcs = NULL; size_t i, j, n, n0; + char* pidpath = NULL; + char* socketpath = NULL; memset(&site, 0, sizeof(site)); @@ -246,6 +403,19 @@ int main(int argc, char** argv) if ((gerror = libgamma_site_initialise(&site, method, sitename))) goto fail_libgamma; + /* Get PID file and socket pathname */ + if (!(pidpath = get_pidfile_pathname(&site))) + goto fail; + if (!(socketpath = get_socket_pathname(&site))) + goto fail; + + /* Create PID file */ + if (create_pidfile(pidpath) < 0) + { + free(pidpath), pidpath = NULL; + goto fail; + } + /* Get partitions */ if (site.partitions_available) if (!(partitions = calloc(site.partitions_available, sizeof(*partitions)))) @@ -426,6 +596,10 @@ int main(int argc, char** argv) libgamma_partition_destroy(partitions + i); free(partitions); libgamma_site_destroy(&site); + free(socketpath); + if (pidpath) + unlink(pidpath); + free(pidpath); return rc; /* Fail */ fail: diff --git a/src/util.c b/src/util.c new file mode 100644 index 0000000..cb8c155 --- /dev/null +++ b/src/util.c @@ -0,0 +1,98 @@ +/** + * gammad -- Cooperative gamma server + * Copyright (C) 2016 Mattias Andrée (maandree@kth.se) + * + * This library 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 library 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 library. If not, see . + */ +#include "util.h" + +#include +#include +#include +#include +#include + + + +/** + * Duplicate a memory segment + * + * @param src The memory segment, must not be `NULL` + * @param n The size of the memory segment, must not be zero + * @return The duplicate of the memory segment, + * `NULL` on error + */ +void* memdup(const void* src, size_t n) +{ + void* dest = malloc(n); + if (dest == NULL) + return NULL; + memcpy(dest, src, n); + return dest; +} + + +/** + * Read an entire file + * + * @param fd The file descriptor + * @param n Output for the size of the file + * @return The read content, plus a NUL byte at + * the end (not counted in `*n`) + */ +void* nread(int fd, size_t* n) +{ + size_t size = 32; + ssize_t got; + struct stat st; + char* buf = NULL; + char* new; + int saved_errno; + + *n = 0; + + if (!fstat(fd, &st)) + size = st.st_size <= 0 ? 32 : (size_t)(st.st_size); + + buf = malloc(size + 1); + if (buf == NULL) + return NULL; + + for (;;) + { + if (*n == size) + { + new = realloc(buf, (size <<= 1) + 1); + if (new == NULL) + goto fail; + buf = new; + } + + got = read(fd, buf + *n, size - *n); + if (got < 0) + goto fail; + if (got == 0) + break; + *n += (size_t)got; + } + + buf[*n] = '\0'; + return buf; + fail: + saved_errno = errno; + free(buf); + errno = saved_errno; + return NULL; +} + diff --git a/src/util.h b/src/util.h index 4f9cbde..c44164b 100644 --- a/src/util.h +++ b/src/util.h @@ -15,8 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this library. If not, see . */ -#include -#include +#include @@ -28,12 +27,16 @@ * @return The duplicate of the memory segment, * `NULL` on error */ -static inline void* memdup(const void* src, size_t n) -{ - void* dest = malloc(n); - if (dest == NULL) - return NULL; - memcpy(dest, src, n); - return dest; -} +void* memdup(const void* src, size_t n); + + +/** + * Read an entire file + * + * @param fd The file descriptor + * @param n Output for the size of the file + * @return The read content, plus a NUL byte at + * the end (not counted in `*n`) + */ +void* nread(int fd, size_t* n); -- cgit v1.2.3-70-g09d2