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 /*
23  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
24  */
25 
26 #include <strings.h>
27 #include <libscf.h>
28 #include <fm/fmd_api.h>
29 #include <fm/libtopo.h>
30 #include <fm/libfmevent.h>
31 
32 #include "fmevt.h"
33 
34 /*
35  * Post-processing according to the FMEV_RULESET_SMF ruleset.
36  *
37  * Raw event we expect:
38  *
39  * ==========================================================================
40  * Class: "state-transition"
41  * Subclasses: The new state, one of SCF_STATE_STRING_* from libscf.h
42  * Attr:
43  * Name		DATA_TYPE_*	Description
44  * ------------ --------------- ---------------------------------------------
45  * fmri		STRING		svc:/... (svc scheme shorthand version)
46  * transition	INT32		(old_state << 16) | new_state
47  * reason-version UINT32	reason-short namespace version
48  * reason-short	STRING		Short/keyword reason for transition
49  * reason-long	STRING		Long-winded reason for the transition
50  * ==========================================================================
51  *
52  * Protocol event components we return:
53  *
54  * ==========================================================================
55  * Class: ireport.os.smf.state-transition.<new-state>
56  * Attr:
57  * Name		DATA_TYPE_*	Description
58  * ------------ --------------- ----------------------------------------
59  * svc		NVLIST		"svc" scheme FMRI of affected service instance
60  * svc-string	STRING		SMF FMRI in short string form svc:/foo/bar
61  * from-state	STRING		Previous state; SCF_STATE_STRING_*
62  * to-state	STRING		New state; SCF_STATE_STRING_*
63  * reason-version UINT32	reason-short namespace version
64  * reason-short	STRING		Short/keyword reason for transition
65  * reason-long	STRING		Long-winded reason for the transition
66  * ==========================================================================
67  */
68 
69 /*
70  * svc.startd generates events using the FMRI shorthand (svc:/foo/bar)
71  * instead of the standard form (svc:///foo/bar).  This function converts to
72  * the standard representation.  The caller must free the allocated string.
73  */
74 static char *
shortfmri_to_fmristr(fmd_hdl_t * hdl,const char * shortfmristr)75 shortfmri_to_fmristr(fmd_hdl_t *hdl, const char *shortfmristr)
76 {
77 	size_t len;
78 	char *fmristr;
79 
80 	if (strncmp(shortfmristr, "svc:/", 5) != 0)
81 		return (NULL);
82 
83 	len = strlen(shortfmristr) + 3;
84 	fmristr = fmd_hdl_alloc(hdl, len, FMD_SLEEP);
85 	(void) snprintf(fmristr, len, "svc:///%s", shortfmristr + 5);
86 
87 	return (fmristr);
88 }
89 
90 /*
91  * Convert a shorthand svc FMRI into a full svc FMRI nvlist
92  */
93 static nvlist_t *
shortfmri_to_fmri(fmd_hdl_t * hdl,const char * shortfmristr)94 shortfmri_to_fmri(fmd_hdl_t *hdl, const char *shortfmristr)
95 {
96 	nvlist_t *ret, *fmri;
97 	topo_hdl_t *thp;
98 	char *fmristr;
99 	int err;
100 
101 	if ((fmristr = shortfmri_to_fmristr(hdl, shortfmristr)) == NULL)
102 		return (NULL);
103 
104 	thp = fmd_hdl_topo_hold(hdl, TOPO_VERSION);
105 
106 	if (topo_fmri_str2nvl(thp, fmristr, &fmri, &err) != 0) {
107 		fmd_hdl_error(hdl, "failed to convert '%s' to nvlist\n",
108 		    fmristr);
109 		fmd_hdl_strfree(hdl, fmristr);
110 		fmd_hdl_topo_rele(hdl, thp);
111 		return (NULL);
112 	}
113 
114 	fmd_hdl_strfree(hdl, fmristr);
115 
116 	if ((ret = fmd_nvl_dup(hdl, fmri, FMD_SLEEP)) == NULL) {
117 		fmd_hdl_error(hdl, "failed to dup fmri\n");
118 		nvlist_free(fmri);
119 		fmd_hdl_topo_rele(hdl, thp);
120 		return (NULL);
121 	}
122 
123 	nvlist_free(fmri);
124 	fmd_hdl_topo_rele(hdl, thp);
125 
126 	return (ret);
127 }
128 
129 /*ARGSUSED*/
130 uint_t
fmevt_pp_smf(char * classes[FMEVT_FANOUT_MAX],nvlist_t * attr[FMEVT_FANOUT_MAX],const char * ruleset,const nvlist_t * detector,nvlist_t * rawattr,const struct fmevt_ppargs * eap)131 fmevt_pp_smf(char *classes[FMEVT_FANOUT_MAX],
132     nvlist_t *attr[FMEVT_FANOUT_MAX], const char *ruleset,
133     const nvlist_t *detector, nvlist_t *rawattr,
134     const struct fmevt_ppargs *eap)
135 {
136 	int32_t transition, from, to;
137 	const char *fromstr, *tostr;
138 	char *svcname, *rsn, *rsnl;
139 	nvlist_t *myattr;
140 	nvlist_t *fmri;
141 	uint32_t ver;
142 
143 	if (!fmd_prop_get_int32(fmevt_hdl, "inbound_postprocess_smf"))
144 		return (0);
145 
146 	if (rawattr == NULL ||
147 	    strcmp(eap->pp_rawclass, "state-transition") != 0 ||
148 	    nvlist_lookup_string(rawattr, "fmri", &svcname) != 0 ||
149 	    nvlist_lookup_int32(rawattr, "transition", &transition) != 0 ||
150 	    nvlist_lookup_string(rawattr, "reason-short", &rsn) != 0 ||
151 	    nvlist_lookup_string(rawattr, "reason-long", &rsnl) != 0 ||
152 	    nvlist_lookup_uint32(rawattr, "reason-version", &ver) != 0)
153 		return (0);
154 
155 	from = transition >> 16;
156 	to = transition & 0xffff;
157 
158 	fromstr = smf_state_to_string(from);
159 	tostr = smf_state_to_string(to);
160 
161 	if (fromstr == NULL || tostr == NULL)
162 		return (0);
163 
164 	if (strcmp(eap->pp_rawsubclass, tostr) != 0)
165 		return (0);
166 
167 	if ((fmri = shortfmri_to_fmri(fmevt_hdl, svcname)) == NULL)
168 		return (0);
169 
170 	if (snprintf(classes[0], FMEVT_MAX_CLASS, "%s.%s.%s.%s",
171 	    FM_IREPORT_CLASS, "os.smf", eap->pp_rawclass,
172 	    eap->pp_rawsubclass) >= FMEVT_MAX_CLASS - 1)
173 		return (0);
174 
175 	if ((myattr = fmd_nvl_alloc(fmevt_hdl, FMD_SLEEP)) == NULL)
176 		return (0);
177 
178 	if (nvlist_add_nvlist(myattr, "svc", fmri) != 0 ||
179 	    nvlist_add_string(myattr, "svc-string", svcname) != 0 ||
180 	    nvlist_add_string(myattr, "from-state", fromstr) != 0 ||
181 	    nvlist_add_string(myattr, "to-state", tostr) != 0 ||
182 	    nvlist_add_uint32(myattr, "reason-version", ver) != 0 ||
183 	    nvlist_add_string(myattr, "reason-short", rsn) != 0 ||
184 	    nvlist_add_string(myattr, "reason-long", rsnl) != 0) {
185 		nvlist_free(fmri);
186 		nvlist_free(myattr);
187 		return (0);
188 	}
189 
190 	attr[0] = myattr;
191 	nvlist_free(fmri);
192 
193 	return (1);
194 }
195