| /* |
| * Copyright (c) 2010 Pawel Jakub Dawidek <pjd@FreeBSD.org> |
| * Copyright (c) 2020 iXsystems, Inc. |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND |
| * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE |
| * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
| * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| * SUCH DAMAGE. |
| */ |
| |
| #include <sys/cdefs.h> |
| __FBSDID("$FreeBSD$"); |
| |
| #include <sys/types.h> |
| #include <sys/param.h> |
| #include <sys/kernel.h> |
| #include <sys/systm.h> |
| #include <sys/malloc.h> |
| #include <sys/kmem.h> |
| #include <sys/list.h> |
| #include <sys/proc.h> |
| #include <sys/sbuf.h> |
| #include <sys/nvpair.h> |
| #include <sys/sunddi.h> |
| #include <sys/sysevent.h> |
| #include <sys/fm/protocol.h> |
| #include <sys/fm/util.h> |
| #include <sys/bus.h> |
| |
| static int |
| log_sysevent(nvlist_t *event) |
| { |
| struct sbuf *sb; |
| const char *type; |
| char typestr[128]; |
| nvpair_t *elem = NULL; |
| |
| sb = sbuf_new_auto(); |
| if (sb == NULL) |
| return (ENOMEM); |
| type = NULL; |
| |
| while ((elem = nvlist_next_nvpair(event, elem)) != NULL) { |
| switch (nvpair_type(elem)) { |
| case DATA_TYPE_BOOLEAN: |
| { |
| boolean_t value; |
| |
| (void) nvpair_value_boolean_value(elem, &value); |
| sbuf_printf(sb, " %s=%s", nvpair_name(elem), |
| value ? "true" : "false"); |
| break; |
| } |
| case DATA_TYPE_UINT8: |
| { |
| uint8_t value; |
| |
| (void) nvpair_value_uint8(elem, &value); |
| sbuf_printf(sb, " %s=%hhu", nvpair_name(elem), value); |
| break; |
| } |
| case DATA_TYPE_INT32: |
| { |
| int32_t value; |
| |
| (void) nvpair_value_int32(elem, &value); |
| sbuf_printf(sb, " %s=%jd", nvpair_name(elem), |
| (intmax_t)value); |
| break; |
| } |
| case DATA_TYPE_UINT32: |
| { |
| uint32_t value; |
| |
| (void) nvpair_value_uint32(elem, &value); |
| sbuf_printf(sb, " %s=%ju", nvpair_name(elem), |
| (uintmax_t)value); |
| break; |
| } |
| case DATA_TYPE_INT64: |
| { |
| int64_t value; |
| |
| (void) nvpair_value_int64(elem, &value); |
| sbuf_printf(sb, " %s=%jd", nvpair_name(elem), |
| (intmax_t)value); |
| break; |
| } |
| case DATA_TYPE_UINT64: |
| { |
| uint64_t value; |
| |
| (void) nvpair_value_uint64(elem, &value); |
| sbuf_printf(sb, " %s=%ju", nvpair_name(elem), |
| (uintmax_t)value); |
| break; |
| } |
| case DATA_TYPE_STRING: |
| { |
| char *value; |
| |
| (void) nvpair_value_string(elem, &value); |
| sbuf_printf(sb, " %s=%s", nvpair_name(elem), value); |
| if (strcmp(FM_CLASS, nvpair_name(elem)) == 0) |
| type = value; |
| break; |
| } |
| case DATA_TYPE_UINT8_ARRAY: |
| { |
| uint8_t *value; |
| uint_t ii, nelem; |
| |
| (void) nvpair_value_uint8_array(elem, &value, &nelem); |
| sbuf_printf(sb, " %s=", nvpair_name(elem)); |
| for (ii = 0; ii < nelem; ii++) |
| sbuf_printf(sb, "%02hhx", value[ii]); |
| break; |
| } |
| case DATA_TYPE_UINT16_ARRAY: |
| { |
| uint16_t *value; |
| uint_t ii, nelem; |
| |
| (void) nvpair_value_uint16_array(elem, &value, &nelem); |
| sbuf_printf(sb, " %s=", nvpair_name(elem)); |
| for (ii = 0; ii < nelem; ii++) |
| sbuf_printf(sb, "%04hx", value[ii]); |
| break; |
| } |
| case DATA_TYPE_UINT32_ARRAY: |
| { |
| uint32_t *value; |
| uint_t ii, nelem; |
| |
| (void) nvpair_value_uint32_array(elem, &value, &nelem); |
| sbuf_printf(sb, " %s=", nvpair_name(elem)); |
| for (ii = 0; ii < nelem; ii++) |
| sbuf_printf(sb, "%08jx", (uintmax_t)value[ii]); |
| break; |
| } |
| case DATA_TYPE_INT64_ARRAY: |
| { |
| int64_t *value; |
| uint_t ii, nelem; |
| |
| (void) nvpair_value_int64_array(elem, &value, &nelem); |
| sbuf_printf(sb, " %s=", nvpair_name(elem)); |
| for (ii = 0; ii < nelem; ii++) |
| sbuf_printf(sb, "%016lld", |
| (long long)value[ii]); |
| break; |
| } |
| case DATA_TYPE_UINT64_ARRAY: |
| { |
| uint64_t *value; |
| uint_t ii, nelem; |
| |
| (void) nvpair_value_uint64_array(elem, &value, &nelem); |
| sbuf_printf(sb, " %s=", nvpair_name(elem)); |
| for (ii = 0; ii < nelem; ii++) |
| sbuf_printf(sb, "%016jx", (uintmax_t)value[ii]); |
| break; |
| } |
| case DATA_TYPE_STRING_ARRAY: |
| { |
| char **strarr; |
| uint_t ii, nelem; |
| |
| (void) nvpair_value_string_array(elem, &strarr, &nelem); |
| |
| for (ii = 0; ii < nelem; ii++) { |
| if (strarr[ii] == NULL) { |
| sbuf_printf(sb, " <NULL>"); |
| continue; |
| } |
| |
| sbuf_printf(sb, " %s", strarr[ii]); |
| if (strcmp(FM_CLASS, strarr[ii]) == 0) |
| type = strarr[ii]; |
| } |
| break; |
| } |
| case DATA_TYPE_NVLIST: |
| /* XXX - requires recursing in log_sysevent */ |
| break; |
| default: |
| printf("%s: type %d is not implemented\n", __func__, |
| nvpair_type(elem)); |
| break; |
| } |
| } |
| |
| if (sbuf_finish(sb) != 0) { |
| sbuf_delete(sb); |
| return (ENOMEM); |
| } |
| |
| if (type == NULL) |
| type = ""; |
| if (strncmp(type, "ESC_ZFS_", 8) == 0) { |
| snprintf(typestr, sizeof (typestr), "misc.fs.zfs.%s", type + 8); |
| type = typestr; |
| } |
| devctl_notify("ZFS", "ZFS", type, sbuf_data(sb)); |
| sbuf_delete(sb); |
| |
| return (0); |
| } |
| |
| static void |
| sysevent_worker(void *arg __unused) |
| { |
| zfs_zevent_t *ze; |
| nvlist_t *event; |
| uint64_t dropped = 0; |
| uint64_t dst_size; |
| int error; |
| |
| zfs_zevent_init(&ze); |
| for (;;) { |
| dst_size = 131072; |
| dropped = 0; |
| event = NULL; |
| error = zfs_zevent_next(ze, &event, |
| &dst_size, &dropped); |
| if (error) { |
| error = zfs_zevent_wait(ze); |
| if (error == ESHUTDOWN) |
| break; |
| } else { |
| VERIFY3P(event, !=, NULL); |
| log_sysevent(event); |
| nvlist_free(event); |
| } |
| } |
| |
| /* |
| * We avoid zfs_zevent_destroy() here because we're otherwise racing |
| * against fm_fini() destroying the zevent_lock. zfs_zevent_destroy() |
| * will currently only clear `ze->ze_zevent` from an event list then |
| * free `ze`, so just inline the free() here -- events have already |
| * been drained. |
| */ |
| VERIFY3P(ze->ze_zevent, ==, NULL); |
| kmem_free(ze, sizeof (zfs_zevent_t)); |
| |
| kthread_exit(); |
| } |
| |
| void |
| ddi_sysevent_init(void) |
| { |
| kproc_kthread_add(sysevent_worker, NULL, &system_proc, NULL, 0, 0, |
| "zfskern", "sysevent"); |
| } |