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
44static int
45log_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
224static void
225sysevent_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
264void
265ddi_sysevent_init(void)
266{
267	kproc_kthread_add(sysevent_worker, NULL, &system_proc, NULL, 0, 0,
268	    "zfskern", "sysevent");
269}
270