summaryrefslogtreecommitdiffstats
path: root/lib/libutil
diff options
context:
space:
mode:
authorpjd <pjd@FreeBSD.org>2005-08-24 17:21:38 +0000
committerpjd <pjd@FreeBSD.org>2005-08-24 17:21:38 +0000
commita5fe3401b9f1cc44c8011e8aa5cdbbe2637084e4 (patch)
treeb6de69447d47fffaa83dd6300aab8b56a9a55567 /lib/libutil
parent473f777e4d124b4ed88d3eb95798889f5570e7f4 (diff)
downloadFreeBSD-src-a5fe3401b9f1cc44c8011e8aa5cdbbe2637084e4.zip
FreeBSD-src-a5fe3401b9f1cc44c8011e8aa5cdbbe2637084e4.tar.gz
Add a family of functions for reliable pidfiles handling.
Idea from: jmg Discussed on: arch@
Diffstat (limited to 'lib/libutil')
-rw-r--r--lib/libutil/Makefile11
-rw-r--r--lib/libutil/libutil.h17
-rw-r--r--lib/libutil/pidfile.3227
-rw-r--r--lib/libutil/pidfile.c246
4 files changed, 498 insertions, 3 deletions
diff --git a/lib/libutil/Makefile b/lib/libutil/Makefile
index 0cab08a..ebbcf55 100644
--- a/lib/libutil/Makefile
+++ b/lib/libutil/Makefile
@@ -8,15 +8,16 @@ CFLAGS+=-DLIBC_SCCS -I${.CURDIR} -I${.CURDIR}/../libc/gen/
CFLAGS+=-DINET6
SRCS= _secure_path.c auth.c fparseln.c humanize_number.c login.c \
login_auth.c login_cap.c login_class.c login_crypt.c login_ok.c \
- login_times.c login_tty.c logout.c logwtmp.c property.c pty.c \
- pw_util.c realhostname.c stub.c \
+ login_times.c login_tty.c logout.c logwtmp.c \
+ pidfile.c property.c pty.c pw_util.c realhostname.c stub.c \
trimdomain.c uucplock.c
INCS= libutil.h login_cap.h
MAN+= login.3 login_auth.3 login_tty.3 logout.3 logwtmp.3 pty.3 \
login_cap.3 login_class.3 login_times.3 login_ok.3 \
_secure_path.3 uucplock.3 property.3 auth.3 realhostname.3 \
- realhostname_sa.3 trimdomain.3 fparseln.3 humanize_number.3
+ realhostname_sa.3 trimdomain.3 fparseln.3 humanize_number.3 \
+ pidfile.3
MAN+= login.conf.5 auth.conf.5
MLINKS+= property.3 properties_read.3 property.3 properties_free.3
MLINKS+= property.3 property_find.3
@@ -39,5 +40,9 @@ MLINKS+=login_ok.3 auth_ttyok.3 login_ok.3 auth_hostok.3 \
MLINKS+=login_auth.3 auth_checknologin.3 login_auth.3 auth_cat.3
MLINKS+=uucplock.3 uu_lock.3 uucplock.3 uu_lock_txfr.3 \
uucplock.3 uu_unlock.3 uucplock.3 uu_lockerr.3
+MLINKS+=pidfile.3 pidfile_open.3 \
+ pidfile.3 pidfile_write.3 \
+ pidfile.3 pidfile_close.3 \
+ pidfile.3 pidfile_remove.3
.include <bsd.lib.mk>
diff --git a/lib/libutil/libutil.h b/lib/libutil/libutil.h
index 3042d0f..961fa1b 100644
--- a/lib/libutil/libutil.h
+++ b/lib/libutil/libutil.h
@@ -49,6 +49,16 @@ typedef struct _property {
char *value;
} *properties;
+#ifdef _SYS_PARAM_H_
+/* for pidfile.c */
+struct pidfh {
+ int pf_fd;
+ char pf_path[MAXPATHLEN + 1];
+ __dev_t pf_dev;
+ ino_t pf_ino;
+};
+#endif
+
/* Avoid pulling in all the include files for no need */
struct termios;
struct winsize;
@@ -102,6 +112,13 @@ struct passwd *pw_scan(const char *_line, int _flags);
const char *pw_tempname(void);
int pw_tmp(int _mfd);
#endif
+
+#ifdef _SYS_PARAM_H_
+struct pidfh *pidfile_open(const char *path, mode_t mode, pid_t *pidptr);
+int pidfile_write(struct pidfh *pfh);
+int pidfile_close(struct pidfh *pfh);
+int pidfile_remove(struct pidfh *pfh);
+#endif
__END_DECLS
#define UU_LOCK_INUSE (1)
diff --git a/lib/libutil/pidfile.3 b/lib/libutil/pidfile.3
new file mode 100644
index 0000000..4ae7067
--- /dev/null
+++ b/lib/libutil/pidfile.3
@@ -0,0 +1,227 @@
+.\" Copyright (c) 2005 Pawel Jakub Dawidek <pjd@FreeBSD.org>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd August 22, 2005
+.Dt PIDFILE 3
+.Os
+.Sh NAME
+.Nm pidfile_open ,
+.Nm pidfile_write ,
+.Nm pidfile_close ,
+.Nm pidfile_remove
+.Nd library for PID files handling
+.Sh LIBRARY
+.Lb libutil
+.Sh SYNOPSIS
+.In sys/param.h
+.In libutil.h
+.Ft struct pidfh *
+.Fn pidfile_open "const char *path" "mode_t mode" "pid_t *pidptr"
+.Ft int
+.Fn pidfile_write "struct pidfh *pfh"
+.Ft int
+.Fn pidfile_close "struct pidfh *pfh"
+.Ft int
+.Fn pidfile_remove "struct pidfh *pfh"
+.Sh DESCRIPTION
+The
+.Nm libpidfile
+library provides functions for daemons to handle file with PID.
+It uses
+.Xr flock 2
+to lock pidfile and detect already running daemons.
+.Pp
+The
+.Fn pidfile_open
+function opens (or creates) a file specified by
+.Fa path
+argument and locks it with
+.Xr flock 2
+syscall.
+If file can not be locked, PID of already running daemon is returned in
+.Fa pidptr
+argument (if it is not NULL).
+The function doesn't write process' PID into the file here, so it can be
+used before fork()ing and exit with proper error message when needed.
+If
+.Fa path
+argument is NULL,
+.Pa /var/run/<progname>.pid
+file will be used.
+.Pp
+The
+.Fn pidfile_write
+function write process' PID into previously opened file.
+.Pp
+The
+.Fn pidfile_close
+function closes pidfile.
+It should be used after daemon fork()s to start a child process.
+.Pp
+The
+.Fn pidfile_remove
+function closes and removes pidfile.
+.Sh RETURN VALUES
+The
+.Fn pidfile_open
+function return a valid pointer to a pidfh structure on success or
+.Dv NULL
+if an error occurs.
+If an error does occur,
+.Va errno
+will be set.
+.Rv -std pidfile_write pidfile_close pidfile_remove
+.Sh EXAMPLES
+The following example shows in which order those functions should be used.
+.Bd -literal
+struct pidfh *pfh;
+pid_t otherpid, childpid;
+
+pfh = pidfile_open("/var/run/daemon.pid", 0644, &otherpid);
+if (pfh == NULL) {
+ if (errno == EEXIST)
+ errx(EXIT_FAILURE, "Daemon already running, pid: %d.", otherpid);
+ /* If we cannot create pidfile from other reasons, only warn. */
+ warn("Cannot open or create pidfile");
+}
+
+if (daemon(0, 0) == -1) {
+ warn("Cannot daemonize");
+ pidfile_remove(pfh);
+ exit(EXIT_FAILURE);
+}
+
+pidfile_write(pfh);
+
+for (;;) {
+ /* Do work. */
+ childpid = fork();
+ switch (childpid) {
+ case -1:
+ syslog(LOG_ERR, "Cannot fork(): %s.", strerror(errno));
+ break;
+ case 0:
+ pidfile_close(pfh);
+ /* Do child work. */
+ break;
+ default:
+ syslog(LOG_INFO, "Child %d started.", childpid);
+ break;
+ }
+}
+
+pidfile_remove(pfh);
+exit(EXIT_SUCCESS);
+.Ed
+.Sh ERRORS
+The
+.Fn pidfile_open
+function will fail if:
+.Bl -tag -width Er
+.It Bq Er EEXIST
+Some process already holds the lock on the given pidfile, which means,
+daemon is already running.
+.It Bq Er ENAMETOOLONG
+Specified pidfile's name is too long.
+.It Bq Er EINVAL
+Some process already holds the lock on the given pidfile, but PID read
+from there is invalid.
+.El
+.Pp
+The
+.Fn pidfile_open
+function may also fail and set
+.Va errno
+for any errors specified for the
+.Xr fstat 2 ,
+.Xr open 2 ,
+.Xr read 2
+routines.
+.Pp
+The
+.Fn pidfile_write
+function will fail if:
+.Bl -tag -width Er
+.It Bq Er EDOOFUS
+Inproper function use.
+Probably called before
+.Fn pidfile_open .
+.El
+.Pp
+The
+.Fn pidfile_write
+function may also fail and set
+.Va errno
+for any errors specified for the
+.Xr fstat 2 ,
+.Xr ftruncate 2 ,
+.Xr write 2
+routines.
+.Pp
+The
+.Fn pidfile_close
+function may fail and set
+.Va errno
+for any errors specified for the
+.Xr close 2 ,
+.Xr fstat 2
+routines.
+.Pp
+The
+.Fn pidfile_remove
+function will fail if:
+.Bl -tag -width Er
+.It Bq Er EDOOFUS
+Inproper function use.
+Probably called not from the process which made
+.Fn pidfile_write .
+.El
+.Pp
+The
+.Fn pidfile_remove
+function may also fail and set
+.Va errno
+for any errors specified for the
+.Xr close 2 ,
+.Xr flock 2 ,
+.Xr fstat 2 ,
+.Xr write 2 ,
+.Xr unlink 2
+routines.
+.Pp
+.Sh SEE ALSO
+.Xr flock 2 ,
+.Xr open 2 ,
+.Xr daemon 3
+.Sh AUTHORS
+.An -nosplit
+The
+.Xr pidfile 3
+functionality is based on ideas from
+.An John-Mark Gurney Aq jmg@FreeBSD.org .
+.Pp
+The code and manual page was written by
+.An Pawel Jakub Dawidek Aq pjd@FreeBSD.org .
diff --git a/lib/libutil/pidfile.c b/lib/libutil/pidfile.c
new file mode 100644
index 0000000..0b8ade1
--- /dev/null
+++ b/lib/libutil/pidfile.c
@@ -0,0 +1,246 @@
+/*-
+ * Copyright (c) 2005 Pawel Jakub Dawidek <pjd@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <err.h>
+#include <errno.h>
+#include <libutil.h>
+
+static int _pidfile_remove(struct pidfh *pfh, int freeit);
+
+static int
+pidfile_verify(struct pidfh *pfh)
+{
+ struct stat sb;
+ int fd;
+
+ if (pfh == NULL || pfh->pf_fd == -1)
+ return (EDOOFUS);
+ /*
+ * Check remembered descriptor.
+ */
+ if (fstat(pfh->pf_fd, &sb) == -1)
+ return (errno);
+ if (sb.st_dev != pfh->pf_dev || sb.st_ino != pfh->pf_ino)
+ return (EDOOFUS);
+ return (0);
+}
+
+static int
+pidfile_read(const char *path, pid_t *pidptr)
+{
+ char buf[16], *endptr;
+ int error, fd, i;
+
+ fd = open(path, O_RDONLY);
+ if (fd == -1)
+ return (errno);
+
+ i = read(fd, buf, sizeof(buf) - 1);
+ error = errno; /* Remember errno in case close() wants to change it. */
+ close(fd);
+ if (i == -1)
+ return (error);
+ buf[i] = '\0';
+
+ *pidptr = strtol(buf, &endptr, 10);
+ if (endptr != &buf[i])
+ return (EINVAL);
+
+ return (0);
+}
+
+struct pidfh *
+pidfile_open(const char *path, mode_t mode, pid_t *pidptr)
+{
+ struct pidfh *pfh;
+ struct stat sb;
+ int error, fd;
+
+ pfh = malloc(sizeof(*pfh));
+ if (pfh == NULL)
+ return (NULL);
+
+ if (path == NULL) {
+ snprintf(pfh->pf_path, sizeof(pfh->pf_path), "/var/run/%s.pid",
+ getprogname());
+ } else {
+ strlcpy(pfh->pf_path, path, sizeof(pfh->pf_path));
+ }
+ if (strlen(pfh->pf_path) == sizeof(pfh->pf_path) - 1) {
+ free(pfh);
+ errno = ENAMETOOLONG;
+ return (NULL);
+ }
+
+ /*
+ * Open the PID file and obtain exclusive lock.
+ * We truncate PID file here only to remove old PID immediatelly,
+ * PID file will be truncated again in pidfile_write(), so
+ * pidfile_write() can be called multiple times.
+ */
+ fd = open(pfh->pf_path,
+ O_WRONLY | O_CREAT | O_EXLOCK | O_TRUNC | O_NONBLOCK, mode);
+ if (fd == -1) {
+ if (errno == EWOULDBLOCK && pidptr != NULL) {
+ errno = pidfile_read(pfh->pf_path, pidptr);
+ if (errno == 0)
+ errno = EEXIST;
+ }
+ free(pfh);
+ return (NULL);
+ }
+ /*
+ * Remember file information, so in pidfile_write() we are sure we write
+ * to the proper descriptor.
+ */
+ if (fstat(fd, &sb) == -1) {
+ error = errno;
+ unlink(pfh->pf_path);
+ close(fd);
+ free(pfh);
+ errno = error;
+ return (NULL);
+ }
+
+ pfh->pf_fd = fd;
+ pfh->pf_dev = sb.st_dev;
+ pfh->pf_ino = sb.st_ino;
+
+ return (pfh);
+}
+
+int
+pidfile_write(struct pidfh *pfh)
+{
+ struct stat sb;
+ char pidstr[16];
+ int error, fd;
+
+ /*
+ * Check remembered descriptor, so we don't overwrite some other
+ * file if pidfile was closed and descriptor reused.
+ */
+ errno = pidfile_verify(pfh);
+ if (errno != 0) {
+ /*
+ * Don't close descriptor, because we are not sure if it's ours.
+ */
+ return (-1);
+ }
+ fd = pfh->pf_fd;
+
+ /*
+ * Truncate PID file, so multiple calls of pidfile_write() are allowed.
+ */
+ if (ftruncate(fd, 0) == -1) {
+ error = errno;
+ _pidfile_remove(pfh, 0);
+ errno = error;
+ return (-1);
+ }
+
+ snprintf(pidstr, sizeof(pidstr), "%u", getpid());
+ if (write(fd, pidstr, strlen(pidstr)) != (ssize_t)strlen(pidstr)) {
+ error = errno;
+ _pidfile_remove(pfh, 0);
+ errno = error;
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+pidfile_close(struct pidfh *pfh)
+{
+ int error;
+
+ error = pidfile_verify(pfh);
+ if (error != 0) {
+ errno = error;
+ return (-1);
+ }
+
+ if (close(pfh->pf_fd) == -1)
+ error = errno;
+ free(pfh);
+ if (error != 0) {
+ errno = error;
+ return (-1);
+ }
+ return (0);
+}
+
+static int
+_pidfile_remove(struct pidfh *pfh, int freeit)
+{
+ int error;
+
+ error = pidfile_verify(pfh);
+ if (error != 0) {
+ errno = error;
+ return (-1);
+ }
+
+ if (unlink(pfh->pf_path) == -1)
+ error = errno;
+ if (flock(pfh->pf_fd, LOCK_UN) == -1) {
+ if (error == 0)
+ error = errno;
+ }
+ if (close(pfh->pf_fd) == -1) {
+ if (error == 0)
+ error = errno;
+ }
+ if (freeit)
+ free(pfh);
+ else
+ pfh->pf_fd = -1;
+ if (error != 0) {
+ errno = error;
+ return (-1);
+ }
+ return (0);
+}
+
+int
+pidfile_remove(struct pidfh *pfh)
+{
+
+ return (_pidfile_remove(pfh, 1));
+}
OpenPOWER on IntegriCloud