/*
* Copyright (c) 2001-2002 Packet Design, LLC.
* All rights reserved.
*
* Subject to the following obligations and disclaimer of warranty,
* use and redistribution of this software, in source or object code
* forms, with or without modifications are expressly permitted by
* Packet Design; provided, however, that:
*
* (i) Any and all reproductions of the source or object code
* must include the copyright notice above and the following
* disclaimer of warranties; and
* (ii) No rights are granted, in any manner or form, to use
* Packet Design trademarks, including the mark "PACKET DESIGN"
* on advertising, endorsements, or otherwise except as such
* appears in the above copyright notice or in the software.
*
* THIS SOFTWARE IS BEING PROVIDED BY PACKET DESIGN "AS IS", AND
* TO THE MAXIMUM EXTENT PERMITTED BY LAW, PACKET DESIGN MAKES NO
* REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING
* THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
* OR NON-INFRINGEMENT. PACKET DESIGN DOES NOT WARRANT, GUARANTEE,
* OR MAKE ANY REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS
* OF THE USE OF THIS SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY,
* RELIABILITY OR OTHERWISE. IN NO EVENT SHALL PACKET DESIGN BE
* LIABLE FOR ANY DAMAGES RESULTING FROM OR ARISING OUT OF ANY USE
* OF THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, PUNITIVE, OR CONSEQUENTIAL
* DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, LOSS OF
* USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER 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 PACKET DESIGN IS ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*
* Author: Archie Cobbs <archie@freebsd.org>
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <stdio.h>
#include <assert.h>
#include <signal.h>
#include <errno.h>
#include <syslog.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdarg.h>
#include "structs/structs.h"
#include "structs/type/array.h"
#include "util/typed_mem.h"
#include "config/app_config.h"
#include "sys/alog.h"
/************************************************************************
PIDFILE SUBSYSTEM
************************************************************************/
static app_ss_startup_t app_pidfile_start;
static app_ss_shutdown_t app_pidfile_stop;
static app_ss_willrun_t app_pidfile_willrun;
static app_ss_changed_t app_pidfile_changed;
static int app_pidfile_fd = -1;
static int app_pidfile_pid;
const struct app_subsystem app_config_pidfile_subsystem = {
"pidfile",
NULL,
app_pidfile_start,
app_pidfile_stop,
app_pidfile_willrun,
app_pidfile_changed,
NULL
};
/*
* PID file startup
*/
static int
app_pidfile_start(struct app_config_ctx *ctx,
const struct app_subsystem *ss, const void *config)
{
const char *const name = ss->arg;
char *path;
char buf[32];
/* Get PID file name */
if ((path = structs_get_string(app_config_get_type(ctx),
name, config, TYPED_MEM_TEMP)) == NULL) {
alogf(LOG_ERR, "%s: %m", "structs_get_string");
return (-1);
}
/* Create new PID file, or reset old one */
if (app_pidfile_fd != -1) {
alog(LOG_DEBUG, "rewriting pidfile \"%s\"", path);
(void)ftruncate(app_pidfile_fd, 0);
} else {
#ifdef O_EXLOCK
const int flags = O_CREAT|O_WRONLY|O_EXLOCK|O_NONBLOCK;
#else
const int flags = O_CREAT|O_WRONLY|O_NONBLOCK;
#endif
/* Open file */
alog(LOG_DEBUG, "creating pidfile \"%s\"", path);
if ((app_pidfile_fd = open(path, flags, 0644)) == -1)
goto failed;
#ifdef O_EXLOCK
goto got_lock;
#else
{
struct flock f;
memset(&f, 0, sizeof(f));
f.l_pid = getpid();
f.l_type = F_WRLCK;
f.l_whence = SEEK_SET;
if (fcntl(app_pidfile_fd, F_SETLK, &f) != -1)
goto got_lock;
}
#endif
failed:
/* Failed to open and lock pidfile */
switch (errno) {
case EWOULDBLOCK:
alog(LOG_ERR, "%s: file locked: exiting", path);
(void)kill(getpid(), SIGTERM);
FREE(TYPED_MEM_TEMP, path);
return (-1);
default:
alog(LOG_ERR, "%s: %m", path);
FREE(TYPED_MEM_TEMP, path);
return (-1);
}
got_lock:
(void)fcntl(app_pidfile_fd, F_SETFD, 1);
if (ftruncate(app_pidfile_fd, 0) == -1) {
alog(LOG_ERR, "%s: can't truncate: %m", path);
FREE(TYPED_MEM_TEMP, path);
return (-1);
}
}
/* Write PID into file */
snprintf(buf, sizeof(buf), "%d\n", getpid());
if (write(app_pidfile_fd, buf, strlen(buf)) != strlen(buf)) {
alog(LOG_ERR, "%s: write error: %m", path);
(void)close(app_pidfile_fd);
app_pidfile_fd = -1;
FREE(TYPED_MEM_TEMP, path);
return (-1);
}
/* Leave file open so it stays locked */
FREE(TYPED_MEM_TEMP, path);
app_pidfile_pid = getpid();
return (0);
}
/*
* PID file shutdown
*/
static void
app_pidfile_stop(struct app_config_ctx *ctx,
const struct app_subsystem *ss, const void *config)
{
const char *const name = ss->arg;
char *path;
/* Get PID file name */
if ((path = structs_get_string(app_config_get_type(ctx),
name, config, TYPED_MEM_TEMP)) == NULL) {
alogf(LOG_ERR, "%s: %m", "structs_get_string");
return;
}
/* Remove old PID file */
alog(LOG_DEBUG, "removing pidfile \"%s\"", path);
(void)unlink(path);
(void)close(app_pidfile_fd);
app_pidfile_fd = -1;
app_pidfile_pid = -1;
FREE(TYPED_MEM_TEMP, path);
}
/*
* PID file necessity check
*/
static int
app_pidfile_willrun(struct app_config_ctx *ctx,
const struct app_subsystem *ss, const void *config)
{
const char *const name = ss->arg;
char *path;
int r;
/* Get PID file name */
assert(name != NULL);
if ((path = structs_get_string(app_config_get_type(ctx),
name, config, TYPED_MEM_TEMP)) == NULL) {
alogf(LOG_ERR, "%s: %m", "structs_get_string");
return (-1);
}
/* Check if not NULL */
r = (*path != '\0');
FREE(TYPED_MEM_TEMP, path);
return (r);
}
/*
* PID file configuration changed check
*/
static int
app_pidfile_changed(struct app_config_ctx *ctx,
const struct app_subsystem *ss, const void *config1,
const void *config2)
{
const char *const name = ss->arg;
int equal;
/* Compare PID file names as well as PID itself */
if ((equal = structs_equal(app_config_get_type(ctx),
name, config1, config2)) == -1) {
alogf(LOG_ERR, "%s: %m", "structs_equal");
return (-1);
}
return (!equal || getpid() != app_pidfile_pid);
}
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>