zfs/cmd/zed/zed_event.c

917 lines
23 KiB
C
Raw Normal View History

/*
* This file is part of the ZFS Event Daemon (ZED)
* for ZFS on Linux (ZoL) <http://zfsonlinux.org/>.
* Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049).
* Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC.
* 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.
*/
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <libzfs.h> /* FIXME: Replace with libzfs_core. */
#include <paths.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/zfs_ioctl.h>
#include <time.h>
#include <unistd.h>
#include <sys/fm/fs/zfs.h>
#include "zed.h"
#include "zed_conf.h"
#include "zed_disk_event.h"
#include "zed_exec.h"
#include "zed_file.h"
#include "zed_log.h"
#include "zed_strings.h"
#include "agents/zfs_agents.h"
Protect against adding duplicate strings in ZED The zed_strings container stores strings in an AVL, but does not check for duplicate strings being added. Within the AVL, strings are indexed by the string value itself. avl_add() requires the node being added must not already exist in the tree, and will assert() if this is not the case. This should not cause problems in practice. ZED uses this container in two places. In zed_conf.c, it is used to store the names of enabled zedlets as zed scans the zedlet directory listing; duplicate entries cannot occur here since duplicate names cannot occur within a directory. In zed_event.c, it is used to store the environment variables (as "NAME=VALUE" strings) that will be passed to zedlets; duplicate strings here should never happen unless there is a bug resulting in a duplicate nvpair or environment variable. This commit protects against adding a duplicate to a zed_strings container by first checking for the string being added, and removing the previous entry should one exist. This implements a "last one wins" policy. This commit also changes the prototype for zed_strings_add() to allow the string key (by which it is indexed in the AVL) to differ from the string value. By adding zedlet environment variables using the variable name as the key, multiple adds for the same variable name will result in only the last value being stored. Finally, this commit routes all additions of zedlet environment variables through the updated _zed_event_add_var(). This ensures all zedlet environment variable names are properly converted. Signed-off-by: Chris Dunlap <cdunlap@llnl.gov> Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov> Closes #3042
2014-10-19 19:05:07 +00:00
#define MAXBUF 4096
/*
* Open the libzfs interface.
*/
void
zed_event_init(struct zed_conf *zcp)
{
if (!zcp)
zed_log_die("Failed zed_event_init: %s", strerror(EINVAL));
zcp->zfs_hdl = libzfs_init();
if (!zcp->zfs_hdl)
zed_log_die("Failed to initialize libzfs");
zcp->zevent_fd = open(ZFS_DEV, O_RDWR);
if (zcp->zevent_fd < 0)
zed_log_die("Failed to open \"%s\": %s",
ZFS_DEV, strerror(errno));
zfs_agent_init(zcp->zfs_hdl);
if (zed_disk_event_init() != 0)
zed_log_die("Failed to initialize disk events");
}
/*
* Close the libzfs interface.
*/
void
zed_event_fini(struct zed_conf *zcp)
{
if (!zcp)
zed_log_die("Failed zed_event_fini: %s", strerror(EINVAL));
zed_disk_event_fini();
zfs_agent_fini();
if (zcp->zevent_fd >= 0) {
if (close(zcp->zevent_fd) < 0)
zed_log_msg(LOG_WARNING, "Failed to close \"%s\": %s",
ZFS_DEV, strerror(errno));
zcp->zevent_fd = -1;
}
if (zcp->zfs_hdl) {
libzfs_fini(zcp->zfs_hdl);
zcp->zfs_hdl = NULL;
}
}
/*
* Seek to the event specified by [saved_eid] and [saved_etime].
* This protects against processing a given event more than once.
* Return 0 upon a successful seek to the specified event, or -1 otherwise.
*
* A zevent is considered to be uniquely specified by its (eid,time) tuple.
* The unsigned 64b eid is set to 1 when the kernel module is loaded, and
* incremented by 1 for each new event. Since the state file can persist
* across a kernel module reload, the time must be checked to ensure a match.
*/
int
zed_event_seek(struct zed_conf *zcp, uint64_t saved_eid, int64_t saved_etime[])
{
uint64_t eid;
int found;
nvlist_t *nvl;
int n_dropped;
int64_t *etime;
uint_t nelem;
int rv;
if (!zcp) {
errno = EINVAL;
zed_log_msg(LOG_ERR, "Failed to seek zevent: %s",
strerror(errno));
return (-1);
}
eid = 0;
found = 0;
while ((eid < saved_eid) && !found) {
rv = zpool_events_next(zcp->zfs_hdl, &nvl, &n_dropped,
ZEVENT_NONBLOCK, zcp->zevent_fd);
if ((rv != 0) || !nvl)
break;
if (n_dropped > 0) {
zed_log_msg(LOG_WARNING, "Missed %d events", n_dropped);
/*
* FIXME: Increase max size of event nvlist in
* /sys/module/zfs/parameters/zfs_zevent_len_max ?
*/
}
if (nvlist_lookup_uint64(nvl, "eid", &eid) != 0) {
zed_log_msg(LOG_WARNING, "Failed to lookup zevent eid");
} else if (nvlist_lookup_int64_array(nvl, "time",
&etime, &nelem) != 0) {
zed_log_msg(LOG_WARNING,
"Failed to lookup zevent time (eid=%llu)", eid);
} else if (nelem != 2) {
zed_log_msg(LOG_WARNING,
"Failed to lookup zevent time (eid=%llu, nelem=%u)",
eid, nelem);
} else if ((eid != saved_eid) ||
(etime[0] != saved_etime[0]) ||
(etime[1] != saved_etime[1])) {
/* no-op */
} else {
found = 1;
}
free(nvl);
}
if (!found && (saved_eid > 0)) {
if (zpool_events_seek(zcp->zfs_hdl, ZEVENT_SEEK_START,
zcp->zevent_fd) < 0)
zed_log_msg(LOG_WARNING, "Failed to seek to eid=0");
else
eid = 0;
}
zed_log_msg(LOG_NOTICE, "Processing events since eid=%llu", eid);
return (found ? 0 : -1);
}
/*
* Return non-zero if nvpair [name] should be formatted in hex; o/w, return 0.
*/
static int
_zed_event_value_is_hex(const char *name)
{
const char *hex_suffix[] = {
"_guid",
"_guids",
NULL
};
const char **pp;
char *p;
if (!name)
return (0);
for (pp = hex_suffix; *pp; pp++) {
p = strstr(name, *pp);
if (p && strlen(p) == strlen(*pp))
return (1);
}
return (0);
}
/*
* Add an environment variable for [eid] to the container [zsp].
*
* The variable name is the concatenation of [prefix] and [name] converted to
* uppercase with non-alphanumeric characters converted to underscores;
* [prefix] is optional, and [name] must begin with an alphabetic character.
* If the converted variable name already exists within the container [zsp],
* its existing value will be replaced with the new value.
*
* The variable value is specified by the format string [fmt].
*
* Returns 0 on success, and -1 on error (with errno set).
*
* All environment variables in [zsp] should be added through this function.
*/
static int
_zed_event_add_var(uint64_t eid, zed_strings_t *zsp,
const char *prefix, const char *name, const char *fmt, ...)
{
char keybuf[MAXBUF];
char valbuf[MAXBUF];
char *dstp;
const char *srcp;
const char *lastp;
int n;
int buflen;
va_list vargs;
assert(zsp != NULL);
assert(fmt != NULL);
if (!name) {
errno = EINVAL;
zed_log_msg(LOG_WARNING,
"Failed to add variable for eid=%llu: Name is empty", eid);
return (-1);
} else if (!isalpha(name[0])) {
errno = EINVAL;
zed_log_msg(LOG_WARNING,
"Failed to add variable for eid=%llu: "
"Name \"%s\" is invalid", eid, name);
return (-1);
}
/*
* Construct the string key by converting PREFIX (if present) and NAME.
*/
dstp = keybuf;
lastp = keybuf + sizeof (keybuf);
if (prefix) {
for (srcp = prefix; *srcp && (dstp < lastp); srcp++)
*dstp++ = isalnum(*srcp) ? toupper(*srcp) : '_';
}
for (srcp = name; *srcp && (dstp < lastp); srcp++)
*dstp++ = isalnum(*srcp) ? toupper(*srcp) : '_';
if (dstp == lastp) {
errno = ENAMETOOLONG;
zed_log_msg(LOG_WARNING,
"Failed to add variable for eid=%llu: Name too long", eid);
return (-1);
}
*dstp = '\0';
/*
* Construct the string specified by "[PREFIX][NAME]=[FMT]".
*/
dstp = valbuf;
buflen = sizeof (valbuf);
n = strlcpy(dstp, keybuf, buflen);
if (n >= sizeof (valbuf)) {
errno = EMSGSIZE;
zed_log_msg(LOG_WARNING, "Failed to add %s for eid=%llu: %s",
keybuf, eid, "Exceeded buffer size");
return (-1);
}
dstp += n;
buflen -= n;
*dstp++ = '=';
buflen--;
if (buflen <= 0) {
errno = EMSGSIZE;
zed_log_msg(LOG_WARNING, "Failed to add %s for eid=%llu: %s",
keybuf, eid, "Exceeded buffer size");
return (-1);
}
va_start(vargs, fmt);
n = vsnprintf(dstp, buflen, fmt, vargs);
va_end(vargs);
if ((n < 0) || (n >= buflen)) {
errno = EMSGSIZE;
zed_log_msg(LOG_WARNING, "Failed to add %s for eid=%llu: %s",
keybuf, eid, "Exceeded buffer size");
return (-1);
} else if (zed_strings_add(zsp, keybuf, valbuf) < 0) {
zed_log_msg(LOG_WARNING, "Failed to add %s for eid=%llu: %s",
keybuf, eid, strerror(errno));
return (-1);
}
return (0);
}
static int
_zed_event_add_array_err(uint64_t eid, const char *name)
{
errno = EMSGSIZE;
zed_log_msg(LOG_WARNING,
"Failed to convert nvpair \"%s\" for eid=%llu: "
"Exceeded buffer size", name, eid);
return (-1);
}
static int
_zed_event_add_int8_array(uint64_t eid, zed_strings_t *zsp,
const char *prefix, nvpair_t *nvp)
{
char buf[MAXBUF];
int buflen = sizeof (buf);
const char *name;
int8_t *i8p;
uint_t nelem;
uint_t i;
char *p;
int n;
assert((nvp != NULL) && (nvpair_type(nvp) == DATA_TYPE_INT8_ARRAY));
name = nvpair_name(nvp);
(void) nvpair_value_int8_array(nvp, &i8p, &nelem);
for (i = 0, p = buf; (i < nelem) && (buflen > 0); i++) {
n = snprintf(p, buflen, "%d ", i8p[i]);
if ((n < 0) || (n >= buflen))
return (_zed_event_add_array_err(eid, name));
p += n;
buflen -= n;
}
if (nelem > 0)
*--p = '\0';
return (_zed_event_add_var(eid, zsp, prefix, name, "%s", buf));
}
static int
_zed_event_add_uint8_array(uint64_t eid, zed_strings_t *zsp,
const char *prefix, nvpair_t *nvp)
{
char buf[MAXBUF];
int buflen = sizeof (buf);
const char *name;
uint8_t *u8p;
uint_t nelem;
uint_t i;
char *p;
int n;
assert((nvp != NULL) && (nvpair_type(nvp) == DATA_TYPE_UINT8_ARRAY));
name = nvpair_name(nvp);
(void) nvpair_value_uint8_array(nvp, &u8p, &nelem);
for (i = 0, p = buf; (i < nelem) && (buflen > 0); i++) {
n = snprintf(p, buflen, "%u ", u8p[i]);
if ((n < 0) || (n >= buflen))
return (_zed_event_add_array_err(eid, name));
p += n;
buflen -= n;
}
if (nelem > 0)
*--p = '\0';
return (_zed_event_add_var(eid, zsp, prefix, name, "%s", buf));
}
static int
_zed_event_add_int16_array(uint64_t eid, zed_strings_t *zsp,
const char *prefix, nvpair_t *nvp)
{
char buf[MAXBUF];
int buflen = sizeof (buf);
const char *name;
int16_t *i16p;
uint_t nelem;
uint_t i;
char *p;
int n;
assert((nvp != NULL) && (nvpair_type(nvp) == DATA_TYPE_INT16_ARRAY));
name = nvpair_name(nvp);
(void) nvpair_value_int16_array(nvp, &i16p, &nelem);
for (i = 0, p = buf; (i < nelem) && (buflen > 0); i++) {
n = snprintf(p, buflen, "%d ", i16p[i]);
if ((n < 0) || (n >= buflen))
return (_zed_event_add_array_err(eid, name));
p += n;
buflen -= n;
}
if (nelem > 0)
*--p = '\0';
return (_zed_event_add_var(eid, zsp, prefix, name, "%s", buf));
}
static int
_zed_event_add_uint16_array(uint64_t eid, zed_strings_t *zsp,
const char *prefix, nvpair_t *nvp)
{
char buf[MAXBUF];
int buflen = sizeof (buf);
const char *name;
uint16_t *u16p;
uint_t nelem;
uint_t i;
char *p;
int n;
assert((nvp != NULL) && (nvpair_type(nvp) == DATA_TYPE_UINT16_ARRAY));
name = nvpair_name(nvp);
(void) nvpair_value_uint16_array(nvp, &u16p, &nelem);
for (i = 0, p = buf; (i < nelem) && (buflen > 0); i++) {
n = snprintf(p, buflen, "%u ", u16p[i]);
if ((n < 0) || (n >= buflen))
return (_zed_event_add_array_err(eid, name));
p += n;
buflen -= n;
}
if (nelem > 0)
*--p = '\0';
return (_zed_event_add_var(eid, zsp, prefix, name, "%s", buf));
}
static int
_zed_event_add_int32_array(uint64_t eid, zed_strings_t *zsp,
const char *prefix, nvpair_t *nvp)
{
char buf[MAXBUF];
int buflen = sizeof (buf);
const char *name;
int32_t *i32p;
uint_t nelem;
uint_t i;
char *p;
int n;
assert((nvp != NULL) && (nvpair_type(nvp) == DATA_TYPE_INT32_ARRAY));
name = nvpair_name(nvp);
(void) nvpair_value_int32_array(nvp, &i32p, &nelem);
for (i = 0, p = buf; (i < nelem) && (buflen > 0); i++) {
n = snprintf(p, buflen, "%d ", i32p[i]);
if ((n < 0) || (n >= buflen))
return (_zed_event_add_array_err(eid, name));
p += n;
buflen -= n;
}
if (nelem > 0)
*--p = '\0';
return (_zed_event_add_var(eid, zsp, prefix, name, "%s", buf));
}
static int
_zed_event_add_uint32_array(uint64_t eid, zed_strings_t *zsp,
const char *prefix, nvpair_t *nvp)
{
char buf[MAXBUF];
int buflen = sizeof (buf);
const char *name;
uint32_t *u32p;
uint_t nelem;
uint_t i;
char *p;
int n;
assert((nvp != NULL) && (nvpair_type(nvp) == DATA_TYPE_UINT32_ARRAY));
name = nvpair_name(nvp);
(void) nvpair_value_uint32_array(nvp, &u32p, &nelem);
for (i = 0, p = buf; (i < nelem) && (buflen > 0); i++) {
n = snprintf(p, buflen, "%u ", u32p[i]);
if ((n < 0) || (n >= buflen))
return (_zed_event_add_array_err(eid, name));
p += n;
buflen -= n;
}
if (nelem > 0)
*--p = '\0';
return (_zed_event_add_var(eid, zsp, prefix, name, "%s", buf));
}
static int
_zed_event_add_int64_array(uint64_t eid, zed_strings_t *zsp,
const char *prefix, nvpair_t *nvp)
{
char buf[MAXBUF];
int buflen = sizeof (buf);
const char *name;
int64_t *i64p;
uint_t nelem;
uint_t i;
char *p;
int n;
assert((nvp != NULL) && (nvpair_type(nvp) == DATA_TYPE_INT64_ARRAY));
name = nvpair_name(nvp);
(void) nvpair_value_int64_array(nvp, &i64p, &nelem);
for (i = 0, p = buf; (i < nelem) && (buflen > 0); i++) {
n = snprintf(p, buflen, "%lld ", (u_longlong_t)i64p[i]);
if ((n < 0) || (n >= buflen))
return (_zed_event_add_array_err(eid, name));
p += n;
buflen -= n;
}
if (nelem > 0)
*--p = '\0';
return (_zed_event_add_var(eid, zsp, prefix, name, "%s", buf));
}
static int
_zed_event_add_uint64_array(uint64_t eid, zed_strings_t *zsp,
const char *prefix, nvpair_t *nvp)
{
char buf[MAXBUF];
int buflen = sizeof (buf);
const char *name;
const char *fmt;
uint64_t *u64p;
uint_t nelem;
uint_t i;
char *p;
int n;
assert((nvp != NULL) && (nvpair_type(nvp) == DATA_TYPE_UINT64_ARRAY));
name = nvpair_name(nvp);
fmt = _zed_event_value_is_hex(name) ? "0x%.16llX " : "%llu ";
(void) nvpair_value_uint64_array(nvp, &u64p, &nelem);
for (i = 0, p = buf; (i < nelem) && (buflen > 0); i++) {
n = snprintf(p, buflen, fmt, (u_longlong_t)u64p[i]);
if ((n < 0) || (n >= buflen))
return (_zed_event_add_array_err(eid, name));
p += n;
buflen -= n;
}
if (nelem > 0)
*--p = '\0';
return (_zed_event_add_var(eid, zsp, prefix, name, "%s", buf));
}
static int
_zed_event_add_string_array(uint64_t eid, zed_strings_t *zsp,
const char *prefix, nvpair_t *nvp)
{
char buf[MAXBUF];
int buflen = sizeof (buf);
const char *name;
char **strp;
uint_t nelem;
uint_t i;
char *p;
int n;
assert((nvp != NULL) && (nvpair_type(nvp) == DATA_TYPE_STRING_ARRAY));
name = nvpair_name(nvp);
(void) nvpair_value_string_array(nvp, &strp, &nelem);
for (i = 0, p = buf; (i < nelem) && (buflen > 0); i++) {
n = snprintf(p, buflen, "%s ", strp[i] ? strp[i] : "<NULL>");
if ((n < 0) || (n >= buflen))
return (_zed_event_add_array_err(eid, name));
p += n;
buflen -= n;
}
if (nelem > 0)
*--p = '\0';
return (_zed_event_add_var(eid, zsp, prefix, name, "%s", buf));
Protect against adding duplicate strings in ZED The zed_strings container stores strings in an AVL, but does not check for duplicate strings being added. Within the AVL, strings are indexed by the string value itself. avl_add() requires the node being added must not already exist in the tree, and will assert() if this is not the case. This should not cause problems in practice. ZED uses this container in two places. In zed_conf.c, it is used to store the names of enabled zedlets as zed scans the zedlet directory listing; duplicate entries cannot occur here since duplicate names cannot occur within a directory. In zed_event.c, it is used to store the environment variables (as "NAME=VALUE" strings) that will be passed to zedlets; duplicate strings here should never happen unless there is a bug resulting in a duplicate nvpair or environment variable. This commit protects against adding a duplicate to a zed_strings container by first checking for the string being added, and removing the previous entry should one exist. This implements a "last one wins" policy. This commit also changes the prototype for zed_strings_add() to allow the string key (by which it is indexed in the AVL) to differ from the string value. By adding zedlet environment variables using the variable name as the key, multiple adds for the same variable name will result in only the last value being stored. Finally, this commit routes all additions of zedlet environment variables through the updated _zed_event_add_var(). This ensures all zedlet environment variable names are properly converted. Signed-off-by: Chris Dunlap <cdunlap@llnl.gov> Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov> Closes #3042
2014-10-19 19:05:07 +00:00
}
/*
* Convert the nvpair [nvp] to a string which is added to the environment
* of the child process.
* Return 0 on success, -1 on error.
*
* FIXME: Refactor with cmd/zpool/zpool_main.c:zpool_do_events_nvprint()?
*/
static void
_zed_event_add_nvpair(uint64_t eid, zed_strings_t *zsp, nvpair_t *nvp)
{
const char *name;
data_type_t type;
const char *prefix = ZEVENT_VAR_PREFIX;
boolean_t b;
double d;
uint8_t i8;
uint16_t i16;
uint32_t i32;
uint64_t i64;
char *str;
assert(zsp != NULL);
assert(nvp != NULL);
name = nvpair_name(nvp);
type = nvpair_type(nvp);
switch (type) {
case DATA_TYPE_BOOLEAN:
_zed_event_add_var(eid, zsp, prefix, name, "%s", "1");
break;
case DATA_TYPE_BOOLEAN_VALUE:
(void) nvpair_value_boolean_value(nvp, &b);
_zed_event_add_var(eid, zsp, prefix, name, "%s", b ? "1" : "0");
break;
case DATA_TYPE_BYTE:
(void) nvpair_value_byte(nvp, &i8);
_zed_event_add_var(eid, zsp, prefix, name, "%d", i8);
break;
case DATA_TYPE_INT8:
(void) nvpair_value_int8(nvp, (int8_t *)&i8);
_zed_event_add_var(eid, zsp, prefix, name, "%d", i8);
break;
case DATA_TYPE_UINT8:
(void) nvpair_value_uint8(nvp, &i8);
_zed_event_add_var(eid, zsp, prefix, name, "%u", i8);
break;
case DATA_TYPE_INT16:
(void) nvpair_value_int16(nvp, (int16_t *)&i16);
_zed_event_add_var(eid, zsp, prefix, name, "%d", i16);
break;
case DATA_TYPE_UINT16:
(void) nvpair_value_uint16(nvp, &i16);
_zed_event_add_var(eid, zsp, prefix, name, "%u", i16);
break;
case DATA_TYPE_INT32:
(void) nvpair_value_int32(nvp, (int32_t *)&i32);
_zed_event_add_var(eid, zsp, prefix, name, "%d", i32);
break;
case DATA_TYPE_UINT32:
(void) nvpair_value_uint32(nvp, &i32);
_zed_event_add_var(eid, zsp, prefix, name, "%u", i32);
break;
case DATA_TYPE_INT64:
(void) nvpair_value_int64(nvp, (int64_t *)&i64);
_zed_event_add_var(eid, zsp, prefix, name,
"%lld", (longlong_t)i64);
break;
case DATA_TYPE_UINT64:
(void) nvpair_value_uint64(nvp, &i64);
_zed_event_add_var(eid, zsp, prefix, name,
(_zed_event_value_is_hex(name) ? "0x%.16llX" : "%llu"),
(u_longlong_t)i64);
/*
* shadow readable strings for vdev state pairs
*/
if (strcmp(name, FM_EREPORT_PAYLOAD_ZFS_VDEV_STATE) == 0 ||
strcmp(name, FM_EREPORT_PAYLOAD_ZFS_VDEV_LASTSTATE) == 0) {
char alt[32];
(void) snprintf(alt, sizeof (alt), "%s_str", name);
_zed_event_add_var(eid, zsp, prefix, alt, "%s",
zpool_state_to_name(i64, VDEV_AUX_NONE));
}
break;
case DATA_TYPE_DOUBLE:
(void) nvpair_value_double(nvp, &d);
_zed_event_add_var(eid, zsp, prefix, name, "%g", d);
break;
case DATA_TYPE_HRTIME:
(void) nvpair_value_hrtime(nvp, (hrtime_t *)&i64);
_zed_event_add_var(eid, zsp, prefix, name,
"%llu", (u_longlong_t)i64);
break;
case DATA_TYPE_NVLIST:
_zed_event_add_var(eid, zsp, prefix, name,
"%s", "_NOT_IMPLEMENTED_"); /* FIXME */
break;
case DATA_TYPE_STRING:
(void) nvpair_value_string(nvp, &str);
_zed_event_add_var(eid, zsp, prefix, name,
"%s", (str ? str : "<NULL>"));
break;
case DATA_TYPE_BOOLEAN_ARRAY:
_zed_event_add_var(eid, zsp, prefix, name,
"%s", "_NOT_IMPLEMENTED_"); /* FIXME */
break;
case DATA_TYPE_BYTE_ARRAY:
_zed_event_add_var(eid, zsp, prefix, name,
"%s", "_NOT_IMPLEMENTED_"); /* FIXME */
break;
case DATA_TYPE_INT8_ARRAY:
_zed_event_add_int8_array(eid, zsp, prefix, nvp);
break;
case DATA_TYPE_UINT8_ARRAY:
_zed_event_add_uint8_array(eid, zsp, prefix, nvp);
break;
case DATA_TYPE_INT16_ARRAY:
_zed_event_add_int16_array(eid, zsp, prefix, nvp);
break;
case DATA_TYPE_UINT16_ARRAY:
_zed_event_add_uint16_array(eid, zsp, prefix, nvp);
break;
case DATA_TYPE_INT32_ARRAY:
_zed_event_add_int32_array(eid, zsp, prefix, nvp);
break;
case DATA_TYPE_UINT32_ARRAY:
_zed_event_add_uint32_array(eid, zsp, prefix, nvp);
break;
case DATA_TYPE_INT64_ARRAY:
_zed_event_add_int64_array(eid, zsp, prefix, nvp);
break;
case DATA_TYPE_UINT64_ARRAY:
_zed_event_add_uint64_array(eid, zsp, prefix, nvp);
break;
case DATA_TYPE_STRING_ARRAY:
_zed_event_add_string_array(eid, zsp, prefix, nvp);
break;
case DATA_TYPE_NVLIST_ARRAY:
_zed_event_add_var(eid, zsp, prefix, name,
"%s", "_NOT_IMPLEMENTED_"); /* FIXME */
break;
default:
errno = EINVAL;
zed_log_msg(LOG_WARNING,
"Failed to convert nvpair \"%s\" for eid=%llu: "
"Unrecognized type=%u", name, eid, (unsigned int) type);
break;
}
}
/*
* Restrict various environment variables to safe and sane values
* when constructing the environment for the child process.
*
* Reference: Secure Programming Cookbook by Viega & Messier, Section 1.1.
*/
static void
_zed_event_add_env_restrict(uint64_t eid, zed_strings_t *zsp)
{
Protect against adding duplicate strings in ZED The zed_strings container stores strings in an AVL, but does not check for duplicate strings being added. Within the AVL, strings are indexed by the string value itself. avl_add() requires the node being added must not already exist in the tree, and will assert() if this is not the case. This should not cause problems in practice. ZED uses this container in two places. In zed_conf.c, it is used to store the names of enabled zedlets as zed scans the zedlet directory listing; duplicate entries cannot occur here since duplicate names cannot occur within a directory. In zed_event.c, it is used to store the environment variables (as "NAME=VALUE" strings) that will be passed to zedlets; duplicate strings here should never happen unless there is a bug resulting in a duplicate nvpair or environment variable. This commit protects against adding a duplicate to a zed_strings container by first checking for the string being added, and removing the previous entry should one exist. This implements a "last one wins" policy. This commit also changes the prototype for zed_strings_add() to allow the string key (by which it is indexed in the AVL) to differ from the string value. By adding zedlet environment variables using the variable name as the key, multiple adds for the same variable name will result in only the last value being stored. Finally, this commit routes all additions of zedlet environment variables through the updated _zed_event_add_var(). This ensures all zedlet environment variable names are properly converted. Signed-off-by: Chris Dunlap <cdunlap@llnl.gov> Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov> Closes #3042
2014-10-19 19:05:07 +00:00
const char *env_restrict[][2] = {
{ "IFS", " \t\n" },
{ "PATH", _PATH_STDPATH },
{ "ZDB", SBINDIR "/zdb" },
{ "ZED", SBINDIR "/zed" },
{ "ZFS", SBINDIR "/zfs" },
{ "ZINJECT", SBINDIR "/zinject" },
{ "ZPOOL", SBINDIR "/zpool" },
{ "ZFS_ALIAS", ZFS_META_ALIAS },
{ "ZFS_VERSION", ZFS_META_VERSION },
{ "ZFS_RELEASE", ZFS_META_RELEASE },
{ NULL, NULL }
};
Protect against adding duplicate strings in ZED The zed_strings container stores strings in an AVL, but does not check for duplicate strings being added. Within the AVL, strings are indexed by the string value itself. avl_add() requires the node being added must not already exist in the tree, and will assert() if this is not the case. This should not cause problems in practice. ZED uses this container in two places. In zed_conf.c, it is used to store the names of enabled zedlets as zed scans the zedlet directory listing; duplicate entries cannot occur here since duplicate names cannot occur within a directory. In zed_event.c, it is used to store the environment variables (as "NAME=VALUE" strings) that will be passed to zedlets; duplicate strings here should never happen unless there is a bug resulting in a duplicate nvpair or environment variable. This commit protects against adding a duplicate to a zed_strings container by first checking for the string being added, and removing the previous entry should one exist. This implements a "last one wins" policy. This commit also changes the prototype for zed_strings_add() to allow the string key (by which it is indexed in the AVL) to differ from the string value. By adding zedlet environment variables using the variable name as the key, multiple adds for the same variable name will result in only the last value being stored. Finally, this commit routes all additions of zedlet environment variables through the updated _zed_event_add_var(). This ensures all zedlet environment variable names are properly converted. Signed-off-by: Chris Dunlap <cdunlap@llnl.gov> Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov> Closes #3042
2014-10-19 19:05:07 +00:00
const char *(*pa)[2];
assert(zsp != NULL);
Protect against adding duplicate strings in ZED The zed_strings container stores strings in an AVL, but does not check for duplicate strings being added. Within the AVL, strings are indexed by the string value itself. avl_add() requires the node being added must not already exist in the tree, and will assert() if this is not the case. This should not cause problems in practice. ZED uses this container in two places. In zed_conf.c, it is used to store the names of enabled zedlets as zed scans the zedlet directory listing; duplicate entries cannot occur here since duplicate names cannot occur within a directory. In zed_event.c, it is used to store the environment variables (as "NAME=VALUE" strings) that will be passed to zedlets; duplicate strings here should never happen unless there is a bug resulting in a duplicate nvpair or environment variable. This commit protects against adding a duplicate to a zed_strings container by first checking for the string being added, and removing the previous entry should one exist. This implements a "last one wins" policy. This commit also changes the prototype for zed_strings_add() to allow the string key (by which it is indexed in the AVL) to differ from the string value. By adding zedlet environment variables using the variable name as the key, multiple adds for the same variable name will result in only the last value being stored. Finally, this commit routes all additions of zedlet environment variables through the updated _zed_event_add_var(). This ensures all zedlet environment variable names are properly converted. Signed-off-by: Chris Dunlap <cdunlap@llnl.gov> Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov> Closes #3042
2014-10-19 19:05:07 +00:00
for (pa = env_restrict; *(*pa); pa++) {
_zed_event_add_var(eid, zsp, NULL, (*pa)[0], "%s", (*pa)[1]);
}
}
/*
* Preserve specified variables from the parent environment
* when constructing the environment for the child process.
*
* Reference: Secure Programming Cookbook by Viega & Messier, Section 1.1.
*/
static void
_zed_event_add_env_preserve(uint64_t eid, zed_strings_t *zsp)
{
const char *env_preserve[] = {
"TZ",
NULL
};
Protect against adding duplicate strings in ZED The zed_strings container stores strings in an AVL, but does not check for duplicate strings being added. Within the AVL, strings are indexed by the string value itself. avl_add() requires the node being added must not already exist in the tree, and will assert() if this is not the case. This should not cause problems in practice. ZED uses this container in two places. In zed_conf.c, it is used to store the names of enabled zedlets as zed scans the zedlet directory listing; duplicate entries cannot occur here since duplicate names cannot occur within a directory. In zed_event.c, it is used to store the environment variables (as "NAME=VALUE" strings) that will be passed to zedlets; duplicate strings here should never happen unless there is a bug resulting in a duplicate nvpair or environment variable. This commit protects against adding a duplicate to a zed_strings container by first checking for the string being added, and removing the previous entry should one exist. This implements a "last one wins" policy. This commit also changes the prototype for zed_strings_add() to allow the string key (by which it is indexed in the AVL) to differ from the string value. By adding zedlet environment variables using the variable name as the key, multiple adds for the same variable name will result in only the last value being stored. Finally, this commit routes all additions of zedlet environment variables through the updated _zed_event_add_var(). This ensures all zedlet environment variable names are properly converted. Signed-off-by: Chris Dunlap <cdunlap@llnl.gov> Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov> Closes #3042
2014-10-19 19:05:07 +00:00
const char **keyp;
const char *val;
assert(zsp != NULL);
Protect against adding duplicate strings in ZED The zed_strings container stores strings in an AVL, but does not check for duplicate strings being added. Within the AVL, strings are indexed by the string value itself. avl_add() requires the node being added must not already exist in the tree, and will assert() if this is not the case. This should not cause problems in practice. ZED uses this container in two places. In zed_conf.c, it is used to store the names of enabled zedlets as zed scans the zedlet directory listing; duplicate entries cannot occur here since duplicate names cannot occur within a directory. In zed_event.c, it is used to store the environment variables (as "NAME=VALUE" strings) that will be passed to zedlets; duplicate strings here should never happen unless there is a bug resulting in a duplicate nvpair or environment variable. This commit protects against adding a duplicate to a zed_strings container by first checking for the string being added, and removing the previous entry should one exist. This implements a "last one wins" policy. This commit also changes the prototype for zed_strings_add() to allow the string key (by which it is indexed in the AVL) to differ from the string value. By adding zedlet environment variables using the variable name as the key, multiple adds for the same variable name will result in only the last value being stored. Finally, this commit routes all additions of zedlet environment variables through the updated _zed_event_add_var(). This ensures all zedlet environment variable names are properly converted. Signed-off-by: Chris Dunlap <cdunlap@llnl.gov> Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov> Closes #3042
2014-10-19 19:05:07 +00:00
for (keyp = env_preserve; *keyp; keyp++) {
if ((val = getenv(*keyp)))
_zed_event_add_var(eid, zsp, NULL, *keyp, "%s", val);
}
}
/*
* Compute the "subclass" by removing the first 3 components of [class]
OpenZFS 5997 - FRU field not set during pool creation and never updated Authored by: Hans Rosenfeld <hans.rosenfeld@nexenta.com> Reviewed by: Dan Fields <dan.fields@nexenta.com> Reviewed by: Josef Sipek <josef.sipek@nexenta.com> Reviewed by: Richard Elling <richard.elling@gmail.com> Reviewed by: George Wilson <george.wilson@delphix.com> Approved by: Robert Mustacchi <rm@joyent.com> Signed-off-by: Don Brady <don.brady@intel.com> Ported-by: Brian Behlendorf <behlendorf1@llnl.gov> OpenZFS-issue: https://www.illumos.org/issues/5997 OpenZFS-commit: https://github.com/openzfs/openzfs/commit/1437283 Porting Notes: In addition to the OpenZFS changes this patch realigns the events with those found in OpenZFS. Events which would be logged as sysevents on illumos have been been mapped to the 'sysevent' class for Linux. In addition, several subclass names have been changed to match what is used in OpenZFS. In all cases this means a '.' was changed to an '_' in the subclass. The scripts provided by ZoL have been updated, however users which provide scripts for any of the following events will need to rename them based on the new subclass names. ereport.fs.zfs.config.sync sysevent.fs.zfs.config_sync ereport.fs.zfs.zpool.destroy sysevent.fs.zfs.pool_destroy ereport.fs.zfs.zpool.reguid sysevent.fs.zfs.pool_reguid ereport.fs.zfs.vdev.remove sysevent.fs.zfs.vdev_remove ereport.fs.zfs.vdev.clear sysevent.fs.zfs.vdev_clear ereport.fs.zfs.vdev.check sysevent.fs.zfs.vdev_check ereport.fs.zfs.vdev.spare sysevent.fs.zfs.vdev_spare ereport.fs.zfs.vdev.autoexpand sysevent.fs.zfs.vdev_autoexpand ereport.fs.zfs.resilver.start sysevent.fs.zfs.resilver_start ereport.fs.zfs.resilver.finish sysevent.fs.zfs.resilver_finish ereport.fs.zfs.scrub.start sysevent.fs.zfs.scrub_start ereport.fs.zfs.scrub.finish sysevent.fs.zfs.scrub_finish ereport.fs.zfs.bootfs.vdev.attach sysevent.fs.zfs.bootfs_vdev_attach
2016-07-27 22:29:15 +00:00
* (which will always be of the form "*.fs.zfs"). Return a pointer inside
* the string [class], or NULL if insufficient components exist.
*/
static const char *
_zed_event_get_subclass(const char *class)
{
const char *p;
int i;
if (!class)
return (NULL);
p = class;
for (i = 0; i < 3; i++) {
p = strchr(p, '.');
if (!p)
break;
p++;
}
return (p);
}
/*
* Convert the zevent time from a 2-element array of 64b integers
* into a more convenient form:
* - TIME_SECS is the second component of the time.
* - TIME_NSECS is the nanosecond component of the time.
* - TIME_STRING is an almost-RFC3339-compliant string representation.
*/
static void
_zed_event_add_time_strings(uint64_t eid, zed_strings_t *zsp, int64_t etime[])
{
struct tm *stp;
char buf[32];
assert(zsp != NULL);
assert(etime != NULL);
Protect against adding duplicate strings in ZED The zed_strings container stores strings in an AVL, but does not check for duplicate strings being added. Within the AVL, strings are indexed by the string value itself. avl_add() requires the node being added must not already exist in the tree, and will assert() if this is not the case. This should not cause problems in practice. ZED uses this container in two places. In zed_conf.c, it is used to store the names of enabled zedlets as zed scans the zedlet directory listing; duplicate entries cannot occur here since duplicate names cannot occur within a directory. In zed_event.c, it is used to store the environment variables (as "NAME=VALUE" strings) that will be passed to zedlets; duplicate strings here should never happen unless there is a bug resulting in a duplicate nvpair or environment variable. This commit protects against adding a duplicate to a zed_strings container by first checking for the string being added, and removing the previous entry should one exist. This implements a "last one wins" policy. This commit also changes the prototype for zed_strings_add() to allow the string key (by which it is indexed in the AVL) to differ from the string value. By adding zedlet environment variables using the variable name as the key, multiple adds for the same variable name will result in only the last value being stored. Finally, this commit routes all additions of zedlet environment variables through the updated _zed_event_add_var(). This ensures all zedlet environment variable names are properly converted. Signed-off-by: Chris Dunlap <cdunlap@llnl.gov> Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov> Closes #3042
2014-10-19 19:05:07 +00:00
_zed_event_add_var(eid, zsp, ZEVENT_VAR_PREFIX, "TIME_SECS",
"%lld", (long long int) etime[0]);
_zed_event_add_var(eid, zsp, ZEVENT_VAR_PREFIX, "TIME_NSECS",
"%lld", (long long int) etime[1]);
if (!(stp = localtime((const time_t *) &etime[0]))) {
zed_log_msg(LOG_WARNING, "Failed to add %s%s for eid=%llu: %s",
ZEVENT_VAR_PREFIX, "TIME_STRING", eid, "localtime error");
} else if (!strftime(buf, sizeof (buf), "%Y-%m-%d %H:%M:%S%z", stp)) {
zed_log_msg(LOG_WARNING, "Failed to add %s%s for eid=%llu: %s",
ZEVENT_VAR_PREFIX, "TIME_STRING", eid, "strftime error");
} else {
Protect against adding duplicate strings in ZED The zed_strings container stores strings in an AVL, but does not check for duplicate strings being added. Within the AVL, strings are indexed by the string value itself. avl_add() requires the node being added must not already exist in the tree, and will assert() if this is not the case. This should not cause problems in practice. ZED uses this container in two places. In zed_conf.c, it is used to store the names of enabled zedlets as zed scans the zedlet directory listing; duplicate entries cannot occur here since duplicate names cannot occur within a directory. In zed_event.c, it is used to store the environment variables (as "NAME=VALUE" strings) that will be passed to zedlets; duplicate strings here should never happen unless there is a bug resulting in a duplicate nvpair or environment variable. This commit protects against adding a duplicate to a zed_strings container by first checking for the string being added, and removing the previous entry should one exist. This implements a "last one wins" policy. This commit also changes the prototype for zed_strings_add() to allow the string key (by which it is indexed in the AVL) to differ from the string value. By adding zedlet environment variables using the variable name as the key, multiple adds for the same variable name will result in only the last value being stored. Finally, this commit routes all additions of zedlet environment variables through the updated _zed_event_add_var(). This ensures all zedlet environment variable names are properly converted. Signed-off-by: Chris Dunlap <cdunlap@llnl.gov> Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov> Closes #3042
2014-10-19 19:05:07 +00:00
_zed_event_add_var(eid, zsp, ZEVENT_VAR_PREFIX, "TIME_STRING",
"%s", buf);
}
}
/*
* Service the next zevent, blocking until one is available.
*/
void
zed_event_service(struct zed_conf *zcp)
{
nvlist_t *nvl;
nvpair_t *nvp;
int n_dropped;
zed_strings_t *zsp;
uint64_t eid;
int64_t *etime;
uint_t nelem;
char *class;
const char *subclass;
int rv;
if (!zcp) {
errno = EINVAL;
zed_log_msg(LOG_ERR, "Failed to service zevent: %s",
strerror(errno));
return;
}
rv = zpool_events_next(zcp->zfs_hdl, &nvl, &n_dropped, ZEVENT_NONE,
zcp->zevent_fd);
if ((rv != 0) || !nvl)
return;
if (n_dropped > 0) {
zed_log_msg(LOG_WARNING, "Missed %d events", n_dropped);
/*
* FIXME: Increase max size of event nvlist in
* /sys/module/zfs/parameters/zfs_zevent_len_max ?
*/
}
if (nvlist_lookup_uint64(nvl, "eid", &eid) != 0) {
zed_log_msg(LOG_WARNING, "Failed to lookup zevent eid");
} else if (nvlist_lookup_int64_array(
nvl, "time", &etime, &nelem) != 0) {
zed_log_msg(LOG_WARNING,
"Failed to lookup zevent time (eid=%llu)", eid);
} else if (nelem != 2) {
zed_log_msg(LOG_WARNING,
"Failed to lookup zevent time (eid=%llu, nelem=%u)",
eid, nelem);
} else if (nvlist_lookup_string(nvl, "class", &class) != 0) {
zed_log_msg(LOG_WARNING,
"Failed to lookup zevent class (eid=%llu)", eid);
} else {
/* let internal modules see this event first */
zfs_agent_post_event(class, NULL, nvl);
zsp = zed_strings_create();
nvp = NULL;
while ((nvp = nvlist_next_nvpair(nvl, nvp)))
_zed_event_add_nvpair(eid, zsp, nvp);
_zed_event_add_env_restrict(eid, zsp);
_zed_event_add_env_preserve(eid, zsp);
Protect against adding duplicate strings in ZED The zed_strings container stores strings in an AVL, but does not check for duplicate strings being added. Within the AVL, strings are indexed by the string value itself. avl_add() requires the node being added must not already exist in the tree, and will assert() if this is not the case. This should not cause problems in practice. ZED uses this container in two places. In zed_conf.c, it is used to store the names of enabled zedlets as zed scans the zedlet directory listing; duplicate entries cannot occur here since duplicate names cannot occur within a directory. In zed_event.c, it is used to store the environment variables (as "NAME=VALUE" strings) that will be passed to zedlets; duplicate strings here should never happen unless there is a bug resulting in a duplicate nvpair or environment variable. This commit protects against adding a duplicate to a zed_strings container by first checking for the string being added, and removing the previous entry should one exist. This implements a "last one wins" policy. This commit also changes the prototype for zed_strings_add() to allow the string key (by which it is indexed in the AVL) to differ from the string value. By adding zedlet environment variables using the variable name as the key, multiple adds for the same variable name will result in only the last value being stored. Finally, this commit routes all additions of zedlet environment variables through the updated _zed_event_add_var(). This ensures all zedlet environment variable names are properly converted. Signed-off-by: Chris Dunlap <cdunlap@llnl.gov> Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov> Closes #3042
2014-10-19 19:05:07 +00:00
_zed_event_add_var(eid, zsp, ZED_VAR_PREFIX, "PID",
"%d", (int)getpid());
Protect against adding duplicate strings in ZED The zed_strings container stores strings in an AVL, but does not check for duplicate strings being added. Within the AVL, strings are indexed by the string value itself. avl_add() requires the node being added must not already exist in the tree, and will assert() if this is not the case. This should not cause problems in practice. ZED uses this container in two places. In zed_conf.c, it is used to store the names of enabled zedlets as zed scans the zedlet directory listing; duplicate entries cannot occur here since duplicate names cannot occur within a directory. In zed_event.c, it is used to store the environment variables (as "NAME=VALUE" strings) that will be passed to zedlets; duplicate strings here should never happen unless there is a bug resulting in a duplicate nvpair or environment variable. This commit protects against adding a duplicate to a zed_strings container by first checking for the string being added, and removing the previous entry should one exist. This implements a "last one wins" policy. This commit also changes the prototype for zed_strings_add() to allow the string key (by which it is indexed in the AVL) to differ from the string value. By adding zedlet environment variables using the variable name as the key, multiple adds for the same variable name will result in only the last value being stored. Finally, this commit routes all additions of zedlet environment variables through the updated _zed_event_add_var(). This ensures all zedlet environment variable names are properly converted. Signed-off-by: Chris Dunlap <cdunlap@llnl.gov> Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov> Closes #3042
2014-10-19 19:05:07 +00:00
_zed_event_add_var(eid, zsp, ZED_VAR_PREFIX, "ZEDLET_DIR",
"%s", zcp->zedlet_dir);
subclass = _zed_event_get_subclass(class);
Protect against adding duplicate strings in ZED The zed_strings container stores strings in an AVL, but does not check for duplicate strings being added. Within the AVL, strings are indexed by the string value itself. avl_add() requires the node being added must not already exist in the tree, and will assert() if this is not the case. This should not cause problems in practice. ZED uses this container in two places. In zed_conf.c, it is used to store the names of enabled zedlets as zed scans the zedlet directory listing; duplicate entries cannot occur here since duplicate names cannot occur within a directory. In zed_event.c, it is used to store the environment variables (as "NAME=VALUE" strings) that will be passed to zedlets; duplicate strings here should never happen unless there is a bug resulting in a duplicate nvpair or environment variable. This commit protects against adding a duplicate to a zed_strings container by first checking for the string being added, and removing the previous entry should one exist. This implements a "last one wins" policy. This commit also changes the prototype for zed_strings_add() to allow the string key (by which it is indexed in the AVL) to differ from the string value. By adding zedlet environment variables using the variable name as the key, multiple adds for the same variable name will result in only the last value being stored. Finally, this commit routes all additions of zedlet environment variables through the updated _zed_event_add_var(). This ensures all zedlet environment variable names are properly converted. Signed-off-by: Chris Dunlap <cdunlap@llnl.gov> Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov> Closes #3042
2014-10-19 19:05:07 +00:00
_zed_event_add_var(eid, zsp, ZEVENT_VAR_PREFIX, "SUBCLASS",
"%s", (subclass ? subclass : class));
_zed_event_add_time_strings(eid, zsp, etime);
zed_exec_process(eid, class, subclass,
zcp->zedlet_dir, zcp->zedlets, zsp, zcp->zevent_fd);
zed_conf_write_state(zcp, eid, etime);
zed_strings_destroy(zsp);
}
nvlist_free(nvl);
}