/* Given a new pidfile name in 'path', deletes any previously-created pidfile * if the previous file differs to the new one. * * If a previous file is deleted, returns 1, which means that a new pidfile * must be created. Otherwise, this returns 0, which means that the existing * file does not need to be touched. */ static int cleanup_old_pidfile(const char* path) { if (pidfile_path != NULL) { if (strcmp(pidfile_path, path) != 0) { pidfile_cleanup(); free(pidfile_path); pidfile_path = NULL; return 1; } else return 0; } else return 1; }
/* Locks the pidfile specified by path and writes the process pid to it. * The new pidfile is "registered" in the global variables pidfile_fd, * pidfile_path and pidfile_pid so that any further call to pidfile_lock(3) * can check if we are recreating the same file or a new one. * * Returns 0 on success, otherwise the pid of the process who owns the * lock if it can be read, otherwise -1. */ pid_t pidfile_lock(const char *path) { char dpath[PATH_MAX]; static bool registered_atexit = false; /* Register for cleanup with atexit. */ if (!registered_atexit) { if (atexit(pidfile_cleanup) == -1) return -1; registered_atexit = true; } if (path == NULL || strchr(path, '/') == NULL) { if (pidfile_varrun_path(dpath, sizeof(dpath), path) == -1) return -1; path = dpath; } /* If path has changed (no good reason), clean up the old pidfile. */ if (pidfile_fd != -1 && strcmp(pidfile_path, path) != 0) pidfile_clean(); if (pidfile_fd == -1) { int fd, opts; opts = O_WRONLY | O_CREAT | O_NONBLOCK; #ifdef O_CLOEXEC opts |= O_CLOEXEC; #endif #ifdef O_EXLOCK opts |= O_EXLOCK; #endif if ((fd = open(path, opts, 0644)) == -1) goto return_pid; #ifndef O_CLOEXEC if ((opts = fcntl(fd, F_GETFD)) == -1 || fctnl(fd, F_SETFL, opts | FD_CLOEXEC) == -1) { int error = errno; (void) close(fd); errno = error; return -1; } #endif #ifndef O_EXLOCK if (flock(fd, LOCK_EX | LOCK_NB) == -1) { int error = errno; (void) close(fd); if (error != EAGAIN) { errno = error; return -1; } fd = -1; } #endif return_pid: if (fd == -1) { pid_t pid; if (errno == EAGAIN) { /* The pidfile is locked, return the process ID * it contains. * If sucessful, set errno to EEXIST. */ if ((pid = pidfile_read(path)) != -1) errno = EEXIST; } else pid = -1; return pid; } pidfile_fd = fd; strlcpy(pidfile_path, path, sizeof(pidfile_path)); } pidfile_pid = getpid(); /* Truncate the file, as we could be re-writing it. * Then write the process ID. */ if (ftruncate(pidfile_fd, 0) == -1 || lseek(pidfile_fd, 0, SEEK_SET) == -1 || dprintf(pidfile_fd, "%d\n", pidfile_pid) == -1) { int error = errno; pidfile_cleanup(); errno = error; return -1; } /* Hold the fd open to persist the lock. */ return 0; }
int pidfile(const char *basename) { FILE *f; /* * Register handler which will remove the pidfile later. */ if (!pidfile_atexit_done) { if (atexit(pidfile_cleanup) < 0) return -1; pidfile_atexit_done = 1; } if (basename == NULL) basename = getprogname(); /* * If pidfile has already been created for the supplied basename * we don't need to create a pidfile again. */ if (pidfile_path != NULL) { if (strcmp(pidfile_basename, basename) == 0) return 0; /* * Remove existing pidfile if it was created by this process. */ pidfile_cleanup(); free(pidfile_path); pidfile_path = NULL; free(pidfile_basename); pidfile_basename = NULL; } pidfile_pid = getpid(); pidfile_basename = strdup(basename); if (pidfile_basename == NULL) return -1; /* _PATH_VARRUN includes trailing / */ (void) asprintf(&pidfile_path, "%s%s.pid", _PATH_VARRUN, basename); if (pidfile_path == NULL) { free(pidfile_basename); pidfile_basename = NULL; return -1; } if ((f = fopen(pidfile_path, "w")) == NULL) { free(pidfile_path); pidfile_path = NULL; free(pidfile_basename); pidfile_basename = NULL; return -1; } (void) fprintf(f, "%d\n", pidfile_pid); (void) fclose(f); return 0; }