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.
|
2021-05-03 19:21:21 +00:00
|
|
|
* Refer to the OpenZFS git commit log for authoritative copyright attribution.
|
2015-05-06 22:56:03 +00:00
|
|
|
*
|
|
|
|
* 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 <ctype.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <fcntl.h>
|
2021-03-26 13:41:38 +00:00
|
|
|
#include <libzfs_core.h>
|
2014-01-21 21:30:03 +00:00
|
|
|
#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>
|
2016-08-31 21:46:58 +00:00
|
|
|
#include <sys/fm/fs/zfs.h>
|
2014-01-21 21:30:03 +00:00
|
|
|
#include "zed.h"
|
|
|
|
#include "zed_conf.h"
|
2016-08-31 21:46:58 +00:00
|
|
|
#include "zed_disk_event.h"
|
2020-06-11 20:25:39 +00:00
|
|
|
#include "zed_event.h"
|
2014-01-21 21:30:03 +00:00
|
|
|
#include "zed_exec.h"
|
|
|
|
#include "zed_file.h"
|
|
|
|
#include "zed_log.h"
|
|
|
|
#include "zed_strings.h"
|
|
|
|
|
2016-08-31 21:46:58 +00:00
|
|
|
#include "agents/zfs_agents.h"
|
|
|
|
|
2014-10-19 19:05:07 +00:00
|
|
|
#define MAXBUF 4096
|
|
|
|
|
2014-01-21 21:30:03 +00:00
|
|
|
/*
|
|
|
|
* Open the libzfs interface.
|
|
|
|
*/
|
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
|
|
|
int
|
2014-01-21 21:30:03 +00:00
|
|
|
zed_event_init(struct zed_conf *zcp)
|
|
|
|
{
|
|
|
|
if (!zcp)
|
|
|
|
zed_log_die("Failed zed_event_init: %s", strerror(EINVAL));
|
|
|
|
|
|
|
|
zcp->zfs_hdl = libzfs_init();
|
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
|
|
|
if (!zcp->zfs_hdl) {
|
|
|
|
if (zcp->do_idle)
|
|
|
|
return (-1);
|
2014-01-21 21:30:03 +00:00
|
|
|
zed_log_die("Failed to initialize libzfs");
|
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
|
|
|
}
|
2014-01-21 21:30:03 +00:00
|
|
|
|
2021-04-12 12:07:14 +00:00
|
|
|
zcp->zevent_fd = open(ZFS_DEV, O_RDWR | O_CLOEXEC);
|
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
|
|
|
if (zcp->zevent_fd < 0) {
|
|
|
|
if (zcp->do_idle)
|
|
|
|
return (-1);
|
2014-01-21 21:30:03 +00:00
|
|
|
zed_log_die("Failed to open \"%s\": %s",
|
|
|
|
ZFS_DEV, strerror(errno));
|
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
|
|
|
}
|
2016-08-31 21:46:58 +00:00
|
|
|
|
2016-11-07 23:01:38 +00:00
|
|
|
zfs_agent_init(zcp->zfs_hdl);
|
|
|
|
|
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
|
|
|
if (zed_disk_event_init() != 0) {
|
|
|
|
if (zcp->do_idle)
|
|
|
|
return (-1);
|
2016-08-31 21:46:58 +00:00
|
|
|
zed_log_die("Failed to initialize disk events");
|
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
|
|
|
}
|
|
|
|
|
|
|
|
return (0);
|
2014-01-21 21:30:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Close the libzfs interface.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
zed_event_fini(struct zed_conf *zcp)
|
|
|
|
{
|
|
|
|
if (!zcp)
|
|
|
|
zed_log_die("Failed zed_event_fini: %s", strerror(EINVAL));
|
|
|
|
|
2016-08-31 21:46:58 +00:00
|
|
|
zed_disk_event_fini();
|
2016-11-07 23:01:38 +00:00
|
|
|
zfs_agent_fini();
|
2016-08-31 21:46:58 +00:00
|
|
|
|
2014-01-21 21:30:03 +00:00
|
|
|
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;
|
|
|
|
}
|
2021-03-26 13:41:38 +00:00
|
|
|
|
|
|
|
zed_exec_fini();
|
2014-01-21 21:30:03 +00:00
|
|
|
}
|
|
|
|
|
2021-04-02 15:14:31 +00:00
|
|
|
static void
|
|
|
|
_bump_event_queue_length(void)
|
|
|
|
{
|
|
|
|
int zzlm = -1, wr;
|
|
|
|
char qlen_buf[12] = {0}; /* parameter is int => max "-2147483647\n" */
|
|
|
|
long int qlen;
|
|
|
|
|
|
|
|
zzlm = open("/sys/module/zfs/parameters/zfs_zevent_len_max", O_RDWR);
|
|
|
|
if (zzlm < 0)
|
|
|
|
goto done;
|
|
|
|
|
|
|
|
if (read(zzlm, qlen_buf, sizeof (qlen_buf)) < 0)
|
|
|
|
goto done;
|
|
|
|
qlen_buf[sizeof (qlen_buf) - 1] = '\0';
|
|
|
|
|
|
|
|
errno = 0;
|
|
|
|
qlen = strtol(qlen_buf, NULL, 10);
|
|
|
|
if (errno == ERANGE)
|
|
|
|
goto done;
|
|
|
|
|
|
|
|
if (qlen <= 0)
|
|
|
|
qlen = 512; /* default zfs_zevent_len_max value */
|
|
|
|
else
|
|
|
|
qlen *= 2;
|
|
|
|
|
|
|
|
if (qlen > INT_MAX)
|
|
|
|
qlen = INT_MAX;
|
|
|
|
wr = snprintf(qlen_buf, sizeof (qlen_buf), "%ld", qlen);
|
|
|
|
|
|
|
|
if (pwrite(zzlm, qlen_buf, wr, 0) < 0)
|
|
|
|
goto done;
|
|
|
|
|
|
|
|
zed_log_msg(LOG_WARNING, "Bumping queue length to %ld", qlen);
|
|
|
|
|
|
|
|
done:
|
|
|
|
if (zzlm > -1)
|
|
|
|
(void) close(zzlm);
|
|
|
|
}
|
|
|
|
|
2014-01-21 21:30:03 +00:00
|
|
|
/*
|
|
|
|
* Seek to the event specified by [saved_eid] and [saved_etime].
|
2014-09-10 21:22:39 +00:00
|
|
|
* This protects against processing a given event more than once.
|
2014-01-21 21:30:03 +00:00
|
|
|
* Return 0 upon a successful seek to the specified event, or -1 otherwise.
|
2014-09-10 21:22:39 +00:00
|
|
|
*
|
2014-01-21 21:30:03 +00:00
|
|
|
* A zevent is considered to be uniquely specified by its (eid,time) tuple.
|
2014-09-10 21:22:39 +00:00
|
|
|
* 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.
|
2014-01-21 21:30:03 +00:00
|
|
|
*/
|
|
|
|
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);
|
2021-04-02 15:14:31 +00:00
|
|
|
_bump_event_queue_length();
|
2014-01-21 21:30:03 +00:00
|
|
|
}
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2015-01-15 00:31:21 +00:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
2021-04-02 18:47:00 +00:00
|
|
|
static __attribute__((format(printf, 5, 6))) int
|
2015-01-15 00:31:21 +00:00
|
|
|
_zed_event_add_var(uint64_t eid, zed_strings_t *zsp,
|
|
|
|
const char *prefix, const char *name, const char *fmt, ...)
|
2014-01-21 21:30:03 +00:00
|
|
|
{
|
2015-01-15 00:31:21 +00:00
|
|
|
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--;
|
|
|
|
|
2016-12-21 19:27:24 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2015-01-15 00:31:21 +00:00
|
|
|
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;
|
2014-01-21 21:30:03 +00:00
|
|
|
int8_t *i8p;
|
|
|
|
uint_t nelem;
|
|
|
|
uint_t i;
|
|
|
|
char *p;
|
|
|
|
int n;
|
|
|
|
|
2015-01-15 00:31:21 +00:00
|
|
|
assert((nvp != NULL) && (nvpair_type(nvp) == DATA_TYPE_INT8_ARRAY));
|
2014-01-21 21:30:03 +00:00
|
|
|
|
2015-01-15 00:31:21 +00:00
|
|
|
name = nvpair_name(nvp);
|
2014-01-21 21:30:03 +00:00
|
|
|
(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]);
|
2015-01-15 00:31:21 +00:00
|
|
|
if ((n < 0) || (n >= buflen))
|
|
|
|
return (_zed_event_add_array_err(eid, name));
|
2014-01-21 21:30:03 +00:00
|
|
|
p += n;
|
|
|
|
buflen -= n;
|
|
|
|
}
|
|
|
|
if (nelem > 0)
|
|
|
|
*--p = '\0';
|
|
|
|
|
2015-01-15 00:31:21 +00:00
|
|
|
return (_zed_event_add_var(eid, zsp, prefix, name, "%s", buf));
|
2014-01-21 21:30:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2015-01-15 00:31:21 +00:00
|
|
|
_zed_event_add_uint8_array(uint64_t eid, zed_strings_t *zsp,
|
|
|
|
const char *prefix, nvpair_t *nvp)
|
2014-01-21 21:30:03 +00:00
|
|
|
{
|
2015-01-15 00:31:21 +00:00
|
|
|
char buf[MAXBUF];
|
|
|
|
int buflen = sizeof (buf);
|
|
|
|
const char *name;
|
2014-01-21 21:30:03 +00:00
|
|
|
uint8_t *u8p;
|
|
|
|
uint_t nelem;
|
|
|
|
uint_t i;
|
|
|
|
char *p;
|
|
|
|
int n;
|
|
|
|
|
2015-01-15 00:31:21 +00:00
|
|
|
assert((nvp != NULL) && (nvpair_type(nvp) == DATA_TYPE_UINT8_ARRAY));
|
2014-01-21 21:30:03 +00:00
|
|
|
|
2015-01-15 00:31:21 +00:00
|
|
|
name = nvpair_name(nvp);
|
2014-01-21 21:30:03 +00:00
|
|
|
(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]);
|
2015-01-15 00:31:21 +00:00
|
|
|
if ((n < 0) || (n >= buflen))
|
|
|
|
return (_zed_event_add_array_err(eid, name));
|
2014-01-21 21:30:03 +00:00
|
|
|
p += n;
|
|
|
|
buflen -= n;
|
|
|
|
}
|
|
|
|
if (nelem > 0)
|
|
|
|
*--p = '\0';
|
|
|
|
|
2015-01-15 00:31:21 +00:00
|
|
|
return (_zed_event_add_var(eid, zsp, prefix, name, "%s", buf));
|
2014-01-21 21:30:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2015-01-15 00:31:21 +00:00
|
|
|
_zed_event_add_int16_array(uint64_t eid, zed_strings_t *zsp,
|
|
|
|
const char *prefix, nvpair_t *nvp)
|
2014-01-21 21:30:03 +00:00
|
|
|
{
|
2015-01-15 00:31:21 +00:00
|
|
|
char buf[MAXBUF];
|
|
|
|
int buflen = sizeof (buf);
|
|
|
|
const char *name;
|
2014-01-21 21:30:03 +00:00
|
|
|
int16_t *i16p;
|
|
|
|
uint_t nelem;
|
|
|
|
uint_t i;
|
|
|
|
char *p;
|
|
|
|
int n;
|
|
|
|
|
2015-01-15 00:31:21 +00:00
|
|
|
assert((nvp != NULL) && (nvpair_type(nvp) == DATA_TYPE_INT16_ARRAY));
|
2014-01-21 21:30:03 +00:00
|
|
|
|
2015-01-15 00:31:21 +00:00
|
|
|
name = nvpair_name(nvp);
|
2014-01-21 21:30:03 +00:00
|
|
|
(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]);
|
2015-01-15 00:31:21 +00:00
|
|
|
if ((n < 0) || (n >= buflen))
|
|
|
|
return (_zed_event_add_array_err(eid, name));
|
2014-01-21 21:30:03 +00:00
|
|
|
p += n;
|
|
|
|
buflen -= n;
|
|
|
|
}
|
|
|
|
if (nelem > 0)
|
|
|
|
*--p = '\0';
|
|
|
|
|
2015-01-15 00:31:21 +00:00
|
|
|
return (_zed_event_add_var(eid, zsp, prefix, name, "%s", buf));
|
2014-01-21 21:30:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2015-01-15 00:31:21 +00:00
|
|
|
_zed_event_add_uint16_array(uint64_t eid, zed_strings_t *zsp,
|
|
|
|
const char *prefix, nvpair_t *nvp)
|
2014-01-21 21:30:03 +00:00
|
|
|
{
|
2015-01-15 00:31:21 +00:00
|
|
|
char buf[MAXBUF];
|
|
|
|
int buflen = sizeof (buf);
|
|
|
|
const char *name;
|
2014-01-21 21:30:03 +00:00
|
|
|
uint16_t *u16p;
|
|
|
|
uint_t nelem;
|
|
|
|
uint_t i;
|
|
|
|
char *p;
|
|
|
|
int n;
|
|
|
|
|
2015-01-15 00:31:21 +00:00
|
|
|
assert((nvp != NULL) && (nvpair_type(nvp) == DATA_TYPE_UINT16_ARRAY));
|
2014-01-21 21:30:03 +00:00
|
|
|
|
2015-01-15 00:31:21 +00:00
|
|
|
name = nvpair_name(nvp);
|
2014-01-21 21:30:03 +00:00
|
|
|
(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]);
|
2015-01-15 00:31:21 +00:00
|
|
|
if ((n < 0) || (n >= buflen))
|
|
|
|
return (_zed_event_add_array_err(eid, name));
|
2014-01-21 21:30:03 +00:00
|
|
|
p += n;
|
|
|
|
buflen -= n;
|
|
|
|
}
|
|
|
|
if (nelem > 0)
|
|
|
|
*--p = '\0';
|
|
|
|
|
2015-01-15 00:31:21 +00:00
|
|
|
return (_zed_event_add_var(eid, zsp, prefix, name, "%s", buf));
|
2014-01-21 21:30:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2015-01-15 00:31:21 +00:00
|
|
|
_zed_event_add_int32_array(uint64_t eid, zed_strings_t *zsp,
|
|
|
|
const char *prefix, nvpair_t *nvp)
|
2014-01-21 21:30:03 +00:00
|
|
|
{
|
2015-01-15 00:31:21 +00:00
|
|
|
char buf[MAXBUF];
|
|
|
|
int buflen = sizeof (buf);
|
|
|
|
const char *name;
|
2014-01-21 21:30:03 +00:00
|
|
|
int32_t *i32p;
|
|
|
|
uint_t nelem;
|
|
|
|
uint_t i;
|
|
|
|
char *p;
|
|
|
|
int n;
|
|
|
|
|
2015-01-15 00:31:21 +00:00
|
|
|
assert((nvp != NULL) && (nvpair_type(nvp) == DATA_TYPE_INT32_ARRAY));
|
2014-01-21 21:30:03 +00:00
|
|
|
|
2015-01-15 00:31:21 +00:00
|
|
|
name = nvpair_name(nvp);
|
2014-01-21 21:30:03 +00:00
|
|
|
(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]);
|
2015-01-15 00:31:21 +00:00
|
|
|
if ((n < 0) || (n >= buflen))
|
|
|
|
return (_zed_event_add_array_err(eid, name));
|
2014-01-21 21:30:03 +00:00
|
|
|
p += n;
|
|
|
|
buflen -= n;
|
|
|
|
}
|
|
|
|
if (nelem > 0)
|
|
|
|
*--p = '\0';
|
|
|
|
|
2015-01-15 00:31:21 +00:00
|
|
|
return (_zed_event_add_var(eid, zsp, prefix, name, "%s", buf));
|
2014-01-21 21:30:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2015-01-15 00:31:21 +00:00
|
|
|
_zed_event_add_uint32_array(uint64_t eid, zed_strings_t *zsp,
|
|
|
|
const char *prefix, nvpair_t *nvp)
|
2014-01-21 21:30:03 +00:00
|
|
|
{
|
2015-01-15 00:31:21 +00:00
|
|
|
char buf[MAXBUF];
|
|
|
|
int buflen = sizeof (buf);
|
|
|
|
const char *name;
|
2014-01-21 21:30:03 +00:00
|
|
|
uint32_t *u32p;
|
|
|
|
uint_t nelem;
|
|
|
|
uint_t i;
|
|
|
|
char *p;
|
|
|
|
int n;
|
|
|
|
|
2015-01-15 00:31:21 +00:00
|
|
|
assert((nvp != NULL) && (nvpair_type(nvp) == DATA_TYPE_UINT32_ARRAY));
|
2014-01-21 21:30:03 +00:00
|
|
|
|
2015-01-15 00:31:21 +00:00
|
|
|
name = nvpair_name(nvp);
|
2014-01-21 21:30:03 +00:00
|
|
|
(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]);
|
2015-01-15 00:31:21 +00:00
|
|
|
if ((n < 0) || (n >= buflen))
|
|
|
|
return (_zed_event_add_array_err(eid, name));
|
2014-01-21 21:30:03 +00:00
|
|
|
p += n;
|
|
|
|
buflen -= n;
|
|
|
|
}
|
|
|
|
if (nelem > 0)
|
|
|
|
*--p = '\0';
|
|
|
|
|
2015-01-15 00:31:21 +00:00
|
|
|
return (_zed_event_add_var(eid, zsp, prefix, name, "%s", buf));
|
2014-01-21 21:30:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2015-01-15 00:31:21 +00:00
|
|
|
_zed_event_add_int64_array(uint64_t eid, zed_strings_t *zsp,
|
|
|
|
const char *prefix, nvpair_t *nvp)
|
2014-01-21 21:30:03 +00:00
|
|
|
{
|
2015-01-15 00:31:21 +00:00
|
|
|
char buf[MAXBUF];
|
|
|
|
int buflen = sizeof (buf);
|
|
|
|
const char *name;
|
2014-01-21 21:30:03 +00:00
|
|
|
int64_t *i64p;
|
|
|
|
uint_t nelem;
|
|
|
|
uint_t i;
|
|
|
|
char *p;
|
|
|
|
int n;
|
|
|
|
|
2015-01-15 00:31:21 +00:00
|
|
|
assert((nvp != NULL) && (nvpair_type(nvp) == DATA_TYPE_INT64_ARRAY));
|
2014-01-21 21:30:03 +00:00
|
|
|
|
2015-01-15 00:31:21 +00:00
|
|
|
name = nvpair_name(nvp);
|
2014-01-21 21:30:03 +00:00
|
|
|
(void) nvpair_value_int64_array(nvp, &i64p, &nelem);
|
|
|
|
for (i = 0, p = buf; (i < nelem) && (buflen > 0); i++) {
|
2016-12-12 18:46:26 +00:00
|
|
|
n = snprintf(p, buflen, "%lld ", (u_longlong_t)i64p[i]);
|
2015-01-15 00:31:21 +00:00
|
|
|
if ((n < 0) || (n >= buflen))
|
|
|
|
return (_zed_event_add_array_err(eid, name));
|
2014-01-21 21:30:03 +00:00
|
|
|
p += n;
|
|
|
|
buflen -= n;
|
|
|
|
}
|
|
|
|
if (nelem > 0)
|
|
|
|
*--p = '\0';
|
|
|
|
|
2015-01-15 00:31:21 +00:00
|
|
|
return (_zed_event_add_var(eid, zsp, prefix, name, "%s", buf));
|
2014-01-21 21:30:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2015-01-15 00:31:21 +00:00
|
|
|
_zed_event_add_uint64_array(uint64_t eid, zed_strings_t *zsp,
|
|
|
|
const char *prefix, nvpair_t *nvp)
|
2014-01-21 21:30:03 +00:00
|
|
|
{
|
2015-01-15 00:31:21 +00:00
|
|
|
char buf[MAXBUF];
|
|
|
|
int buflen = sizeof (buf);
|
|
|
|
const char *name;
|
|
|
|
const char *fmt;
|
2014-01-21 21:30:03 +00:00
|
|
|
uint64_t *u64p;
|
|
|
|
uint_t nelem;
|
|
|
|
uint_t i;
|
|
|
|
char *p;
|
|
|
|
int n;
|
|
|
|
|
2015-01-15 00:31:21 +00:00
|
|
|
assert((nvp != NULL) && (nvpair_type(nvp) == DATA_TYPE_UINT64_ARRAY));
|
2014-01-21 21:30:03 +00:00
|
|
|
|
2015-01-15 00:31:21 +00:00
|
|
|
name = nvpair_name(nvp);
|
|
|
|
fmt = _zed_event_value_is_hex(name) ? "0x%.16llX " : "%llu ";
|
2014-01-21 21:30:03 +00:00
|
|
|
(void) nvpair_value_uint64_array(nvp, &u64p, &nelem);
|
|
|
|
for (i = 0, p = buf; (i < nelem) && (buflen > 0); i++) {
|
2016-12-12 18:46:26 +00:00
|
|
|
n = snprintf(p, buflen, fmt, (u_longlong_t)u64p[i]);
|
2015-01-15 00:31:21 +00:00
|
|
|
if ((n < 0) || (n >= buflen))
|
|
|
|
return (_zed_event_add_array_err(eid, name));
|
2014-01-21 21:30:03 +00:00
|
|
|
p += n;
|
|
|
|
buflen -= n;
|
|
|
|
}
|
|
|
|
if (nelem > 0)
|
|
|
|
*--p = '\0';
|
|
|
|
|
2015-01-15 00:31:21 +00:00
|
|
|
return (_zed_event_add_var(eid, zsp, prefix, name, "%s", buf));
|
2014-01-21 21:30:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2015-01-15 00:31:21 +00:00
|
|
|
_zed_event_add_string_array(uint64_t eid, zed_strings_t *zsp,
|
|
|
|
const char *prefix, nvpair_t *nvp)
|
2014-01-21 21:30:03 +00:00
|
|
|
{
|
2015-01-15 00:31:21 +00:00
|
|
|
char buf[MAXBUF];
|
|
|
|
int buflen = sizeof (buf);
|
|
|
|
const char *name;
|
2014-01-21 21:30:03 +00:00
|
|
|
char **strp;
|
|
|
|
uint_t nelem;
|
|
|
|
uint_t i;
|
|
|
|
char *p;
|
|
|
|
int n;
|
|
|
|
|
2015-01-15 00:31:21 +00:00
|
|
|
assert((nvp != NULL) && (nvpair_type(nvp) == DATA_TYPE_STRING_ARRAY));
|
2014-01-21 21:30:03 +00:00
|
|
|
|
2015-01-15 00:31:21 +00:00
|
|
|
name = nvpair_name(nvp);
|
2014-01-21 21:30:03 +00:00
|
|
|
(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>");
|
2015-01-15 00:31:21 +00:00
|
|
|
if ((n < 0) || (n >= buflen))
|
|
|
|
return (_zed_event_add_array_err(eid, name));
|
2014-01-21 21:30:03 +00:00
|
|
|
p += n;
|
|
|
|
buflen -= n;
|
|
|
|
}
|
|
|
|
if (nelem > 0)
|
|
|
|
*--p = '\0';
|
|
|
|
|
2015-01-15 00:31:21 +00:00
|
|
|
return (_zed_event_add_var(eid, zsp, prefix, name, "%s", buf));
|
2014-10-19 19:05:07 +00:00
|
|
|
}
|
|
|
|
|
2014-01-21 21:30:03 +00:00
|
|
|
/*
|
|
|
|
* Convert the nvpair [nvp] to a string which is added to the environment
|
2014-09-10 21:22:39 +00:00
|
|
|
* of the child process.
|
2014-01-21 21:30:03 +00:00
|
|
|
* Return 0 on success, -1 on error.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
_zed_event_add_nvpair(uint64_t eid, zed_strings_t *zsp, nvpair_t *nvp)
|
|
|
|
{
|
|
|
|
const char *name;
|
|
|
|
data_type_t type;
|
2015-01-15 00:31:21 +00:00
|
|
|
const char *prefix = ZEVENT_VAR_PREFIX;
|
2014-01-21 21:30:03 +00:00
|
|
|
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:
|
2015-01-15 00:31:21 +00:00
|
|
|
_zed_event_add_var(eid, zsp, prefix, name, "%s", "1");
|
2014-01-21 21:30:03 +00:00
|
|
|
break;
|
|
|
|
case DATA_TYPE_BOOLEAN_VALUE:
|
|
|
|
(void) nvpair_value_boolean_value(nvp, &b);
|
2015-01-15 00:31:21 +00:00
|
|
|
_zed_event_add_var(eid, zsp, prefix, name, "%s", b ? "1" : "0");
|
2014-01-21 21:30:03 +00:00
|
|
|
break;
|
|
|
|
case DATA_TYPE_BYTE:
|
|
|
|
(void) nvpair_value_byte(nvp, &i8);
|
2015-01-15 00:31:21 +00:00
|
|
|
_zed_event_add_var(eid, zsp, prefix, name, "%d", i8);
|
2014-01-21 21:30:03 +00:00
|
|
|
break;
|
|
|
|
case DATA_TYPE_INT8:
|
2016-12-12 18:46:26 +00:00
|
|
|
(void) nvpair_value_int8(nvp, (int8_t *)&i8);
|
2015-01-15 00:31:21 +00:00
|
|
|
_zed_event_add_var(eid, zsp, prefix, name, "%d", i8);
|
2014-01-21 21:30:03 +00:00
|
|
|
break;
|
|
|
|
case DATA_TYPE_UINT8:
|
|
|
|
(void) nvpair_value_uint8(nvp, &i8);
|
2015-01-15 00:31:21 +00:00
|
|
|
_zed_event_add_var(eid, zsp, prefix, name, "%u", i8);
|
2014-01-21 21:30:03 +00:00
|
|
|
break;
|
|
|
|
case DATA_TYPE_INT16:
|
2016-12-12 18:46:26 +00:00
|
|
|
(void) nvpair_value_int16(nvp, (int16_t *)&i16);
|
2015-01-15 00:31:21 +00:00
|
|
|
_zed_event_add_var(eid, zsp, prefix, name, "%d", i16);
|
2014-01-21 21:30:03 +00:00
|
|
|
break;
|
|
|
|
case DATA_TYPE_UINT16:
|
|
|
|
(void) nvpair_value_uint16(nvp, &i16);
|
2015-01-15 00:31:21 +00:00
|
|
|
_zed_event_add_var(eid, zsp, prefix, name, "%u", i16);
|
2014-01-21 21:30:03 +00:00
|
|
|
break;
|
|
|
|
case DATA_TYPE_INT32:
|
2016-12-12 18:46:26 +00:00
|
|
|
(void) nvpair_value_int32(nvp, (int32_t *)&i32);
|
2015-01-15 00:31:21 +00:00
|
|
|
_zed_event_add_var(eid, zsp, prefix, name, "%d", i32);
|
2014-01-21 21:30:03 +00:00
|
|
|
break;
|
|
|
|
case DATA_TYPE_UINT32:
|
|
|
|
(void) nvpair_value_uint32(nvp, &i32);
|
2015-01-15 00:31:21 +00:00
|
|
|
_zed_event_add_var(eid, zsp, prefix, name, "%u", i32);
|
2014-01-21 21:30:03 +00:00
|
|
|
break;
|
|
|
|
case DATA_TYPE_INT64:
|
2016-12-12 18:46:26 +00:00
|
|
|
(void) nvpair_value_int64(nvp, (int64_t *)&i64);
|
2015-01-15 00:31:21 +00:00
|
|
|
_zed_event_add_var(eid, zsp, prefix, name,
|
2016-12-12 18:46:26 +00:00
|
|
|
"%lld", (longlong_t)i64);
|
2014-01-21 21:30:03 +00:00
|
|
|
break;
|
|
|
|
case DATA_TYPE_UINT64:
|
|
|
|
(void) nvpair_value_uint64(nvp, &i64);
|
2015-01-15 00:31:21 +00:00
|
|
|
_zed_event_add_var(eid, zsp, prefix, name,
|
|
|
|
(_zed_event_value_is_hex(name) ? "0x%.16llX" : "%llu"),
|
2016-12-12 18:46:26 +00:00
|
|
|
(u_longlong_t)i64);
|
2016-08-31 21:46:58 +00:00
|
|
|
/*
|
|
|
|
* 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));
|
2017-04-03 21:23:02 +00:00
|
|
|
} else
|
|
|
|
/*
|
|
|
|
* shadow readable strings for pool state
|
|
|
|
*/
|
|
|
|
if (strcmp(name, FM_EREPORT_PAYLOAD_ZFS_POOL_STATE) == 0) {
|
|
|
|
char alt[32];
|
|
|
|
|
|
|
|
(void) snprintf(alt, sizeof (alt), "%s_str", name);
|
|
|
|
_zed_event_add_var(eid, zsp, prefix, alt, "%s",
|
2017-04-05 20:21:10 +00:00
|
|
|
zpool_pool_state_to_name(i64));
|
2016-08-31 21:46:58 +00:00
|
|
|
}
|
2014-01-21 21:30:03 +00:00
|
|
|
break;
|
|
|
|
case DATA_TYPE_DOUBLE:
|
|
|
|
(void) nvpair_value_double(nvp, &d);
|
2015-01-15 00:31:21 +00:00
|
|
|
_zed_event_add_var(eid, zsp, prefix, name, "%g", d);
|
2014-01-21 21:30:03 +00:00
|
|
|
break;
|
|
|
|
case DATA_TYPE_HRTIME:
|
2016-12-12 18:46:26 +00:00
|
|
|
(void) nvpair_value_hrtime(nvp, (hrtime_t *)&i64);
|
2015-01-15 00:31:21 +00:00
|
|
|
_zed_event_add_var(eid, zsp, prefix, name,
|
2016-12-12 18:46:26 +00:00
|
|
|
"%llu", (u_longlong_t)i64);
|
2014-01-21 21:30:03 +00:00
|
|
|
break;
|
|
|
|
case DATA_TYPE_STRING:
|
|
|
|
(void) nvpair_value_string(nvp, &str);
|
2015-01-15 00:31:21 +00:00
|
|
|
_zed_event_add_var(eid, zsp, prefix, name,
|
|
|
|
"%s", (str ? str : "<NULL>"));
|
2014-01-21 21:30:03 +00:00
|
|
|
break;
|
|
|
|
case DATA_TYPE_INT8_ARRAY:
|
2015-01-15 00:31:21 +00:00
|
|
|
_zed_event_add_int8_array(eid, zsp, prefix, nvp);
|
2014-01-21 21:30:03 +00:00
|
|
|
break;
|
|
|
|
case DATA_TYPE_UINT8_ARRAY:
|
2015-01-15 00:31:21 +00:00
|
|
|
_zed_event_add_uint8_array(eid, zsp, prefix, nvp);
|
2014-01-21 21:30:03 +00:00
|
|
|
break;
|
|
|
|
case DATA_TYPE_INT16_ARRAY:
|
2015-01-15 00:31:21 +00:00
|
|
|
_zed_event_add_int16_array(eid, zsp, prefix, nvp);
|
2014-01-21 21:30:03 +00:00
|
|
|
break;
|
|
|
|
case DATA_TYPE_UINT16_ARRAY:
|
2015-01-15 00:31:21 +00:00
|
|
|
_zed_event_add_uint16_array(eid, zsp, prefix, nvp);
|
2014-01-21 21:30:03 +00:00
|
|
|
break;
|
|
|
|
case DATA_TYPE_INT32_ARRAY:
|
2015-01-15 00:31:21 +00:00
|
|
|
_zed_event_add_int32_array(eid, zsp, prefix, nvp);
|
2014-01-21 21:30:03 +00:00
|
|
|
break;
|
|
|
|
case DATA_TYPE_UINT32_ARRAY:
|
2015-01-15 00:31:21 +00:00
|
|
|
_zed_event_add_uint32_array(eid, zsp, prefix, nvp);
|
2014-01-21 21:30:03 +00:00
|
|
|
break;
|
|
|
|
case DATA_TYPE_INT64_ARRAY:
|
2015-01-15 00:31:21 +00:00
|
|
|
_zed_event_add_int64_array(eid, zsp, prefix, nvp);
|
2014-01-21 21:30:03 +00:00
|
|
|
break;
|
|
|
|
case DATA_TYPE_UINT64_ARRAY:
|
2015-01-15 00:31:21 +00:00
|
|
|
_zed_event_add_uint64_array(eid, zsp, prefix, nvp);
|
2014-01-21 21:30:03 +00:00
|
|
|
break;
|
|
|
|
case DATA_TYPE_STRING_ARRAY:
|
2015-01-15 00:31:21 +00:00
|
|
|
_zed_event_add_string_array(eid, zsp, prefix, nvp);
|
2014-01-21 21:30:03 +00:00
|
|
|
break;
|
2021-04-02 18:47:00 +00:00
|
|
|
case DATA_TYPE_NVLIST:
|
|
|
|
case DATA_TYPE_BOOLEAN_ARRAY:
|
|
|
|
case DATA_TYPE_BYTE_ARRAY:
|
2014-01-21 21:30:03 +00:00
|
|
|
case DATA_TYPE_NVLIST_ARRAY:
|
2021-04-02 18:47:00 +00:00
|
|
|
_zed_event_add_var(eid, zsp, prefix, name, "_NOT_IMPLEMENTED_");
|
2014-01-21 21:30:03 +00:00
|
|
|
break;
|
|
|
|
default:
|
2015-01-15 00:31:21 +00:00
|
|
|
errno = EINVAL;
|
2014-01-21 21:30:03 +00:00
|
|
|
zed_log_msg(LOG_WARNING,
|
|
|
|
"Failed to convert nvpair \"%s\" for eid=%llu: "
|
|
|
|
"Unrecognized type=%u", name, eid, (unsigned int) type);
|
2015-01-15 00:31:21 +00:00
|
|
|
break;
|
2014-01-21 21:30:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Restrict various environment variables to safe and sane values
|
2018-02-23 19:38:05 +00:00
|
|
|
* when constructing the environment for the child process, unless
|
|
|
|
* we're running with a custom $PATH (like under the ZFS test suite).
|
2014-09-10 21:22:39 +00:00
|
|
|
*
|
2014-01-21 21:30:03 +00:00
|
|
|
* Reference: Secure Programming Cookbook by Viega & Messier, Section 1.1.
|
|
|
|
*/
|
|
|
|
static void
|
2018-02-23 19:38:05 +00:00
|
|
|
_zed_event_add_env_restrict(uint64_t eid, zed_strings_t *zsp,
|
|
|
|
const char *path)
|
2014-01-21 21:30:03 +00:00
|
|
|
{
|
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 }
|
2014-01-21 21:30:03 +00:00
|
|
|
};
|
2018-02-23 19:38:05 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If we have a custom $PATH, use the default ZFS binary locations
|
|
|
|
* instead of the hard-coded ones.
|
|
|
|
*/
|
|
|
|
const char *env_path[][2] = {
|
|
|
|
{ "IFS", " \t\n" },
|
|
|
|
{ "PATH", NULL }, /* $PATH copied in later on */
|
|
|
|
{ "ZDB", "zdb" },
|
|
|
|
{ "ZED", "zed" },
|
|
|
|
{ "ZFS", "zfs" },
|
|
|
|
{ "ZINJECT", "zinject" },
|
|
|
|
{ "ZPOOL", "zpool" },
|
|
|
|
{ "ZFS_ALIAS", ZFS_META_ALIAS },
|
|
|
|
{ "ZFS_VERSION", ZFS_META_VERSION },
|
|
|
|
{ "ZFS_RELEASE", ZFS_META_RELEASE },
|
|
|
|
{ NULL, NULL }
|
|
|
|
};
|
2014-10-19 19:05:07 +00:00
|
|
|
const char *(*pa)[2];
|
2014-01-21 21:30:03 +00:00
|
|
|
|
|
|
|
assert(zsp != NULL);
|
|
|
|
|
2018-02-23 19:38:05 +00:00
|
|
|
pa = path != NULL ? env_path : env_restrict;
|
|
|
|
|
|
|
|
for (; *(*pa); pa++) {
|
|
|
|
/* Use our custom $PATH if we have one */
|
|
|
|
if (path != NULL && strcmp((*pa)[0], "PATH") == 0)
|
|
|
|
(*pa)[1] = path;
|
|
|
|
|
2014-10-19 19:05:07 +00:00
|
|
|
_zed_event_add_var(eid, zsp, NULL, (*pa)[0], "%s", (*pa)[1]);
|
2014-01-21 21:30:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Preserve specified variables from the parent environment
|
2014-09-10 21:22:39 +00:00
|
|
|
* when constructing the environment for the child process.
|
|
|
|
*
|
2014-01-21 21:30:03 +00:00
|
|
|
* 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
|
|
|
|
};
|
2014-10-19 19:05:07 +00:00
|
|
|
const char **keyp;
|
|
|
|
const char *val;
|
2014-01-21 21:30:03 +00:00
|
|
|
|
|
|
|
assert(zsp != NULL);
|
|
|
|
|
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);
|
2014-01-21 21:30:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Compute the "subclass" by removing the first 3 components of [class]
|
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.
|
2014-01-21 21:30:03 +00:00
|
|
|
*/
|
|
|
|
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
|
2014-09-10 21:22:39 +00:00
|
|
|
* 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.
|
2014-01-21 21:30:03 +00:00
|
|
|
*/
|
|
|
|
static void
|
|
|
|
_zed_event_add_time_strings(uint64_t eid, zed_strings_t *zsp, int64_t etime[])
|
|
|
|
{
|
2022-04-04 12:07:26 +00:00
|
|
|
struct tm stp;
|
2014-01-21 21:30:03 +00:00
|
|
|
char buf[32];
|
|
|
|
|
|
|
|
assert(zsp != NULL);
|
|
|
|
assert(etime != NULL);
|
|
|
|
|
2014-10-19 19:05:07 +00:00
|
|
|
_zed_event_add_var(eid, zsp, ZEVENT_VAR_PREFIX, "TIME_SECS",
|
2022-04-04 12:07:26 +00:00
|
|
|
"%" PRId64, etime[0]);
|
2014-10-19 19:05:07 +00:00
|
|
|
_zed_event_add_var(eid, zsp, ZEVENT_VAR_PREFIX, "TIME_NSECS",
|
2022-04-04 12:07:26 +00:00
|
|
|
"%" PRId64, etime[1]);
|
2014-01-21 21:30:03 +00:00
|
|
|
|
2022-04-04 12:07:26 +00:00
|
|
|
if (!localtime_r((const time_t *) &etime[0], &stp)) {
|
2014-01-21 21:30:03 +00:00
|
|
|
zed_log_msg(LOG_WARNING, "Failed to add %s%s for eid=%llu: %s",
|
|
|
|
ZEVENT_VAR_PREFIX, "TIME_STRING", eid, "localtime error");
|
2022-04-04 12:07:26 +00:00
|
|
|
} else if (!strftime(buf, sizeof (buf), "%Y-%m-%d %H:%M:%S%z", &stp)) {
|
2014-01-21 21:30:03 +00:00
|
|
|
zed_log_msg(LOG_WARNING, "Failed to add %s%s for eid=%llu: %s",
|
|
|
|
ZEVENT_VAR_PREFIX, "TIME_STRING", eid, "strftime error");
|
|
|
|
} else {
|
2014-10-19 19:05:07 +00:00
|
|
|
_zed_event_add_var(eid, zsp, ZEVENT_VAR_PREFIX, "TIME_STRING",
|
|
|
|
"%s", buf);
|
2014-01-21 21:30:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Service the next zevent, blocking until one is available.
|
|
|
|
*/
|
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
|
|
|
int
|
2014-01-21 21:30:03 +00:00
|
|
|
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));
|
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
|
|
|
return (EINVAL);
|
2014-01-21 21:30:03 +00:00
|
|
|
}
|
|
|
|
rv = zpool_events_next(zcp->zfs_hdl, &nvl, &n_dropped, ZEVENT_NONE,
|
|
|
|
zcp->zevent_fd);
|
|
|
|
|
|
|
|
if ((rv != 0) || !nvl)
|
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
|
|
|
return (errno);
|
2014-01-21 21:30:03 +00:00
|
|
|
|
|
|
|
if (n_dropped > 0) {
|
|
|
|
zed_log_msg(LOG_WARNING, "Missed %d events", n_dropped);
|
2021-04-02 15:14:31 +00:00
|
|
|
_bump_event_queue_length();
|
2014-01-21 21:30:03 +00:00
|
|
|
}
|
|
|
|
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 {
|
2016-08-31 21:46:58 +00:00
|
|
|
/* let internal modules see this event first */
|
2016-11-07 23:01:38 +00:00
|
|
|
zfs_agent_post_event(class, NULL, nvl);
|
2016-08-31 21:46:58 +00:00
|
|
|
|
2014-01-21 21:30:03 +00:00
|
|
|
zsp = zed_strings_create();
|
|
|
|
|
|
|
|
nvp = NULL;
|
|
|
|
while ((nvp = nvlist_next_nvpair(nvl, nvp)))
|
|
|
|
_zed_event_add_nvpair(eid, zsp, nvp);
|
|
|
|
|
2018-02-23 19:38:05 +00:00
|
|
|
_zed_event_add_env_restrict(eid, zsp, zcp->path);
|
2014-01-21 21:30:03 +00:00
|
|
|
_zed_event_add_env_preserve(eid, zsp);
|
|
|
|
|
2014-10-19 19:05:07 +00:00
|
|
|
_zed_event_add_var(eid, zsp, ZED_VAR_PREFIX, "PID",
|
2016-12-12 18:46:26 +00:00
|
|
|
"%d", (int)getpid());
|
2014-10-19 19:05:07 +00:00
|
|
|
_zed_event_add_var(eid, zsp, ZED_VAR_PREFIX, "ZEDLET_DIR",
|
|
|
|
"%s", zcp->zedlet_dir);
|
2014-01-21 21:30:03 +00:00
|
|
|
subclass = _zed_event_get_subclass(class);
|
2014-10-19 19:05:07 +00:00
|
|
|
_zed_event_add_var(eid, zsp, ZEVENT_VAR_PREFIX, "SUBCLASS",
|
|
|
|
"%s", (subclass ? subclass : class));
|
2016-10-19 19:55:59 +00:00
|
|
|
|
2014-01-21 21:30:03 +00:00
|
|
|
_zed_event_add_time_strings(eid, zsp, etime);
|
|
|
|
|
2021-03-29 13:21:54 +00:00
|
|
|
zed_exec_process(eid, class, subclass, zcp, zsp);
|
2014-01-21 21:30:03 +00:00
|
|
|
|
|
|
|
zed_conf_write_state(zcp, eid, etime);
|
|
|
|
|
|
|
|
zed_strings_destroy(zsp);
|
|
|
|
}
|
|
|
|
nvlist_free(nvl);
|
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
|
|
|
return (0);
|
2014-01-21 21:30:03 +00:00
|
|
|
}
|