2014-01-21 21:30:03 +00:00
|
|
|
/*
|
2020-10-09 03:10:13 +00:00
|
|
|
* This file is part of the ZFS Event Daemon (ZED).
|
|
|
|
*
|
2014-01-21 21:30:03 +00:00
|
|
|
* Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049).
|
|
|
|
* Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC.
|
2015-05-06 22:56:03 +00:00
|
|
|
* Refer to the ZoL git commit log for authoritative copyright attribution.
|
|
|
|
*
|
|
|
|
* The contents of this file are subject to the terms of the
|
|
|
|
* Common Development and Distribution License Version 1.0 (CDDL-1.0).
|
|
|
|
* You can obtain a copy of the license from the top-level file
|
|
|
|
* "OPENSOLARIS.LICENSE" or at <http://opensource.org/licenses/CDDL-1.0>.
|
|
|
|
* You may not use this file except in compliance with the license.
|
2014-01-21 21:30:03 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <assert.h>
|
|
|
|
#include <ctype.h>
|
|
|
|
#include <dirent.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <fcntl.h>
|
2014-04-08 22:31:36 +00:00
|
|
|
#include <libgen.h>
|
2014-01-21 21:30:03 +00:00
|
|
|
#include <limits.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <sys/uio.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include "zed.h"
|
|
|
|
#include "zed_conf.h"
|
|
|
|
#include "zed_file.h"
|
|
|
|
#include "zed_log.h"
|
|
|
|
#include "zed_strings.h"
|
|
|
|
|
|
|
|
/*
|
2021-04-07 13:38:22 +00:00
|
|
|
* Initialise the configuration with default values.
|
2014-01-21 21:30:03 +00:00
|
|
|
*/
|
2021-04-07 13:38:22 +00:00
|
|
|
void
|
|
|
|
zed_conf_init(struct zed_conf *zcp)
|
2014-01-21 21:30:03 +00:00
|
|
|
{
|
2021-04-07 13:38:22 +00:00
|
|
|
memset(zcp, 0, sizeof (*zcp));
|
2014-01-21 21:30:03 +00:00
|
|
|
|
2021-04-07 13:38:22 +00:00
|
|
|
/* zcp->zfs_hdl opened in zed_event_init() */
|
|
|
|
/* zcp->zedlets created in zed_conf_scan_dir() */
|
2014-01-21 21:30:03 +00:00
|
|
|
|
2021-04-07 13:38:22 +00:00
|
|
|
zcp->pid_fd = -1; /* opened in zed_conf_write_pid() */
|
|
|
|
zcp->state_fd = -1; /* opened in zed_conf_open_state() */
|
|
|
|
zcp->zevent_fd = -1; /* opened in zed_event_init() */
|
2014-01-21 21:30:03 +00:00
|
|
|
|
2021-04-07 13:38:22 +00:00
|
|
|
zcp->max_jobs = 16;
|
2014-01-21 21:30:03 +00:00
|
|
|
|
2021-04-07 13:38:22 +00:00
|
|
|
if (!(zcp->pid_file = strdup(ZED_PID_FILE)) ||
|
|
|
|
!(zcp->zedlet_dir = strdup(ZED_ZEDLET_DIR)) ||
|
|
|
|
!(zcp->state_file = strdup(ZED_STATE_FILE)))
|
|
|
|
zed_log_die("Failed to create conf: %s", strerror(errno));
|
2014-01-21 21:30:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Destroy the configuration [zcp].
|
2014-09-10 21:22:39 +00:00
|
|
|
*
|
2014-01-21 21:30:03 +00:00
|
|
|
* Note: zfs_hdl & zevent_fd are destroyed via zed_event_fini().
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
zed_conf_destroy(struct zed_conf *zcp)
|
|
|
|
{
|
|
|
|
if (zcp->state_fd >= 0) {
|
|
|
|
if (close(zcp->state_fd) < 0)
|
|
|
|
zed_log_msg(LOG_WARNING,
|
|
|
|
"Failed to close state file \"%s\": %s",
|
|
|
|
zcp->state_file, strerror(errno));
|
2014-10-01 21:56:52 +00:00
|
|
|
zcp->state_fd = -1;
|
2014-01-21 21:30:03 +00:00
|
|
|
}
|
|
|
|
if (zcp->pid_file) {
|
|
|
|
if ((unlink(zcp->pid_file) < 0) && (errno != ENOENT))
|
|
|
|
zed_log_msg(LOG_WARNING,
|
2014-08-28 21:39:48 +00:00
|
|
|
"Failed to remove PID file \"%s\": %s",
|
2014-01-21 21:30:03 +00:00
|
|
|
zcp->pid_file, strerror(errno));
|
|
|
|
}
|
2014-10-01 21:56:07 +00:00
|
|
|
if (zcp->pid_fd >= 0) {
|
|
|
|
if (close(zcp->pid_fd) < 0)
|
|
|
|
zed_log_msg(LOG_WARNING,
|
|
|
|
"Failed to close PID file \"%s\": %s",
|
|
|
|
zcp->pid_file, strerror(errno));
|
|
|
|
zcp->pid_fd = -1;
|
|
|
|
}
|
2014-10-01 21:56:52 +00:00
|
|
|
if (zcp->pid_file) {
|
2014-01-21 21:30:03 +00:00
|
|
|
free(zcp->pid_file);
|
2014-10-01 21:56:52 +00:00
|
|
|
zcp->pid_file = NULL;
|
|
|
|
}
|
|
|
|
if (zcp->zedlet_dir) {
|
2014-09-19 18:10:28 +00:00
|
|
|
free(zcp->zedlet_dir);
|
2014-10-01 21:56:52 +00:00
|
|
|
zcp->zedlet_dir = NULL;
|
|
|
|
}
|
|
|
|
if (zcp->state_file) {
|
2014-01-21 21:30:03 +00:00
|
|
|
free(zcp->state_file);
|
2014-10-01 21:56:52 +00:00
|
|
|
zcp->state_file = NULL;
|
|
|
|
}
|
|
|
|
if (zcp->zedlets) {
|
2014-09-19 18:10:28 +00:00
|
|
|
zed_strings_destroy(zcp->zedlets);
|
2014-10-01 21:56:52 +00:00
|
|
|
zcp->zedlets = NULL;
|
|
|
|
}
|
2014-01-21 21:30:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Display command-line help and exit.
|
2014-09-10 21:22:39 +00:00
|
|
|
*
|
2014-01-21 21:30:03 +00:00
|
|
|
* If [got_err] is 0, output to stdout and exit normally;
|
2014-09-10 21:22:39 +00:00
|
|
|
* otherwise, output to stderr and exit with a failure status.
|
2014-01-21 21:30:03 +00:00
|
|
|
*/
|
|
|
|
static void
|
2021-04-07 13:27:55 +00:00
|
|
|
_zed_conf_display_help(const char *prog, boolean_t got_err)
|
2014-01-21 21:30:03 +00:00
|
|
|
{
|
2021-04-07 13:20:22 +00:00
|
|
|
struct opt { const char *o, *d, *v; };
|
|
|
|
|
2014-01-21 21:30:03 +00:00
|
|
|
FILE *fp = got_err ? stderr : stdout;
|
2021-04-07 13:20:22 +00:00
|
|
|
|
|
|
|
struct opt *oo;
|
|
|
|
struct opt iopts[] = {
|
|
|
|
{ .o = "-h", .d = "Display help" },
|
|
|
|
{ .o = "-L", .d = "Display license information" },
|
|
|
|
{ .o = "-V", .d = "Display version information" },
|
|
|
|
{},
|
|
|
|
};
|
|
|
|
struct opt nopts[] = {
|
|
|
|
{ .o = "-v", .d = "Be verbose" },
|
|
|
|
{ .o = "-f", .d = "Force daemon to run" },
|
|
|
|
{ .o = "-F", .d = "Run daemon in the foreground" },
|
|
|
|
{ .o = "-I",
|
|
|
|
.d = "Idle daemon until kernel module is (re)loaded" },
|
|
|
|
{ .o = "-M", .d = "Lock all pages in memory" },
|
|
|
|
{ .o = "-P", .d = "$PATH for ZED to use (only used by ZTS)" },
|
|
|
|
{ .o = "-Z", .d = "Zero state file" },
|
|
|
|
{},
|
|
|
|
};
|
|
|
|
struct opt vopts[] = {
|
|
|
|
{ .o = "-d DIR", .d = "Read enabled ZEDLETs from DIR.",
|
|
|
|
.v = ZED_ZEDLET_DIR },
|
|
|
|
{ .o = "-p FILE", .d = "Write daemon's PID to FILE.",
|
|
|
|
.v = ZED_PID_FILE },
|
|
|
|
{ .o = "-s FILE", .d = "Write daemon's state to FILE.",
|
|
|
|
.v = ZED_STATE_FILE },
|
|
|
|
{ .o = "-j JOBS", .d = "Start at most JOBS at once.",
|
|
|
|
.v = "16" },
|
|
|
|
{},
|
|
|
|
};
|
2014-01-21 21:30:03 +00:00
|
|
|
|
|
|
|
fprintf(fp, "Usage: %s [OPTION]...\n", (prog ? prog : "zed"));
|
|
|
|
fprintf(fp, "\n");
|
2021-04-07 13:20:22 +00:00
|
|
|
for (oo = iopts; oo->o; ++oo)
|
|
|
|
fprintf(fp, " %*s %s\n", -8, oo->o, oo->d);
|
2014-01-21 21:30:03 +00:00
|
|
|
fprintf(fp, "\n");
|
2021-04-07 13:20:22 +00:00
|
|
|
for (oo = nopts; oo->o; ++oo)
|
|
|
|
fprintf(fp, " %*s %s\n", -8, oo->o, oo->d);
|
2014-01-21 21:30:03 +00:00
|
|
|
fprintf(fp, "\n");
|
2021-04-07 13:20:22 +00:00
|
|
|
for (oo = vopts; oo->o; ++oo)
|
|
|
|
fprintf(fp, " %*s %s [%s]\n", -8, oo->o, oo->d, oo->v);
|
2014-01-21 21:30:03 +00:00
|
|
|
fprintf(fp, "\n");
|
|
|
|
|
|
|
|
exit(got_err ? EXIT_FAILURE : EXIT_SUCCESS);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Display license information to stdout and exit.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
_zed_conf_display_license(void)
|
|
|
|
{
|
2021-04-07 12:52:58 +00:00
|
|
|
printf(
|
|
|
|
"The ZFS Event Daemon (ZED) is distributed under the terms of the\n"
|
|
|
|
" Common Development and Distribution License (CDDL-1.0)\n"
|
|
|
|
" <http://opensource.org/licenses/CDDL-1.0>.\n"
|
|
|
|
"\n"
|
2014-01-21 21:30:03 +00:00
|
|
|
"Developed at Lawrence Livermore National Laboratory"
|
2021-04-07 12:52:58 +00:00
|
|
|
" (LLNL-CODE-403049).\n"
|
|
|
|
"\n");
|
2014-01-21 21:30:03 +00:00
|
|
|
|
|
|
|
exit(EXIT_SUCCESS);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Display version information to stdout and exit.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
_zed_conf_display_version(void)
|
|
|
|
{
|
|
|
|
printf("%s-%s-%s\n",
|
|
|
|
ZFS_META_NAME, ZFS_META_VERSION, ZFS_META_RELEASE);
|
|
|
|
|
|
|
|
exit(EXIT_SUCCESS);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Copy the [path] string to the [resultp] ptr.
|
|
|
|
* If [path] is not an absolute path, prefix it with the current working dir.
|
|
|
|
* If [resultp] is non-null, free its existing string before assignment.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
_zed_conf_parse_path(char **resultp, const char *path)
|
|
|
|
{
|
|
|
|
char buf[PATH_MAX];
|
|
|
|
|
|
|
|
assert(resultp != NULL);
|
|
|
|
assert(path != NULL);
|
|
|
|
|
|
|
|
if (*resultp)
|
|
|
|
free(*resultp);
|
|
|
|
|
|
|
|
if (path[0] == '/') {
|
|
|
|
*resultp = strdup(path);
|
|
|
|
} else {
|
2021-04-07 14:17:44 +00:00
|
|
|
if (!getcwd(buf, sizeof (buf)))
|
|
|
|
zed_log_die("Failed to get current working dir: %s",
|
|
|
|
strerror(errno));
|
|
|
|
|
|
|
|
if (strlcat(buf, "/", sizeof (buf)) >= sizeof (buf) ||
|
|
|
|
strlcat(buf, path, sizeof (buf)) >= sizeof (buf))
|
|
|
|
zed_log_die("Failed to copy path: %s",
|
|
|
|
strerror(ENAMETOOLONG));
|
|
|
|
|
2014-01-21 21:30:03 +00:00
|
|
|
*resultp = strdup(buf);
|
|
|
|
}
|
2021-04-07 14:17:44 +00:00
|
|
|
|
2014-01-21 21:30:03 +00:00
|
|
|
if (!*resultp)
|
|
|
|
zed_log_die("Failed to copy path: %s", strerror(ENOMEM));
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Parse the command-line options into the configuration [zcp].
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
zed_conf_parse_opts(struct zed_conf *zcp, int argc, char **argv)
|
|
|
|
{
|
2021-04-02 13:57:23 +00:00
|
|
|
const char * const opts = ":hLVd:p:P:s:vfFMZIj:";
|
2014-01-21 21:30:03 +00:00
|
|
|
int opt;
|
2021-03-29 13:21:54 +00:00
|
|
|
unsigned long raw;
|
2014-01-21 21:30:03 +00:00
|
|
|
|
|
|
|
if (!zcp || !argv || !argv[0])
|
|
|
|
zed_log_die("Failed to parse options: Internal error");
|
|
|
|
|
|
|
|
opterr = 0; /* suppress default getopt err msgs */
|
|
|
|
|
|
|
|
while ((opt = getopt(argc, argv, opts)) != -1) {
|
|
|
|
switch (opt) {
|
|
|
|
case 'h':
|
2021-04-07 13:27:55 +00:00
|
|
|
_zed_conf_display_help(argv[0], B_FALSE);
|
2014-01-21 21:30:03 +00:00
|
|
|
break;
|
|
|
|
case 'L':
|
|
|
|
_zed_conf_display_license();
|
|
|
|
break;
|
|
|
|
case 'V':
|
|
|
|
_zed_conf_display_version();
|
|
|
|
break;
|
|
|
|
case 'd':
|
2014-09-19 18:10:28 +00:00
|
|
|
_zed_conf_parse_path(&zcp->zedlet_dir, optarg);
|
2014-01-21 21:30:03 +00:00
|
|
|
break;
|
zed additional features
This commit adds two features to zed, that macOS desires. The first
is that when you unload the kernel module, zed would enter into a
cpubusy loop calling zfs_events_next() repeatedly. We now look for
ENODEV, returned by kernel, so zed can exit gracefully.
Second feature is -I (idle) (alas -P persist was taken) is for the
deamon to;
1; if started without ZFS kernel module, stick around waiting for it.
2; if kernel module is unloaded, go back to 1.
This is due to daemons in macOS is started by launchctl, and is
expected to stick around.
Currently, the busy loop only exists when errno is ENODEV. This is
to ensure that functionality that upstream expects is not changed.
It did not care about errors before, and it still does not. (with the
exception of ENODEV).
However, it is probably better that all errors
(ERESTART notwithstanding) exits the loop, and the issues complaining
about zed taking all CPU will go away.
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Jorgen Lundman <lundman@lundman.net>
Closes #10476
2020-06-22 16:53:34 +00:00
|
|
|
case 'I':
|
|
|
|
zcp->do_idle = 1;
|
|
|
|
break;
|
2014-01-21 21:30:03 +00:00
|
|
|
case 'p':
|
|
|
|
_zed_conf_parse_path(&zcp->pid_file, optarg);
|
|
|
|
break;
|
2018-02-23 19:38:05 +00:00
|
|
|
case 'P':
|
|
|
|
_zed_conf_parse_path(&zcp->path, optarg);
|
|
|
|
break;
|
2014-01-21 21:30:03 +00:00
|
|
|
case 's':
|
|
|
|
_zed_conf_parse_path(&zcp->state_file, optarg);
|
|
|
|
break;
|
|
|
|
case 'v':
|
|
|
|
zcp->do_verbose = 1;
|
|
|
|
break;
|
|
|
|
case 'f':
|
|
|
|
zcp->do_force = 1;
|
|
|
|
break;
|
|
|
|
case 'F':
|
|
|
|
zcp->do_foreground = 1;
|
|
|
|
break;
|
|
|
|
case 'M':
|
|
|
|
zcp->do_memlock = 1;
|
|
|
|
break;
|
|
|
|
case 'Z':
|
|
|
|
zcp->do_zero = 1;
|
|
|
|
break;
|
2021-03-29 13:21:54 +00:00
|
|
|
case 'j':
|
|
|
|
errno = 0;
|
|
|
|
raw = strtoul(optarg, NULL, 0);
|
|
|
|
if (errno == ERANGE || raw > INT16_MAX) {
|
|
|
|
zed_log_die("%lu is too many jobs", raw);
|
|
|
|
} if (raw == 0) {
|
|
|
|
zed_log_die("0 jobs makes no sense");
|
|
|
|
} else {
|
|
|
|
zcp->max_jobs = raw;
|
|
|
|
}
|
|
|
|
break;
|
2014-01-21 21:30:03 +00:00
|
|
|
case '?':
|
|
|
|
default:
|
|
|
|
if (optopt == '?')
|
2021-04-07 13:27:55 +00:00
|
|
|
_zed_conf_display_help(argv[0], B_FALSE);
|
2014-01-21 21:30:03 +00:00
|
|
|
|
2021-04-07 13:20:22 +00:00
|
|
|
fprintf(stderr, "%s: Invalid option '-%c'\n\n",
|
|
|
|
argv[0], optopt);
|
2021-04-07 13:27:55 +00:00
|
|
|
_zed_conf_display_help(argv[0], B_TRUE);
|
2014-01-21 21:30:03 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2014-09-19 18:10:28 +00:00
|
|
|
* Scan the [zcp] zedlet_dir for files to exec based on the event class.
|
2014-09-10 21:22:39 +00:00
|
|
|
* Files must be executable by user, but not writable by group or other.
|
|
|
|
* Dotfiles are ignored.
|
|
|
|
*
|
2014-09-19 18:10:28 +00:00
|
|
|
* Return 0 on success with an updated set of zedlets,
|
2014-09-10 21:22:39 +00:00
|
|
|
* or -1 on error with errno set.
|
2014-01-21 21:30:03 +00:00
|
|
|
*/
|
|
|
|
int
|
|
|
|
zed_conf_scan_dir(struct zed_conf *zcp)
|
|
|
|
{
|
2014-09-19 18:10:28 +00:00
|
|
|
zed_strings_t *zedlets;
|
2014-01-21 21:30:03 +00:00
|
|
|
DIR *dirp;
|
|
|
|
struct dirent *direntp;
|
|
|
|
char pathname[PATH_MAX];
|
|
|
|
struct stat st;
|
|
|
|
int n;
|
|
|
|
|
|
|
|
if (!zcp) {
|
|
|
|
errno = EINVAL;
|
2014-09-19 18:10:28 +00:00
|
|
|
zed_log_msg(LOG_ERR, "Failed to scan zedlet dir: %s",
|
2014-01-21 21:30:03 +00:00
|
|
|
strerror(errno));
|
|
|
|
return (-1);
|
|
|
|
}
|
2014-09-19 18:10:28 +00:00
|
|
|
zedlets = zed_strings_create();
|
|
|
|
if (!zedlets) {
|
2014-01-21 21:30:03 +00:00
|
|
|
errno = ENOMEM;
|
|
|
|
zed_log_msg(LOG_WARNING, "Failed to scan dir \"%s\": %s",
|
2014-09-19 18:10:28 +00:00
|
|
|
zcp->zedlet_dir, strerror(errno));
|
2014-01-21 21:30:03 +00:00
|
|
|
return (-1);
|
|
|
|
}
|
2014-09-19 18:10:28 +00:00
|
|
|
dirp = opendir(zcp->zedlet_dir);
|
2014-01-21 21:30:03 +00:00
|
|
|
if (!dirp) {
|
|
|
|
int errno_bak = errno;
|
|
|
|
zed_log_msg(LOG_WARNING, "Failed to open dir \"%s\": %s",
|
2014-09-19 18:10:28 +00:00
|
|
|
zcp->zedlet_dir, strerror(errno));
|
|
|
|
zed_strings_destroy(zedlets);
|
2014-01-21 21:30:03 +00:00
|
|
|
errno = errno_bak;
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
while ((direntp = readdir(dirp))) {
|
|
|
|
if (direntp->d_name[0] == '.')
|
|
|
|
continue;
|
|
|
|
|
|
|
|
n = snprintf(pathname, sizeof (pathname),
|
2014-09-19 18:10:28 +00:00
|
|
|
"%s/%s", zcp->zedlet_dir, direntp->d_name);
|
2014-01-21 21:30:03 +00:00
|
|
|
if ((n < 0) || (n >= sizeof (pathname))) {
|
|
|
|
zed_log_msg(LOG_WARNING, "Failed to stat \"%s\": %s",
|
|
|
|
direntp->d_name, strerror(ENAMETOOLONG));
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (stat(pathname, &st) < 0) {
|
|
|
|
zed_log_msg(LOG_WARNING, "Failed to stat \"%s\": %s",
|
|
|
|
pathname, strerror(errno));
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!S_ISREG(st.st_mode)) {
|
|
|
|
zed_log_msg(LOG_INFO,
|
|
|
|
"Ignoring \"%s\": not a regular file",
|
|
|
|
direntp->d_name);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if ((st.st_uid != 0) && !zcp->do_force) {
|
|
|
|
zed_log_msg(LOG_NOTICE,
|
|
|
|
"Ignoring \"%s\": not owned by root",
|
|
|
|
direntp->d_name);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!(st.st_mode & S_IXUSR)) {
|
|
|
|
zed_log_msg(LOG_INFO,
|
|
|
|
"Ignoring \"%s\": not executable by user",
|
|
|
|
direntp->d_name);
|
|
|
|
continue;
|
|
|
|
}
|
2017-10-06 02:38:55 +00:00
|
|
|
if ((st.st_mode & S_IWGRP) && !zcp->do_force) {
|
2014-01-21 21:30:03 +00:00
|
|
|
zed_log_msg(LOG_NOTICE,
|
|
|
|
"Ignoring \"%s\": writable by group",
|
|
|
|
direntp->d_name);
|
|
|
|
continue;
|
|
|
|
}
|
2017-10-06 02:38:55 +00:00
|
|
|
if ((st.st_mode & S_IWOTH) && !zcp->do_force) {
|
2014-01-21 21:30:03 +00:00
|
|
|
zed_log_msg(LOG_NOTICE,
|
|
|
|
"Ignoring \"%s\": writable by other",
|
|
|
|
direntp->d_name);
|
|
|
|
continue;
|
|
|
|
}
|
2014-10-19 19:05:07 +00:00
|
|
|
if (zed_strings_add(zedlets, NULL, direntp->d_name) < 0) {
|
2014-01-21 21:30:03 +00:00
|
|
|
zed_log_msg(LOG_WARNING,
|
|
|
|
"Failed to register \"%s\": %s",
|
|
|
|
direntp->d_name, strerror(errno));
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (zcp->do_verbose)
|
|
|
|
zed_log_msg(LOG_INFO,
|
2014-09-19 18:10:28 +00:00
|
|
|
"Registered zedlet \"%s\"", direntp->d_name);
|
2014-01-21 21:30:03 +00:00
|
|
|
}
|
|
|
|
if (closedir(dirp) < 0) {
|
|
|
|
int errno_bak = errno;
|
|
|
|
zed_log_msg(LOG_WARNING, "Failed to close dir \"%s\": %s",
|
2014-09-19 18:10:28 +00:00
|
|
|
zcp->zedlet_dir, strerror(errno));
|
|
|
|
zed_strings_destroy(zedlets);
|
2014-01-21 21:30:03 +00:00
|
|
|
errno = errno_bak;
|
|
|
|
return (-1);
|
|
|
|
}
|
2014-09-19 18:10:28 +00:00
|
|
|
if (zcp->zedlets)
|
|
|
|
zed_strings_destroy(zcp->zedlets);
|
2014-01-21 21:30:03 +00:00
|
|
|
|
2014-09-19 18:10:28 +00:00
|
|
|
zcp->zedlets = zedlets;
|
2014-01-21 21:30:03 +00:00
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Write the PID file specified in [zcp].
|
|
|
|
* Return 0 on success, -1 on error.
|
2014-09-10 21:22:39 +00:00
|
|
|
*
|
2014-08-27 20:18:01 +00:00
|
|
|
* This must be called after fork()ing to become a daemon (so the correct PID
|
2014-09-10 21:22:39 +00:00
|
|
|
* is recorded), but before daemonization is complete and the parent process
|
|
|
|
* exits (for synchronization with systemd).
|
2014-01-21 21:30:03 +00:00
|
|
|
*/
|
|
|
|
int
|
|
|
|
zed_conf_write_pid(struct zed_conf *zcp)
|
|
|
|
{
|
2014-10-01 21:56:07 +00:00
|
|
|
char buf[PATH_MAX];
|
2014-01-21 21:30:03 +00:00
|
|
|
int n;
|
|
|
|
char *p;
|
|
|
|
mode_t mask;
|
2014-10-01 21:56:07 +00:00
|
|
|
int rv;
|
2014-01-21 21:30:03 +00:00
|
|
|
|
|
|
|
if (!zcp || !zcp->pid_file) {
|
|
|
|
errno = EINVAL;
|
2014-10-01 21:56:07 +00:00
|
|
|
zed_log_msg(LOG_ERR, "Failed to create PID file: %s",
|
2014-01-21 21:30:03 +00:00
|
|
|
strerror(errno));
|
|
|
|
return (-1);
|
|
|
|
}
|
2014-10-01 21:56:07 +00:00
|
|
|
assert(zcp->pid_fd == -1);
|
|
|
|
/*
|
|
|
|
* Create PID file directory if needed.
|
|
|
|
*/
|
|
|
|
n = strlcpy(buf, zcp->pid_file, sizeof (buf));
|
|
|
|
if (n >= sizeof (buf)) {
|
2014-01-21 21:30:03 +00:00
|
|
|
errno = ENAMETOOLONG;
|
2014-10-01 21:56:07 +00:00
|
|
|
zed_log_msg(LOG_ERR, "Failed to create PID file: %s",
|
2014-01-21 21:30:03 +00:00
|
|
|
strerror(errno));
|
2014-10-01 21:56:07 +00:00
|
|
|
goto err;
|
2014-01-21 21:30:03 +00:00
|
|
|
}
|
2014-10-01 21:56:07 +00:00
|
|
|
p = strrchr(buf, '/');
|
2014-01-21 21:30:03 +00:00
|
|
|
if (p)
|
|
|
|
*p = '\0';
|
|
|
|
|
2021-04-12 12:07:14 +00:00
|
|
|
if ((mkdirp(buf, 0755) < 0) && (errno != EEXIST)) {
|
2014-10-01 21:56:07 +00:00
|
|
|
zed_log_msg(LOG_ERR, "Failed to create directory \"%s\": %s",
|
|
|
|
buf, strerror(errno));
|
|
|
|
goto err;
|
2014-04-08 22:31:36 +00:00
|
|
|
}
|
2014-10-01 21:56:07 +00:00
|
|
|
/*
|
|
|
|
* Obtain PID file lock.
|
|
|
|
*/
|
2014-01-21 21:30:03 +00:00
|
|
|
mask = umask(0);
|
|
|
|
umask(mask | 022);
|
2021-04-12 12:07:14 +00:00
|
|
|
zcp->pid_fd = open(zcp->pid_file, O_RDWR | O_CREAT | O_CLOEXEC, 0644);
|
2014-01-21 21:30:03 +00:00
|
|
|
umask(mask);
|
2014-10-01 21:56:07 +00:00
|
|
|
if (zcp->pid_fd < 0) {
|
|
|
|
zed_log_msg(LOG_ERR, "Failed to open PID file \"%s\": %s",
|
|
|
|
zcp->pid_file, strerror(errno));
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
rv = zed_file_lock(zcp->pid_fd);
|
|
|
|
if (rv < 0) {
|
|
|
|
zed_log_msg(LOG_ERR, "Failed to lock PID file \"%s\": %s",
|
2014-01-21 21:30:03 +00:00
|
|
|
zcp->pid_file, strerror(errno));
|
2014-10-01 21:56:07 +00:00
|
|
|
goto err;
|
|
|
|
} else if (rv > 0) {
|
|
|
|
pid_t pid = zed_file_is_locked(zcp->pid_fd);
|
|
|
|
if (pid < 0) {
|
|
|
|
zed_log_msg(LOG_ERR,
|
|
|
|
"Failed to test lock on PID file \"%s\"",
|
|
|
|
zcp->pid_file);
|
|
|
|
} else if (pid > 0) {
|
|
|
|
zed_log_msg(LOG_ERR,
|
|
|
|
"Found PID %d bound to PID file \"%s\"",
|
|
|
|
pid, zcp->pid_file);
|
|
|
|
} else {
|
|
|
|
zed_log_msg(LOG_ERR,
|
|
|
|
"Inconsistent lock state on PID file \"%s\"",
|
|
|
|
zcp->pid_file);
|
|
|
|
}
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Write PID file.
|
|
|
|
*/
|
2016-12-12 18:46:26 +00:00
|
|
|
n = snprintf(buf, sizeof (buf), "%d\n", (int)getpid());
|
2014-10-01 21:56:07 +00:00
|
|
|
if ((n < 0) || (n >= sizeof (buf))) {
|
|
|
|
errno = ERANGE;
|
|
|
|
zed_log_msg(LOG_ERR, "Failed to write PID file \"%s\": %s",
|
|
|
|
zcp->pid_file, strerror(errno));
|
2021-04-02 19:31:23 +00:00
|
|
|
} else if (write(zcp->pid_fd, buf, n) != n) {
|
2014-10-01 21:56:07 +00:00
|
|
|
zed_log_msg(LOG_ERR, "Failed to write PID file \"%s\": %s",
|
2014-01-21 21:30:03 +00:00
|
|
|
zcp->pid_file, strerror(errno));
|
2014-10-01 21:56:07 +00:00
|
|
|
} else if (fdatasync(zcp->pid_fd) < 0) {
|
|
|
|
zed_log_msg(LOG_ERR, "Failed to sync PID file \"%s\": %s",
|
2014-01-21 21:30:03 +00:00
|
|
|
zcp->pid_file, strerror(errno));
|
|
|
|
} else {
|
|
|
|
return (0);
|
|
|
|
}
|
2014-10-01 21:56:07 +00:00
|
|
|
|
|
|
|
err:
|
|
|
|
if (zcp->pid_fd >= 0) {
|
|
|
|
(void) close(zcp->pid_fd);
|
|
|
|
zcp->pid_fd = -1;
|
|
|
|
}
|
2014-01-21 21:30:03 +00:00
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Open and lock the [zcp] state_file.
|
|
|
|
* Return 0 on success, -1 on error.
|
2014-09-10 21:22:39 +00:00
|
|
|
*
|
2014-10-01 21:56:07 +00:00
|
|
|
* FIXME: Move state information into kernel.
|
2014-01-21 21:30:03 +00:00
|
|
|
*/
|
|
|
|
int
|
|
|
|
zed_conf_open_state(struct zed_conf *zcp)
|
|
|
|
{
|
|
|
|
char dirbuf[PATH_MAX];
|
|
|
|
int n;
|
|
|
|
char *p;
|
|
|
|
int rv;
|
|
|
|
|
|
|
|
if (!zcp || !zcp->state_file) {
|
|
|
|
errno = EINVAL;
|
|
|
|
zed_log_msg(LOG_ERR, "Failed to open state file: %s",
|
|
|
|
strerror(errno));
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
n = strlcpy(dirbuf, zcp->state_file, sizeof (dirbuf));
|
|
|
|
if (n >= sizeof (dirbuf)) {
|
|
|
|
errno = ENAMETOOLONG;
|
|
|
|
zed_log_msg(LOG_WARNING, "Failed to open state file: %s",
|
|
|
|
strerror(errno));
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
p = strrchr(dirbuf, '/');
|
|
|
|
if (p)
|
|
|
|
*p = '\0';
|
|
|
|
|
2021-04-12 12:07:14 +00:00
|
|
|
if ((mkdirp(dirbuf, 0755) < 0) && (errno != EEXIST)) {
|
2014-04-08 22:31:36 +00:00
|
|
|
zed_log_msg(LOG_WARNING,
|
|
|
|
"Failed to create directory \"%s\": %s",
|
|
|
|
dirbuf, strerror(errno));
|
2014-01-21 21:30:03 +00:00
|
|
|
return (-1);
|
2014-04-08 22:31:36 +00:00
|
|
|
}
|
2014-01-21 21:30:03 +00:00
|
|
|
if (zcp->state_fd >= 0) {
|
|
|
|
if (close(zcp->state_fd) < 0) {
|
|
|
|
zed_log_msg(LOG_WARNING,
|
|
|
|
"Failed to close state file \"%s\": %s",
|
|
|
|
zcp->state_file, strerror(errno));
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (zcp->do_zero)
|
|
|
|
(void) unlink(zcp->state_file);
|
|
|
|
|
|
|
|
zcp->state_fd = open(zcp->state_file,
|
2021-04-12 12:07:14 +00:00
|
|
|
O_RDWR | O_CREAT | O_CLOEXEC, 0644);
|
2014-01-21 21:30:03 +00:00
|
|
|
if (zcp->state_fd < 0) {
|
|
|
|
zed_log_msg(LOG_WARNING, "Failed to open state file \"%s\": %s",
|
|
|
|
zcp->state_file, strerror(errno));
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
rv = zed_file_lock(zcp->state_fd);
|
|
|
|
if (rv < 0) {
|
|
|
|
zed_log_msg(LOG_WARNING, "Failed to lock state file \"%s\": %s",
|
|
|
|
zcp->state_file, strerror(errno));
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
if (rv > 0) {
|
|
|
|
pid_t pid = zed_file_is_locked(zcp->state_fd);
|
|
|
|
if (pid < 0) {
|
|
|
|
zed_log_msg(LOG_WARNING,
|
|
|
|
"Failed to test lock on state file \"%s\"",
|
|
|
|
zcp->state_file);
|
|
|
|
} else if (pid > 0) {
|
|
|
|
zed_log_msg(LOG_WARNING,
|
2014-08-28 21:39:48 +00:00
|
|
|
"Found PID %d bound to state file \"%s\"",
|
2014-01-21 21:30:03 +00:00
|
|
|
pid, zcp->state_file);
|
|
|
|
} else {
|
|
|
|
zed_log_msg(LOG_WARNING,
|
|
|
|
"Inconsistent lock state on state file \"%s\"",
|
|
|
|
zcp->state_file);
|
|
|
|
}
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2014-09-10 21:22:39 +00:00
|
|
|
* Read the opened [zcp] state_file to obtain the eid & etime of the last event
|
|
|
|
* processed. Write the state from the last event to the [eidp] & [etime] args
|
|
|
|
* passed by reference. Note that etime[] is an array of size 2.
|
2014-01-21 21:30:03 +00:00
|
|
|
* Return 0 on success, -1 on error.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
zed_conf_read_state(struct zed_conf *zcp, uint64_t *eidp, int64_t etime[])
|
|
|
|
{
|
|
|
|
ssize_t len;
|
|
|
|
struct iovec iov[3];
|
|
|
|
ssize_t n;
|
|
|
|
|
|
|
|
if (!zcp || !eidp || !etime) {
|
|
|
|
errno = EINVAL;
|
|
|
|
zed_log_msg(LOG_ERR,
|
|
|
|
"Failed to read state file: %s", strerror(errno));
|
|
|
|
return (-1);
|
|
|
|
}
|
2016-12-12 18:46:26 +00:00
|
|
|
if (lseek(zcp->state_fd, 0, SEEK_SET) == (off_t)-1) {
|
2014-01-21 21:30:03 +00:00
|
|
|
zed_log_msg(LOG_WARNING,
|
|
|
|
"Failed to reposition state file offset: %s",
|
|
|
|
strerror(errno));
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
len = 0;
|
|
|
|
iov[0].iov_base = eidp;
|
|
|
|
len += iov[0].iov_len = sizeof (*eidp);
|
|
|
|
iov[1].iov_base = &etime[0];
|
|
|
|
len += iov[1].iov_len = sizeof (etime[0]);
|
|
|
|
iov[2].iov_base = &etime[1];
|
|
|
|
len += iov[2].iov_len = sizeof (etime[1]);
|
|
|
|
|
|
|
|
n = readv(zcp->state_fd, iov, 3);
|
|
|
|
if (n == 0) {
|
|
|
|
*eidp = 0;
|
|
|
|
} else if (n < 0) {
|
|
|
|
zed_log_msg(LOG_WARNING,
|
|
|
|
"Failed to read state file \"%s\": %s",
|
|
|
|
zcp->state_file, strerror(errno));
|
|
|
|
return (-1);
|
|
|
|
} else if (n != len) {
|
|
|
|
errno = EIO;
|
|
|
|
zed_log_msg(LOG_WARNING,
|
|
|
|
"Failed to read state file \"%s\": Read %d of %d bytes",
|
|
|
|
zcp->state_file, n, len);
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Write the [eid] & [etime] of the last processed event to the opened
|
2014-09-10 21:22:39 +00:00
|
|
|
* [zcp] state_file. Note that etime[] is an array of size 2.
|
2014-01-21 21:30:03 +00:00
|
|
|
* Return 0 on success, -1 on error.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
zed_conf_write_state(struct zed_conf *zcp, uint64_t eid, int64_t etime[])
|
|
|
|
{
|
|
|
|
ssize_t len;
|
|
|
|
struct iovec iov[3];
|
|
|
|
ssize_t n;
|
|
|
|
|
|
|
|
if (!zcp) {
|
|
|
|
errno = EINVAL;
|
|
|
|
zed_log_msg(LOG_ERR,
|
|
|
|
"Failed to write state file: %s", strerror(errno));
|
|
|
|
return (-1);
|
|
|
|
}
|
2016-12-12 18:46:26 +00:00
|
|
|
if (lseek(zcp->state_fd, 0, SEEK_SET) == (off_t)-1) {
|
2014-01-21 21:30:03 +00:00
|
|
|
zed_log_msg(LOG_WARNING,
|
|
|
|
"Failed to reposition state file offset: %s",
|
|
|
|
strerror(errno));
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
len = 0;
|
|
|
|
iov[0].iov_base = &eid;
|
|
|
|
len += iov[0].iov_len = sizeof (eid);
|
|
|
|
iov[1].iov_base = &etime[0];
|
|
|
|
len += iov[1].iov_len = sizeof (etime[0]);
|
|
|
|
iov[2].iov_base = &etime[1];
|
|
|
|
len += iov[2].iov_len = sizeof (etime[1]);
|
|
|
|
|
|
|
|
n = writev(zcp->state_fd, iov, 3);
|
|
|
|
if (n < 0) {
|
|
|
|
zed_log_msg(LOG_WARNING,
|
|
|
|
"Failed to write state file \"%s\": %s",
|
|
|
|
zcp->state_file, strerror(errno));
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
if (n != len) {
|
|
|
|
errno = EIO;
|
|
|
|
zed_log_msg(LOG_WARNING,
|
|
|
|
"Failed to write state file \"%s\": Wrote %d of %d bytes",
|
|
|
|
zcp->state_file, n, len);
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
if (fdatasync(zcp->state_fd) < 0) {
|
|
|
|
zed_log_msg(LOG_WARNING,
|
|
|
|
"Failed to sync state file \"%s\": %s",
|
|
|
|
zcp->state_file, strerror(errno));
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
return (0);
|
|
|
|
}
|