mirror of
https://gitlab.freedesktop.org/libbsd/libbsd.git
synced 2025-01-24 02:51:43 +01:00
Add pidfile functions
Taken from FreeBSD. Remove MAXPATHLEN from ‘struct pidfh’ and allocate pf_path dynamically, as some systems do not have such limits. Use dev_t instead of __dev_t. Replace EDOOFUS with EINVAL.
This commit is contained in:
parent
98a2479f0b
commit
2a81893cc0
2
Makefile
2
Makefile
@ -31,6 +31,7 @@ LIB_SRCS := \
|
||||
dehumanize_number.c \
|
||||
inet_net_pton.c \
|
||||
hash/md5.c \
|
||||
pidfile.c \
|
||||
readpassphrase.c \
|
||||
setmode.c \
|
||||
strmode.c \
|
||||
@ -82,6 +83,7 @@ LIB_MANS := \
|
||||
humanize_number.3 \
|
||||
fmtcheck.3 \
|
||||
nlist.3 \
|
||||
pidfile.3 \
|
||||
setmode.3 \
|
||||
getmode.3 \
|
||||
strmode.3 \
|
||||
|
5
Versions
5
Versions
@ -53,5 +53,10 @@ LIBBSD_0.2 {
|
||||
readpassphrase;
|
||||
|
||||
flopen;
|
||||
|
||||
pidfile_open;
|
||||
pidfile_write;
|
||||
pidfile_close;
|
||||
pidfile_remove;
|
||||
} LIBBSD_0.1;
|
||||
|
||||
|
@ -42,12 +42,24 @@
|
||||
#include <features.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
/* for pidfile.c */
|
||||
struct pidfh {
|
||||
int pf_fd;
|
||||
char *pf_path;
|
||||
dev_t pf_dev;
|
||||
ino_t pf_ino;
|
||||
};
|
||||
|
||||
__BEGIN_DECLS
|
||||
int humanize_number(char *buf, size_t len, int64_t bytes,
|
||||
const char *suffix, int scale, int flags);
|
||||
|
||||
int flopen(const char *_path, int _flags, ...);
|
||||
|
||||
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);
|
||||
__END_DECLS
|
||||
|
||||
/* humanize_number(3) */
|
||||
|
254
src/pidfile.3
Normal file
254
src/pidfile.3
Normal file
@ -0,0 +1,254 @@
|
||||
.\" 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 October 20, 2008
|
||||
.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
|
||||
.ds str-Lb-libbsd Utility functions from BSD systems (libbsd, \-lbsd)
|
||||
.Lb libbsd
|
||||
.Sh SYNOPSIS
|
||||
.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 pidfile
|
||||
family of functions allows daemons to handle PID files.
|
||||
It uses
|
||||
.Xr flopen 3
|
||||
to lock a pidfile and detect already running daemons.
|
||||
.Pp
|
||||
The
|
||||
.Fn pidfile_open
|
||||
function opens (or creates) a file specified by the
|
||||
.Fa path
|
||||
argument and locks it.
|
||||
If a file can not be locked, a PID of an already running daemon is returned in
|
||||
the
|
||||
.Fa pidptr
|
||||
argument (if it is not
|
||||
.Dv NULL ) .
|
||||
The function does not write process' PID into the file here, so it can be
|
||||
used before
|
||||
.Fn fork Ns ing
|
||||
and exit with a proper error message when needed.
|
||||
If the
|
||||
.Fa path
|
||||
argument is
|
||||
.Dv NULL ,
|
||||
.Pa /var/run/ Ns Ao Va progname Ac Ns Pa .pid
|
||||
file will be used.
|
||||
.Pp
|
||||
The
|
||||
.Fn pidfile_write
|
||||
function writes process' PID into a previously opened file.
|
||||
.Pp
|
||||
The
|
||||
.Fn pidfile_close
|
||||
function closes a pidfile.
|
||||
It should be used after daemon
|
||||
.Fn fork Ns s
|
||||
to start a child process.
|
||||
.Pp
|
||||
The
|
||||
.Fn pidfile_remove
|
||||
function closes and removes a pidfile.
|
||||
.Sh RETURN VALUES
|
||||
The
|
||||
.Fn pidfile_open
|
||||
function returns a valid pointer to a
|
||||
.Vt pidfh
|
||||
structure on success, or
|
||||
.Dv NULL
|
||||
if an error occurs.
|
||||
If an error occurs,
|
||||
.Va errno
|
||||
will be set.
|
||||
.Pp
|
||||
.Rv -std pidfile_write pidfile_close pidfile_remove
|
||||
.Sh EXAMPLES
|
||||
The following example shows in which order these functions should be used.
|
||||
Note that it is safe to pass
|
||||
.Dv NULL
|
||||
to
|
||||
.Fn pidfile_write ,
|
||||
.Fn pidfile_remove
|
||||
and
|
||||
.Fn pidfile_close
|
||||
functions.
|
||||
.Bd -literal
|
||||
struct pidfh *pfh;
|
||||
pid_t otherpid, childpid;
|
||||
|
||||
pfh = pidfile_open("/var/run/daemon.pid", 0600, &otherpid);
|
||||
if (pfh == NULL) {
|
||||
if (errno == EEXIST) {
|
||||
errx(EXIT_FAILURE, "Daemon already running, pid: %jd.",
|
||||
(intmax_t)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 %jd started.", (intmax_t)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, meaning that a
|
||||
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.
|
||||
.It Bq Er EAGAIN
|
||||
Some process already holds the lock on the given pidfile, but the file
|
||||
is truncated.
|
||||
Most likely, the existing daemon is writing new PID into
|
||||
the file.
|
||||
.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 ,
|
||||
and
|
||||
.Xr read 2
|
||||
calls.
|
||||
.Pp
|
||||
The
|
||||
.Fn pidfile_write
|
||||
function will fail if:
|
||||
.Bl -tag -width Er
|
||||
.It Bq Er EINVAL
|
||||
Improper 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 ,
|
||||
and
|
||||
.Xr write 2
|
||||
calls.
|
||||
.Pp
|
||||
The
|
||||
.Fn pidfile_close
|
||||
function may fail and set
|
||||
.Va errno
|
||||
for any errors specified for the
|
||||
.Xr close 2
|
||||
and
|
||||
.Xr fstat 2
|
||||
calls.
|
||||
.Pp
|
||||
The
|
||||
.Fn pidfile_remove
|
||||
function will fail if:
|
||||
.Bl -tag -width Er
|
||||
.It Bq Er EINVAL
|
||||
Improper 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 fstat 2 ,
|
||||
.Xr write 2 ,
|
||||
and
|
||||
.Xr unlink 2
|
||||
system calls and the
|
||||
.Xr flopen 3
|
||||
library function.
|
||||
.Sh SEE ALSO
|
||||
.Xr open 2 ,
|
||||
.Xr daemon 3 ,
|
||||
.Xr flopen 3
|
||||
.Sh AUTHORS
|
||||
.An -nosplit
|
||||
The
|
||||
.Nm pidfile
|
||||
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 .
|
255
src/pidfile.c
Normal file
255
src/pidfile.c
Normal file
@ -0,0 +1,255 @@
|
||||
/*-
|
||||
* 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 <time.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;
|
||||
|
||||
if (pfh == NULL || pfh->pf_fd == -1)
|
||||
return (EINVAL);
|
||||
/*
|
||||
* 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 (EINVAL);
|
||||
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);
|
||||
else if (i == 0)
|
||||
return (EAGAIN);
|
||||
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, len, count;
|
||||
struct timespec rqtp;
|
||||
|
||||
pfh = malloc(sizeof(*pfh));
|
||||
if (pfh == NULL)
|
||||
return (NULL);
|
||||
|
||||
if (path == NULL) {
|
||||
len = asprintf(&pfh->pf_path, "/var/run/%s.pid", getprogname());
|
||||
if (len < 0) {
|
||||
free(pfh);
|
||||
return (NULL);
|
||||
}
|
||||
} else
|
||||
pfh->pf_path = strdup(path);
|
||||
|
||||
/*
|
||||
* 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 = flopen(pfh->pf_path,
|
||||
O_WRONLY | O_CREAT | O_TRUNC | O_NONBLOCK, mode);
|
||||
if (fd == -1) {
|
||||
count = 0;
|
||||
rqtp.tv_sec = 0;
|
||||
rqtp.tv_nsec = 5000000;
|
||||
if (errno == EWOULDBLOCK && pidptr != NULL) {
|
||||
again:
|
||||
errno = pidfile_read(pfh->pf_path, pidptr);
|
||||
if (errno == 0)
|
||||
errno = EEXIST;
|
||||
else if (errno == EAGAIN) {
|
||||
if (++count <= 3) {
|
||||
nanosleep(&rqtp, 0);
|
||||
goto again;
|
||||
}
|
||||
}
|
||||
}
|
||||
free(pfh->pf_path);
|
||||
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);
|
||||
free(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)
|
||||
{
|
||||
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 (pwrite(fd, pidstr, strlen(pidstr), 0) != (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->pf_path);
|
||||
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 (close(pfh->pf_fd) == -1) {
|
||||
if (error == 0)
|
||||
error = errno;
|
||||
}
|
||||
if (freeit) {
|
||||
free(pfh->pf_path);
|
||||
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));
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user