1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  *
25  * Copyright 2024 Oxide Computer Co.
26  */
27 
28 #include <sys/fm/protocol.h>
29 
30 #include <strings.h>
31 #include <libgen.h>
32 #include <regex.h>
33 #include <libnvpair.h>
34 
35 #include <fmd_log_impl.h>
36 #include <fmd_log.h>
37 
38 /*ARGSUSED*/
39 int
fmd_log_filter_class(fmd_log_t * lp,const fmd_log_record_t * rp,void * arg)40 fmd_log_filter_class(fmd_log_t *lp, const fmd_log_record_t *rp, void *arg)
41 {
42 	nvlist_t **nva;
43 	uint32_t i, size;
44 	char *class;
45 
46 	if (gmatch(rp->rec_class, arg))
47 		return (1);
48 
49 	/* return false if the record doesn't contain valid fault list */
50 	if (! gmatch(rp->rec_class, FM_LIST_EVENT ".*") ||
51 	    nvlist_lookup_uint32(rp->rec_nvl, FM_SUSPECT_FAULT_SZ,
52 	    &size) != 0 || size == 0 ||
53 	    nvlist_lookup_nvlist_array(rp->rec_nvl, FM_SUSPECT_FAULT_LIST,
54 	    &nva, &size) != 0)
55 		return (0);
56 
57 	/* return true if any fault in the list matches */
58 	for (i = 0; i < size; i++) {
59 		if (nvlist_lookup_string(nva[i], FM_CLASS, &class) == 0 &&
60 		    gmatch(class, arg))
61 			return (1);
62 	}
63 
64 	return (0);
65 }
66 
67 /*ARGSUSED*/
68 int
fmd_log_filter_uuid(fmd_log_t * lp,const fmd_log_record_t * rp,void * arg)69 fmd_log_filter_uuid(fmd_log_t *lp, const fmd_log_record_t *rp, void *arg)
70 {
71 	char *uuid;
72 
73 	/*
74 	 * Note: the uuid filter matches *any* member whose name is 'uuid'.
75 	 * This permits us to match not only a list.suspect uuid but any
76 	 * other event that decides to embed uuids, too, using the same name.
77 	 */
78 	return (nvlist_lookup_string(rp->rec_nvl,
79 	    "uuid", &uuid) == 0 && strcmp(uuid, arg) == 0);
80 }
81 
82 /*ARGSUSED*/
83 int
fmd_log_filter_before(fmd_log_t * lp,const fmd_log_record_t * rp,void * arg)84 fmd_log_filter_before(fmd_log_t *lp, const fmd_log_record_t *rp, void *arg)
85 {
86 	uint64_t sec = ((struct timeval *)arg)->tv_sec;
87 	uint64_t nsec = ((struct timeval *)arg)->tv_usec * (NANOSEC / MICROSEC);
88 	return (rp->rec_sec == sec ? rp->rec_nsec <= nsec : rp->rec_sec <= sec);
89 }
90 
91 /*ARGSUSED*/
92 int
fmd_log_filter_after(fmd_log_t * lp,const fmd_log_record_t * rp,void * arg)93 fmd_log_filter_after(fmd_log_t *lp, const fmd_log_record_t *rp, void *arg)
94 {
95 	uint64_t sec = ((struct timeval *)arg)->tv_sec;
96 	uint64_t nsec = ((struct timeval *)arg)->tv_usec * (NANOSEC / MICROSEC);
97 	return (rp->rec_sec == sec ? rp->rec_nsec >= nsec : rp->rec_sec >= sec);
98 }
99 
100 /*ARGSUSED*/
101 int
fmd_log_filter_nv(fmd_log_t * lp,const fmd_log_record_t * rp,void * arg)102 fmd_log_filter_nv(fmd_log_t *lp, const fmd_log_record_t *rp, void *arg)
103 {
104 	/*
105 	 * The nvarg_next member was added compatibly with the introduction of
106 	 * ABI version 3.  Older consumers pass a smaller structure that does
107 	 * not contain this member, so we treat it as if it were always NULL.
108 	 */
109 	for (fmd_log_filter_nvarg_t *argt = (fmd_log_filter_nvarg_t *)arg;
110 	    argt != NULL; argt = (lp->log_abi < 3) ? NULL : argt->nvarg_next) {
111 		char		*name = argt->nvarg_name;
112 		char		*value = argt->nvarg_value;
113 		regex_t		*value_regex = argt->nvarg_value_regex;
114 		nvpair_t	*nvp;
115 		int		ai;
116 
117 		/* see if nvlist has named member */
118 		if (nvlist_lookup_nvpair_embedded_index(rp->rec_nvl, name,
119 		    &nvp, &ai, NULL) != 0) {
120 			return (0);		/* name filter failure */
121 		}
122 
123 		/* check value match for matching nvpair */
124 		if ((value != NULL) &&
125 		    (nvpair_value_match_regex(nvp, ai,
126 		    value, value_regex, NULL) != 1)) {
127 			return (0);		/* value filter failure */
128 		}
129 	}
130 
131 	return (1);		/* name/value filter pass */
132 }
133 
134 /*
135  * This exists because filters are sorted and grouped based on the pointer to
136  * the filtering function, and we need fmdump to be able to maintain backward
137  * compatibility.  fmdump distinguishes filter classes by the command-line
138  * option used to describe the filter.  As for all library consumers, filters
139  * with the same evaluation function are considered to have the same class, and
140  * groups of filters of the SAME class are ORed together (i.e., match-any) while
141  * distinct classes are ANDed together, so that at least one of every class of
142  * filter must match in order for the record to pass through.  The command-line
143  * syntax fmdump accepts for multiple name-value filter chains cannot be made
144  * compatible with the syntax it accepted for single name-value filters,
145  * requiring that a new command-line option be introduced for multi-name-value
146  * filter chains.  Using a separate function thus allows fmdump to treat
147  * single-name-value and multi-name-value filters as belonging to different
148  * classes, maintaining backward compatibility with its existing command-line
149  * option syntax AND consistency with its documented treatment of filters of
150  * distinct classes.  At the same time, because a single-name-value filter is
151  * merely a special case of a multi-name-value filter (each entry in the
152  * argument list is required to match the record in order for the record to pass
153  * the filter), the actual implementation of the two filter classes is
154  * identical.  A consumer that, unlike fmdump, wants to treat these types of
155  * filters as belonging to a single class can therefore do so simply by using
156  * fmd_log_filter_nv() regardless of the number of name-value parameters in the
157  * argument chain, while those that want the fmdump behaviour should use that
158  * function only for filters with a single such parameter and this function for
159  * those with multiple.  See fmdump(8).
160  */
161 int
fmd_log_filter_nv_multi(fmd_log_t * lp,const fmd_log_record_t * rp,void * arg)162 fmd_log_filter_nv_multi(fmd_log_t *lp, const fmd_log_record_t *rp, void *arg)
163 {
164 	return (fmd_log_filter_nv(lp, rp, arg));
165 }
166