Implement pidfile_lock, pidfile_read and pidfile_clean.
Discussed on tech-net@, ok core@.
This commit is contained in:
parent
3133e4c397
commit
6b9006e83c
|
@ -1,4 +1,4 @@
|
|||
# $NetBSD: mi,v 1.2028 2016/04/09 06:21:16 riastradh Exp $
|
||||
# $NetBSD: mi,v 1.2029 2016/04/10 19:05:50 roy Exp $
|
||||
#
|
||||
# Note: don't delete entries from here - mark them as "obsolete" instead.
|
||||
./etc/mtree/set.comp comp-sys-root
|
||||
|
@ -8026,6 +8026,9 @@
|
|||
./usr/share/man/cat3/pechochar.0 comp-c-catman .cat
|
||||
./usr/share/man/cat3/perror.0 comp-c-catman .cat
|
||||
./usr/share/man/cat3/pidfile.0 comp-c-catman .cat
|
||||
./usr/share/man/cat3/pidfile_clean.0 comp-c-catman .cat
|
||||
./usr/share/man/cat3/pidfile_lock.0 comp-c-catman .cat
|
||||
./usr/share/man/cat3/pidfile_read.0 comp-c-catman .cat
|
||||
./usr/share/man/cat3/pidlock.0 comp-c-catman .cat
|
||||
./usr/share/man/cat3/pmap_getmaps.0 comp-c-catman .cat
|
||||
./usr/share/man/cat3/pmap_getport.0 comp-c-catman .cat
|
||||
|
@ -15174,6 +15177,9 @@
|
|||
./usr/share/man/html3/pechochar.html comp-c-htmlman html
|
||||
./usr/share/man/html3/perror.html comp-c-htmlman html
|
||||
./usr/share/man/html3/pidfile.html comp-c-htmlman html
|
||||
./usr/share/man/html3/pidfile_clean.html comp-c-htmlman html
|
||||
./usr/share/man/html3/pidfile_lock.html comp-c-htmlman html
|
||||
./usr/share/man/html3/pidfile_read.html comp-c-htmlman html
|
||||
./usr/share/man/html3/pidlock.html comp-c-htmlman html
|
||||
./usr/share/man/html3/pmap_getmaps.html comp-c-htmlman html
|
||||
./usr/share/man/html3/pmap_getport.html comp-c-htmlman html
|
||||
|
@ -22311,6 +22317,9 @@
|
|||
./usr/share/man/man3/pechochar.3 comp-c-man .man
|
||||
./usr/share/man/man3/perror.3 comp-c-man .man
|
||||
./usr/share/man/man3/pidfile.3 comp-c-man .man
|
||||
./usr/share/man/man3/pidfile_clean.3 comp-c-man .man
|
||||
./usr/share/man/man3/pidfile_lock.3 comp-c-man .man
|
||||
./usr/share/man/man3/pidfile_read.3 comp-c-man .man
|
||||
./usr/share/man/man3/pidlock.3 comp-c-man .man
|
||||
./usr/share/man/man3/pmap_getmaps.3 comp-c-man .man
|
||||
./usr/share/man/man3/pmap_getport.3 comp-c-man .man
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* $NetBSD: util.h,v 1.68 2015/09/24 14:39:37 christos Exp $ */
|
||||
/* $NetBSD: util.h,v 1.69 2016/04/10 19:05:50 roy Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 1995
|
||||
|
@ -103,6 +103,9 @@ time_t parsedate(const char *, const time_t *, const int *)
|
|||
__RENAME(__parsedate50);
|
||||
#endif
|
||||
int pidfile(const char *);
|
||||
pid_t pidfile_lock(const char *);
|
||||
pid_t pidfile_read(const char *);
|
||||
int pidfile_clean(void);
|
||||
int pidlock(const char *, int, pid_t *, const char *);
|
||||
int pw_abort(void);
|
||||
#ifndef __LIBC12_SOURCE__
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# $NetBSD: Makefile,v 1.78 2015/09/24 14:39:20 christos Exp $
|
||||
# $NetBSD: Makefile,v 1.79 2016/04/10 19:05:50 roy Exp $
|
||||
# @(#)Makefile 8.1 (Berkeley) 6/4/93
|
||||
|
||||
USE_SHLIBDIR= yes
|
||||
|
@ -58,6 +58,9 @@ MLINKS+=login_cap.3 setusercontext.3
|
|||
MLINKS+=loginx.3 logoutx.3 loginx.3 logwtmpx.3
|
||||
MLINKS+=openpty.3 login_tty.3
|
||||
MLINKS+=openpty.3 forkpty.3
|
||||
MLINKS+=pidfile.3 pidfile_clean.3
|
||||
MLINKS+=pidfile.3 pidfile_lock.3
|
||||
MLINKS+=pidfile.3 pidfile_read.3
|
||||
MLINKS+=pw_getconf.3 pw_getpwconf.3
|
||||
MLINKS+=pw_init.3 pw_edit.3
|
||||
MLINKS+=pw_init.3 pw_prompt.3
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
.\" $NetBSD: pidfile.3,v 1.13 2011/03/29 13:55:37 jmmv Exp $
|
||||
.\" $NetBSD: pidfile.3,v 1.14 2016/04/10 19:05:50 roy Exp $
|
||||
.\"
|
||||
.\" Copyright (c) 1999 The NetBSD Foundation, Inc.
|
||||
.\" Copyright (c) 1999, 2016 The NetBSD Foundation, Inc.
|
||||
.\" All rights reserved.
|
||||
.\"
|
||||
.\" This code is derived from software contributed to The NetBSD Foundation
|
||||
.\" by Jason R. Thorpe.
|
||||
.\" by Jason R. Thorpe and Roy Marples.
|
||||
.\"
|
||||
.\" Redistribution and use in source and binary forms, with or without
|
||||
.\" modification, are permitted provided that the following conditions
|
||||
|
@ -27,7 +27,7 @@
|
|||
.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
.\" POSSIBILITY OF SUCH DAMAGE.
|
||||
.\"
|
||||
.Dd March 23, 2011
|
||||
.Dd April 10, 2016
|
||||
.Dt PIDFILE 3
|
||||
.Os
|
||||
.Sh NAME
|
||||
|
@ -39,13 +39,21 @@
|
|||
.In util.h
|
||||
.Ft int
|
||||
.Fn pidfile "const char *path"
|
||||
.Ft pid_t
|
||||
.Fn pidfile_lock "const char *path"
|
||||
.Ft pid_t
|
||||
.Fn pidfile_read "const char *path"
|
||||
.Ft int
|
||||
.Fn pidfile_clean "void"
|
||||
.Sh DESCRIPTION
|
||||
.Fn pidfile
|
||||
creates a file containing the process ID of the caller program.
|
||||
and
|
||||
.Fn pidfile_lock
|
||||
create and lock a file containing the process ID of the calling program.
|
||||
The pid file can be used as a quick reference if
|
||||
the process needs to be sent a signal.
|
||||
When the program exits, the pid file is removed automatically, unless
|
||||
the program receives a fatal signal.
|
||||
The pid file is truncated and removed automatically when the program exits,
|
||||
unless the program receives a fatal signal.
|
||||
.Pp
|
||||
If
|
||||
.Ar path
|
||||
|
@ -72,21 +80,60 @@ is an absolute or relative path (i.e. it contains the
|
|||
character),
|
||||
the pid file is created in the provided location.
|
||||
.Pp
|
||||
Note that only the first invocation of
|
||||
.Fn pidfile
|
||||
causes a pid file to be written; subsequent invocations have no effect
|
||||
unless a new
|
||||
.Ar path
|
||||
is supplied.
|
||||
If called with a new
|
||||
.Ar path ,
|
||||
.Fn pidfile
|
||||
will remove the old pid file and write the new one.
|
||||
and
|
||||
.Fn pidfile_lock
|
||||
will remove the old pid file.
|
||||
.Pp
|
||||
The pid file is truncated, so these functions can be called multiple times and
|
||||
allow a child process to take over the lock.
|
||||
.Pp
|
||||
.Fn pidfile_read
|
||||
will read the last pid file created, or specified by
|
||||
.Ar path ,
|
||||
and return the process ID it contains.
|
||||
.Pp
|
||||
.Fn pidfile_clean
|
||||
will
|
||||
.Xr ftruncate 2 ,
|
||||
.Xr close 2
|
||||
and
|
||||
.Xr unlink 2
|
||||
the last opening pid file if, and only if, the current process wrote it.
|
||||
This function should be called if the program needs to call
|
||||
.Xr _exit 2
|
||||
(such as from a signal handler) and needs to clean up the pid file.
|
||||
.Sh RETURN VALUES
|
||||
.Fn pidfile
|
||||
and
|
||||
.Fn pidfile_clean
|
||||
returns 0 on success and -1 on failure.
|
||||
.Pp
|
||||
.Fn pidfile_lock
|
||||
returns 0 on success.
|
||||
Otherwise, the process ID who owns the lock is returned and if that
|
||||
cannot be derived then -1 is returned.
|
||||
.Pp
|
||||
.Fn pidfile_read
|
||||
returns the process ID if known, otherwise -1.
|
||||
.Sh ERRORS
|
||||
The
|
||||
.Fn pidfile
|
||||
and
|
||||
.Fn pidfile_lock
|
||||
functions will fail if:
|
||||
.Bl -tag -width Er
|
||||
.It Bq Er EEXIST
|
||||
Some process already holds the lock on the given pid file, meaning that a
|
||||
daemon is already running.
|
||||
.It Bq Er ENAMETOOLONG
|
||||
Specified pidfile's name is too long.
|
||||
.El
|
||||
.Sh SEE ALSO
|
||||
.Xr atexit 3
|
||||
.Xr atexit 3 ,
|
||||
.Xr flock 2
|
||||
.Sh HISTORY
|
||||
The
|
||||
.Fn pidfile
|
||||
|
@ -94,12 +141,30 @@ function call appeared in
|
|||
.Nx 1.5 .
|
||||
Support for creating pid files in any arbitrary path was added in
|
||||
.Nx 6.0 .
|
||||
.Sh BUGS
|
||||
.Pp
|
||||
The
|
||||
.Fn pidfile_lock ,
|
||||
.Fn pidfile_read
|
||||
and
|
||||
.Fn pidfile_clean
|
||||
function calls appeared in
|
||||
.Nx 8 .
|
||||
.Sh CAVEATS
|
||||
.Fn pidfile
|
||||
uses
|
||||
and
|
||||
.Fn pidfile_lock
|
||||
use
|
||||
.Xr atexit 3
|
||||
to ensure the pid file is unlinked at program exit.
|
||||
to ensure the pid file is cleaned at program exit.
|
||||
However, programs that use the
|
||||
.Xr _exit 2
|
||||
function (for example, in signal handlers)
|
||||
will not trigger this behaviour.
|
||||
will not trigger this behaviour and should call
|
||||
.Xr pidfile_clean.
|
||||
Like-wise, if the program creates a pid file before
|
||||
.Xr fork 2 Ns ing
|
||||
a child to take over, it should use the
|
||||
.Xr _exit 2
|
||||
function instead of returning or using the
|
||||
.Xr exit 2
|
||||
function to ensure the pid file is not cleaned.
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
/* $NetBSD: pidfile.c,v 1.11 2015/01/22 19:04:28 christos Exp $ */
|
||||
/* $NetBSD: pidfile.c,v 1.12 2016/04/10 19:05:50 roy Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 1999 The NetBSD Foundation, Inc.
|
||||
* Copyright (c) 1999, 2016 The NetBSD Foundation, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This code is derived from software contributed to The NetBSD Foundation
|
||||
* by Jason R. Thorpe, Matthias Scheler and Julio Merino.
|
||||
* by Jason R. Thorpe, Matthias Scheler, Julio Merino and Roy Marples.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
|
@ -31,11 +31,14 @@
|
|||
|
||||
#include <sys/cdefs.h>
|
||||
#if defined(LIBC_SCCS) && !defined(lint)
|
||||
__RCSID("$NetBSD: pidfile.c,v 1.11 2015/01/22 19:04:28 christos Exp $");
|
||||
__RCSID("$NetBSD: pidfile.c,v 1.12 2016/04/10 19:05:50 roy Exp $");
|
||||
#endif
|
||||
|
||||
#include <sys/param.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <paths.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
|
@ -45,131 +48,205 @@ __RCSID("$NetBSD: pidfile.c,v 1.11 2015/01/22 19:04:28 christos Exp $");
|
|||
#include <util.h>
|
||||
|
||||
static pid_t pidfile_pid;
|
||||
static char *pidfile_path;
|
||||
static char pidfile_path[PATH_MAX];
|
||||
static int pidfile_fd = -1;
|
||||
|
||||
/* Deletes an existent pidfile iff it was created by this process. */
|
||||
/* Closes pidfile resources.
|
||||
*
|
||||
* Returns 0 on success, otherwise -1. */
|
||||
static int
|
||||
pidfile_close(void)
|
||||
{
|
||||
int error;
|
||||
|
||||
pidfile_pid = 0;
|
||||
error = close(pidfile_fd);
|
||||
pidfile_fd = -1;
|
||||
pidfile_path[0] = '\0';
|
||||
return error;
|
||||
}
|
||||
|
||||
/* Truncate, close and unlink an existent pidfile,
|
||||
* if and only if it was created by this process.
|
||||
* The pidfile is truncated because we may have dropped permissions
|
||||
* or entered a chroot and thus unable to unlink it.
|
||||
*
|
||||
* Returns 0 on truncation success, otherwise -1. */
|
||||
int
|
||||
pidfile_clean(void)
|
||||
{
|
||||
int error;
|
||||
|
||||
if (pidfile_fd == -1) {
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (pidfile_pid != getpid())
|
||||
error = EPERM;
|
||||
else if (ftruncate(pidfile_fd, 0) == -1)
|
||||
error = errno;
|
||||
else {
|
||||
(void) unlink(pidfile_path);
|
||||
error = 0;
|
||||
}
|
||||
|
||||
(void) pidfile_close();
|
||||
|
||||
if (error != 0) {
|
||||
errno = error;
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* atexit shim for pidfile_clean */
|
||||
static void
|
||||
pidfile_cleanup(void)
|
||||
{
|
||||
|
||||
if ((pidfile_path != NULL) && (pidfile_pid == getpid()))
|
||||
(void) unlink(pidfile_path);
|
||||
pidfile_clean();
|
||||
}
|
||||
|
||||
/* Registers an atexit(3) handler to delete the pidfile we have generated.
|
||||
* We only register the handler when we create a pidfile, so we can assume
|
||||
* that the pidfile exists.
|
||||
*
|
||||
* Returns 0 on success or -1 if the handler could not be registered. */
|
||||
static int
|
||||
register_atexit_handler(void)
|
||||
{
|
||||
static bool done = false;
|
||||
|
||||
if (!done) {
|
||||
if (atexit(pidfile_cleanup) < 0)
|
||||
return -1;
|
||||
done = true;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* Constructs a name for a pidfile in the default location (/var/run). If
|
||||
* 'bname' is NULL, uses the name of the current program for the name of
|
||||
/* Constructs a name for a pidfile in the default location (/var/run).
|
||||
* If 'bname' is NULL, uses the name of the current program for the name of
|
||||
* the pidfile.
|
||||
*
|
||||
* Returns a pointer to a dynamically-allocatd string containing the absolute
|
||||
* path to the pidfile; NULL on failure. */
|
||||
static char *
|
||||
generate_varrun_path(const char *bname)
|
||||
* Returns 0 on success, otherwise -1. */
|
||||
static int
|
||||
pidfile_varrun_path(char *path, size_t len, const char *bname)
|
||||
{
|
||||
char *path;
|
||||
|
||||
if (bname == NULL)
|
||||
bname = getprogname();
|
||||
|
||||
/* _PATH_VARRUN includes trailing / */
|
||||
if (asprintf(&path, "%s%s.pid", _PATH_VARRUN, bname) == -1)
|
||||
return NULL;
|
||||
return path;
|
||||
if ((size_t)snprintf(path, len, "%s%s.pid", _PATH_VARRUN, bname) >= len)
|
||||
{
|
||||
errno = ENAMETOOLONG;
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Creates a pidfile with the provided name. The new pidfile is "registered"
|
||||
* in the global variables pidfile_path and pidfile_pid so that any further
|
||||
* call to pidfile(3) can check if we are recreating the same file or a new
|
||||
* one.
|
||||
*
|
||||
* Returns 0 on success or -1 if there is any error. */
|
||||
static int
|
||||
create_pidfile(const char* path)
|
||||
/* Returns the process ID inside path on success, otherwise -1.
|
||||
* If no path is given, use the last pidfile path, othewise the default one. */
|
||||
pid_t
|
||||
pidfile_read(const char *path)
|
||||
{
|
||||
FILE *f;
|
||||
char dpath[PATH_MAX], buf[16], *eptr;
|
||||
int fd, error;
|
||||
ssize_t n;
|
||||
pid_t pid;
|
||||
|
||||
if (register_atexit_handler() == -1)
|
||||
if (path == NULL) {
|
||||
if (pidfile_path[0] != '\0')
|
||||
path = pidfile_path;
|
||||
else if (pidfile_varrun_path(dpath, sizeof(dpath), NULL) == -1)
|
||||
return -1;
|
||||
else
|
||||
path = dpath;
|
||||
}
|
||||
|
||||
if ((fd = open(path, O_RDONLY | O_CLOEXEC | O_NONBLOCK)) == -1)
|
||||
return -1;
|
||||
n = read(fd, buf, sizeof(buf) - 1);
|
||||
error = errno;
|
||||
(void) close(fd);
|
||||
if (n == -1) {
|
||||
errno = error;
|
||||
return -1;
|
||||
|
||||
if (cleanup_old_pidfile(path) == 0)
|
||||
return 0;
|
||||
|
||||
pidfile_path = strdup(path);
|
||||
if (pidfile_path == NULL)
|
||||
}
|
||||
buf[n] = '\0';
|
||||
pid = (pid_t)strtoi(buf, &eptr, 10, 1, INT_MAX, &error);
|
||||
if (error && !(error == ENOTSUP && *eptr == '\n')) {
|
||||
errno = error;
|
||||
return -1;
|
||||
}
|
||||
return pid;
|
||||
}
|
||||
|
||||
if ((f = fopen(path, "w")) == NULL) {
|
||||
free(pidfile_path);
|
||||
pidfile_path = NULL;
|
||||
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), NULL) == -1)
|
||||
return -1;
|
||||
path = dpath;
|
||||
}
|
||||
|
||||
/* If path has changed (no good reason), clean up the old pidfile. */
|
||||
if (strcmp(pidfile_path, path) != 0)
|
||||
pidfile_cleanup();
|
||||
|
||||
if (pidfile_fd == -1) {
|
||||
pidfile_fd = open(path,
|
||||
O_WRONLY | O_CREAT | O_CLOEXEC | O_NONBLOCK | O_EXLOCK,
|
||||
0644);
|
||||
if (pidfile_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;
|
||||
}
|
||||
strlcpy(pidfile_path, path, sizeof(pidfile_path));
|
||||
}
|
||||
|
||||
pidfile_pid = getpid();
|
||||
|
||||
(void) fprintf(f, "%d\n", pidfile_pid);
|
||||
(void) fclose(f);
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* The old function.
|
||||
* Historical behaviour is that pidfile is not re-written
|
||||
* if path has not changed.
|
||||
*
|
||||
* Returns 0 on success, otherwise -1.
|
||||
* As such we have no way of knowing the process ID who owns the lock. */
|
||||
int
|
||||
pidfile(const char *path)
|
||||
{
|
||||
pid_t pid;
|
||||
|
||||
if (path == NULL || strchr(path, '/') == NULL) {
|
||||
char *default_path;
|
||||
|
||||
if ((default_path = generate_varrun_path(path)) == NULL)
|
||||
return -1;
|
||||
|
||||
if (create_pidfile(default_path) == -1) {
|
||||
free(default_path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
free(default_path);
|
||||
return 0;
|
||||
} else
|
||||
return create_pidfile(path);
|
||||
pid = pidfile_lock(path);
|
||||
return pid == 0 ? 0 : -1;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue