Module Name: src
Committed By: roy
Date: Sun Apr 10 19:05:51 UTC 2016
Modified Files:
src/distrib/sets/lists/comp: mi
src/include: util.h
src/lib/libutil: Makefile pidfile.3 pidfile.c
Log Message:
Implement pidfile_lock, pidfile_read and pidfile_clean.
Discussed on tech-net@, ok core@.
To generate a diff of this commit:
cvs rdiff -u -r1.2028 -r1.2029 src/distrib/sets/lists/comp/mi
cvs rdiff -u -r1.68 -r1.69 src/include/util.h
cvs rdiff -u -r1.78 -r1.79 src/lib/libutil/Makefile
cvs rdiff -u -r1.13 -r1.14 src/lib/libutil/pidfile.3
cvs rdiff -u -r1.11 -r1.12 src/lib/libutil/pidfile.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/distrib/sets/lists/comp/mi
diff -u src/distrib/sets/lists/comp/mi:1.2028 src/distrib/sets/lists/comp/mi:1.2029
--- src/distrib/sets/lists/comp/mi:1.2028 Sat Apr 9 06:21:16 2016
+++ src/distrib/sets/lists/comp/mi Sun Apr 10 19:05:50 2016
@@ -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
Index: src/include/util.h
diff -u src/include/util.h:1.68 src/include/util.h:1.69
--- src/include/util.h:1.68 Thu Sep 24 14:39:37 2015
+++ src/include/util.h Sun Apr 10 19:05:50 2016
@@ -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 ti
__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__
Index: src/lib/libutil/Makefile
diff -u src/lib/libutil/Makefile:1.78 src/lib/libutil/Makefile:1.79
--- src/lib/libutil/Makefile:1.78 Thu Sep 24 14:39:20 2015
+++ src/lib/libutil/Makefile Sun Apr 10 19:05:50 2016
@@ -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
Index: src/lib/libutil/pidfile.3
diff -u src/lib/libutil/pidfile.3:1.13 src/lib/libutil/pidfile.3:1.14
--- src/lib/libutil/pidfile.3:1.13 Tue Mar 29 13:55:37 2011
+++ src/lib/libutil/pidfile.3 Sun Apr 10 19:05:50 2016
@@ -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
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
-.Fn pidfile
-uses
+.Pp
+The
+.Fn pidfile_lock ,
+.Fn pidfile_read
+and
+.Fn pidfile_clean
+function calls appeared in
+.Nx 8 .
+.Sh CAVEATS
+.Fn pidfile
+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.
Index: src/lib/libutil/pidfile.c
diff -u src/lib/libutil/pidfile.c:1.11 src/lib/libutil/pidfile.c:1.12
--- src/lib/libutil/pidfile.c:1.11 Thu Jan 22 19:04:28 2015
+++ src/lib/libutil/pidfile.c Sun Apr 10 19:05:50 2016
@@ -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/
#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. */
-static void
-pidfile_cleanup(void)
+/* Closes pidfile resources.
+ *
+ * Returns 0 on success, otherwise -1. */
+static int
+pidfile_close(void)
{
+ int error;
- if ((pidfile_path != NULL) && (pidfile_pid == getpid()))
- (void) unlink(pidfile_path);
+ pidfile_pid = 0;
+ error = close(pidfile_fd);
+ pidfile_fd = -1;
+ pidfile_path[0] = '\0';
+ return error;
}
-/* 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.
+/* 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 success or -1 if the handler could not be registered. */
-static int
-register_atexit_handler(void)
+ * Returns 0 on truncation success, otherwise -1. */
+int
+pidfile_clean(void)
{
- static bool done = false;
+ int error;
- if (!done) {
- if (atexit(pidfile_cleanup) < 0)
- return -1;
- done = true;
+ 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;
}
-/* 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)
+/* atexit shim for pidfile_clean */
+static void
+pidfile_cleanup(void)
{
- 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;
+ pidfile_clean();
}
-/* 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;
+}
+
+/* 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)
+{
+ char dpath[PATH_MAX], buf[16], *eptr;
+ int fd, error;
+ ssize_t n;
+ pid_t pid;
+
+ 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;
+ }
+ 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;
}
-/* 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.
+/* 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 or -1 if there is any error. */
-static int
-create_pidfile(const char* path)
+ * 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)
{
- FILE *f;
+ char dpath[PATH_MAX];
+ static bool registered_atexit = false;
- if (register_atexit_handler() == -1)
- return -1;
+ /* Register for cleanup with atexit. */
+ if (!registered_atexit) {
+ if (atexit(pidfile_cleanup) == -1)
+ return -1;
+ registered_atexit = true;
+ }
- if (cleanup_old_pidfile(path) == 0)
- return 0;
+ if (path == NULL || strchr(path, '/') == NULL) {
+ if (pidfile_varrun_path(dpath, sizeof(dpath), NULL) == -1)
+ return -1;
+ path = dpath;
+ }
- pidfile_path = strdup(path);
- if (pidfile_path == NULL)
- return -1;
+ /* 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;
- if ((f = fopen(path, "w")) == NULL) {
- free(pidfile_path);
- pidfile_path = NULL;
- return -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;
}