/* * Copyright (c) 2005 Sendmail, Inc. and its suppliers. * All rights reserved. * * By using this file, you agree to the terms and conditions set * forth in the LICENSE file which can be found at the top level of * the sendmail distribution. */ #include "sm/generic.h" SM_RCSID("@(#)$Id: chkpidfile.c,v 1.5 2007/06/07 04:41:32 ca Exp $") #include "sm/error.h" #include "sm/assert.h" #include "sm/stat.h" #include "sm/fcntl.h" #include "sm/signal.h" #include "sm/misc.h" #include /* ** SM_CHK_PIDFILE -- check whether pid_file exists and is "valid" ** ** Parameters: ** pidfile -- pathname for pid file ** pfd -- (pointer to) fd for pid file (output, can be NULL) ** errtxt -- buffer for error text (output) ** errlen -- length of errtext ** ** Returns: ** usual sm_error code ** ** Usage: sm_chk_pidfile(pid_file, &pid_fd, errtxt, sizeof(errtxt)); ** on exit: ** if (pid_fd >= 0) ** { ** close(pid_fd); ** pid_fd = -1; ** (void) unlink(pid_file); ** } */ sm_ret_T sm_chk_pidfile(const char *pidfile, int *pfd, char *errtxt, size_t errlen) { int r, save_errno; pid_t pid; int fd; size_t len; ssize_t nbytes; struct stat stb; char buf[32]; SM_REQUIRE(pidfile != NULL); SM_REQUIRE(errtxt != NULL); pid = getpid(); fd = -1; r = stat(pidfile, &stb); if (r == 0) { long pidold; /* read/write is necessary for lockf() */ fd = open(pidfile, O_RDWR, 0644); if (fd == -1) { snprintf(errtxt, errlen, "pid_file=%s, status=pid_file exists but not readable; not starting as other process might be running" , pidfile); return sm_err_temp(EEXIST); } nbytes = read(fd, buf, sizeof(buf)); if (nbytes == -1 || nbytes <= 1) { save_errno = (nbytes == -1) ? errno : EINVAL; snprintf(errtxt, errlen, "pid_file=%s, status=cannot read content of pid_file; not starting as other process might be running" , pidfile); goto error; } r = sscanf(buf, "%ld\n", &pidold); if (r <= 0) { snprintf(errtxt, errlen, "pid_file=%s, status=cannot read content of pid_file; not starting as other process might be running" , pidfile); save_errno = EEXIST; goto error; } #if HAVE_LOCKF len = (size_t) nbytes; r = lseek(fd, 0, SEEK_SET); r = lockf(fd, F_TEST, len); if (r == -1) { save_errno = errno; snprintf(errtxt, errlen, "pid_file=%s, pid=%ld, status=locked; not starting as other process might be running" , pidfile, pidold); goto error; } #endif /* HAVE_LOCKF */ close(fd); fd = -1; r = kill(pidold, 0); save_errno = errno; if (r == 0) { snprintf(errtxt, errlen, "pid_file=%s, pid=%ld, status=process with pid in pid_file exists; not starting as other process might be running" , pidfile, pidold); return sm_err_temp(EEXIST); } if (r == -1 && save_errno == EPERM) { snprintf(errtxt, errlen, "pid_file=%s, pid=%ld, status=process with pid in pid_file may exist but permission denied; not starting as other process might be running" , pidfile, pidold); return sm_err_temp(EEXIST); } if (r == -1 && save_errno != ESRCH) { snprintf(errtxt, errlen, "pid_file=%s, pid=%ld, kill=%d, status=process with pid in pid_file may exist; not starting as other process might be running" , pidfile, pidold, save_errno); return sm_err_temp(EEXIST); } /* pid from pid_file doesn't exist anymore */ (void) unlink(pidfile); snprintf(errtxt, errlen, "pid_file=%s, pid=%ld, status=starting as process with that pid does not exist; pid_file removed" , pidfile, pidold); } fd = open(pidfile, O_WRONLY|O_CREAT|O_EXCL, 0644); if (fd >= 0) { snprintf(buf, sizeof(buf), "%ld\n", (long) pid); len = strlen(buf); /* ** There's a race condition here as the file is locked ** after it has been created. However, O_EXLOCK doesn't ** have the right semantics (remove lock on exit) and ** SunOS 5 says it shouldn't be used. ** Try to minimize the race condition by locking first ** and then writing the pid into the file, see read() ** above. */ if (pfd != NULL) { #if HAVE_LOCKF r = lockf(fd, F_LOCK, len); if (r != 0) { save_errno = errno; snprintf(errtxt, errlen, "pid_file=%s, lock=%s" , pidfile, strerror(save_errno)); goto error; } #endif /* HAVE_LOCKF */ r = fcntl(fd, F_GETFD, 0); if (r == -1) { save_errno = errno; snprintf(errtxt, errlen, "pid_file=%s, fcntl=%s" , pidfile, strerror(save_errno)); goto error; } (void) fcntl(fd, F_SETFD, r|FD_CLOEXEC); *pfd = fd; } nbytes = write(fd, buf, len); save_errno = errno; if (nbytes != len) { snprintf(errtxt, errlen, "pid_file=%s, status=cannot write, error=%s" , pidfile, strerror(save_errno)); goto error; } if (pfd == NULL) { close(fd); fd = -1; } } else { save_errno = errno; snprintf(errtxt, errlen, "pid_file=%s, status=cannot open for write, error=%s" , pidfile, strerror(save_errno)); goto error; } return 0; error: if (fd >= 0) close(fd); return sm_err_temp((save_errno == 0) ? EIO : save_errno); }