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