1 // SPDX-License-Identifier: BSD-2-Clause 2 /* 3 * Copyright (c) 2010 Pawel Jakub Dawidek <pjd@FreeBSD.org> 4 * Copyright (c) 2020 iXsystems, Inc. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/types.h> 30 #include <sys/param.h> 31 #include <sys/kernel.h> 32 #include <sys/systm.h> 33 #include <sys/malloc.h> 34 #include <sys/kmem.h> 35 #include <sys/list.h> 36 #include <sys/proc.h> 37 #include <sys/sbuf.h> 38 #include <sys/nvpair.h> 39 #include <sys/sunddi.h> 40 #include <sys/sysevent.h> 41 #include <sys/fm/protocol.h> 42 #include <sys/fm/util.h> 43 #include <sys/bus.h> 44 45 static int 46 log_sysevent(nvlist_t *event) 47 { 48 struct sbuf *sb; 49 const char *type; 50 char typestr[128]; 51 nvpair_t *elem = NULL; 52 53 sb = sbuf_new_auto(); 54 if (sb == NULL) 55 return (ENOMEM); 56 type = NULL; 57 58 while ((elem = nvlist_next_nvpair(event, elem)) != NULL) { 59 switch (nvpair_type(elem)) { 60 case DATA_TYPE_BOOLEAN: 61 { 62 boolean_t value; 63 64 (void) nvpair_value_boolean_value(elem, &value); 65 sbuf_printf(sb, " %s=%s", nvpair_name(elem), 66 value ? "true" : "false"); 67 break; 68 } 69 case DATA_TYPE_UINT8: 70 { 71 uint8_t value; 72 73 (void) nvpair_value_uint8(elem, &value); 74 sbuf_printf(sb, " %s=%hhu", nvpair_name(elem), value); 75 break; 76 } 77 case DATA_TYPE_INT32: 78 { 79 int32_t value; 80 81 (void) nvpair_value_int32(elem, &value); 82 sbuf_printf(sb, " %s=%jd", nvpair_name(elem), 83 (intmax_t)value); 84 break; 85 } 86 case DATA_TYPE_UINT32: 87 { 88 uint32_t value; 89 90 (void) nvpair_value_uint32(elem, &value); 91 sbuf_printf(sb, " %s=%ju", nvpair_name(elem), 92 (uintmax_t)value); 93 break; 94 } 95 case DATA_TYPE_INT64: 96 { 97 int64_t value; 98 99 (void) nvpair_value_int64(elem, &value); 100 sbuf_printf(sb, " %s=%jd", nvpair_name(elem), 101 (intmax_t)value); 102 break; 103 } 104 case DATA_TYPE_UINT64: 105 { 106 uint64_t value; 107 108 (void) nvpair_value_uint64(elem, &value); 109 sbuf_printf(sb, " %s=%ju", nvpair_name(elem), 110 (uintmax_t)value); 111 break; 112 } 113 case DATA_TYPE_STRING: 114 { 115 const char *value; 116 117 (void) nvpair_value_string(elem, &value); 118 sbuf_printf(sb, " %s=%s", nvpair_name(elem), value); 119 if (strcmp(FM_CLASS, nvpair_name(elem)) == 0) 120 type = value; 121 break; 122 } 123 case DATA_TYPE_UINT8_ARRAY: 124 { 125 uint8_t *value; 126 uint_t ii, nelem; 127 128 (void) nvpair_value_uint8_array(elem, &value, &nelem); 129 sbuf_printf(sb, " %s=", nvpair_name(elem)); 130 for (ii = 0; ii < nelem; ii++) 131 sbuf_printf(sb, "%02hhx", value[ii]); 132 break; 133 } 134 case DATA_TYPE_UINT16_ARRAY: 135 { 136 uint16_t *value; 137 uint_t ii, nelem; 138 139 (void) nvpair_value_uint16_array(elem, &value, &nelem); 140 sbuf_printf(sb, " %s=", nvpair_name(elem)); 141 for (ii = 0; ii < nelem; ii++) 142 sbuf_printf(sb, "%04hx", value[ii]); 143 break; 144 } 145 case DATA_TYPE_UINT32_ARRAY: 146 { 147 uint32_t *value; 148 uint_t ii, nelem; 149 150 (void) nvpair_value_uint32_array(elem, &value, &nelem); 151 sbuf_printf(sb, " %s=", nvpair_name(elem)); 152 for (ii = 0; ii < nelem; ii++) 153 sbuf_printf(sb, "%08jx", (uintmax_t)value[ii]); 154 break; 155 } 156 case DATA_TYPE_INT64_ARRAY: 157 { 158 int64_t *value; 159 uint_t ii, nelem; 160 161 (void) nvpair_value_int64_array(elem, &value, &nelem); 162 sbuf_printf(sb, " %s=", nvpair_name(elem)); 163 for (ii = 0; ii < nelem; ii++) 164 sbuf_printf(sb, "%016lld", 165 (long long)value[ii]); 166 break; 167 } 168 case DATA_TYPE_UINT64_ARRAY: 169 { 170 uint64_t *value; 171 uint_t ii, nelem; 172 173 (void) nvpair_value_uint64_array(elem, &value, &nelem); 174 sbuf_printf(sb, " %s=", nvpair_name(elem)); 175 for (ii = 0; ii < nelem; ii++) 176 sbuf_printf(sb, "%016jx", (uintmax_t)value[ii]); 177 break; 178 } 179 case DATA_TYPE_STRING_ARRAY: 180 { 181 const char **strarr; 182 uint_t ii, nelem; 183 184 (void) nvpair_value_string_array(elem, &strarr, &nelem); 185 186 for (ii = 0; ii < nelem; ii++) { 187 if (strarr[ii] == NULL) { 188 sbuf_printf(sb, " <NULL>"); 189 continue; 190 } 191 192 sbuf_printf(sb, " %s", strarr[ii]); 193 if (strcmp(FM_CLASS, strarr[ii]) == 0) 194 type = strarr[ii]; 195 } 196 break; 197 } 198 case DATA_TYPE_NVLIST: 199 /* XXX - requires recursing in log_sysevent */ 200 break; 201 default: 202 printf("%s: type %d is not implemented\n", __func__, 203 nvpair_type(elem)); 204 break; 205 } 206 } 207 208 if (sbuf_finish(sb) != 0) { 209 sbuf_delete(sb); 210 return (ENOMEM); 211 } 212 213 if (type == NULL) 214 type = ""; 215 if (strncmp(type, "ESC_ZFS_", 8) == 0) { 216 snprintf(typestr, sizeof (typestr), "misc.fs.zfs.%s", type + 8); 217 type = typestr; 218 } 219 devctl_notify("ZFS", "ZFS", type, sbuf_data(sb)); 220 sbuf_delete(sb); 221 222 return (0); 223 } 224 225 static void 226 sysevent_worker(void *arg __unused) 227 { 228 zfs_zevent_t *ze; 229 nvlist_t *event; 230 uint64_t dropped = 0; 231 uint64_t dst_size; 232 int error; 233 234 zfs_zevent_init(&ze); 235 for (;;) { 236 dst_size = 131072; 237 dropped = 0; 238 event = NULL; 239 error = zfs_zevent_next(ze, &event, 240 &dst_size, &dropped); 241 if (error) { 242 error = zfs_zevent_wait(ze); 243 if (error == ESHUTDOWN) 244 break; 245 } else { 246 VERIFY3P(event, !=, NULL); 247 log_sysevent(event); 248 nvlist_free(event); 249 } 250 } 251 252 /* 253 * We avoid zfs_zevent_destroy() here because we're otherwise racing 254 * against fm_fini() destroying the zevent_lock. zfs_zevent_destroy() 255 * will currently only clear `ze->ze_zevent` from an event list then 256 * free `ze`, so just inline the free() here -- events have already 257 * been drained. 258 */ 259 VERIFY0P(ze->ze_zevent); 260 kmem_free(ze, sizeof (zfs_zevent_t)); 261 262 kthread_exit(); 263 } 264 265 void 266 ddi_sysevent_init(void) 267 { 268 kproc_kthread_add(sysevent_worker, NULL, &system_proc, NULL, 0, 0, 269 "zfskern", "sysevent"); 270 } 271