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
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
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
265 ddi_sysevent_init(void)
266 {
267 	kproc_kthread_add(sysevent_worker, NULL, &system_proc, NULL, 0, 0,
268 	    "zfskern", "sysevent");
269 }
270