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
log_sysevent(nvlist_t * event)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
sysevent_worker(void * arg __unused)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
ddi_sysevent_init(void)266 ddi_sysevent_init(void)
267 {
268 kproc_kthread_add(sysevent_worker, NULL, &system_proc, NULL, 0, 0,
269 "zfskern", "sysevent");
270 }
271