/*
* 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 <stdio.h>
/*
** 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);
}
syntax highlighted by Code2HTML, v. 0.9.1