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) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
24  */
25 
26 #include <unistd.h>
27 #include <stdio.h>
28 #include <string.h>
29 #include <fm/fmd_api.h>
30 #include <libnvpair.h>
31 #include <fm/libtopo.h>
32 #include "out.h"
33 #include "stats.h"
34 #include "alloc.h"
35 #include "stable.h"
36 #include "literals.h"
37 #include "lut.h"
38 #include "esclex.h"
39 #include "tree.h"
40 #include "ipath.h"
41 #include "itree.h"
42 #include "iexpr.h"
43 #include "ptree.h"
44 #include "check.h"
45 #include "version.h"
46 #include "fme.h"
47 #include "eval.h"
48 #include "config.h"
49 #include "platform.h"
50 
51 /*
52  * eversholt diagnosis engine (eft.so) main entry points
53  */
54 
55 fmd_hdl_t *Hdl;		/* handle in global for platform.c */
56 
57 int Debug = 1;	/* turn on here and let fmd_hdl_debug() decide if really on */
58 hrtime_t Hesitate;	/* hesitation time in ns */
59 char *Serd_Override;	/* override for Serd engines */
60 int Verbose;
61 int Estats;
62 int Warn;	/* zero -- eft.so should not issue language warnings */
63 char **Efts;
64 int Max_fme;		/* Maximum number of open FMEs */
65 
66 /* stuff exported by yacc-generated parsers */
67 extern void yyparse(void);
68 extern int yydebug;
69 
70 extern struct lut *Dicts;
71 
72 extern void literals_init(void);
73 extern void literals_fini(void);
74 
75 struct eftsubr {
76 	const char *prefix;
77 	void (*hdlr)(fmd_hdl_t *, fmd_event_t *, nvlist_t *, const char *);
78 } eftsubrs[] = {
79 	{ "ereport.",		fme_receive_external_report },
80 	{ "list.repaired",	fme_receive_repair_list },
81 	{ NULL,			NULL }
82 };
83 
84 /*ARGSUSED*/
85 static void
eft_recv(fmd_hdl_t * hdl,fmd_event_t * ep,nvlist_t * nvl,const char * class)86 eft_recv(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *nvl, const char *class)
87 {
88 	struct eftsubr *sp = eftsubrs;
89 
90 	while (sp->prefix != NULL) {
91 		if (strncmp(class, sp->prefix, strlen(sp->prefix)) == 0)
92 			break;
93 		sp++;
94 	}
95 
96 	if (sp->prefix != NULL) {
97 		(sp->hdlr)(hdl, ep, nvl, class);
98 	} else {
99 		out(O_DIE,
100 		    "eft_recv: event class \"%s\" does not match our "
101 		    "subscriptions", class);
102 	}
103 }
104 
105 /*ARGSUSED*/
106 static void
eft_timeout(fmd_hdl_t * hdl,id_t tid,void * arg)107 eft_timeout(fmd_hdl_t *hdl, id_t tid, void *arg)
108 {
109 	out(O_ALTFP|O_STAMP,
110 	    "\neft.so timer %ld fired with arg %p", tid, arg);
111 
112 	if (arg == NULL)
113 		return;
114 
115 	fme_timer_fired(arg, tid);
116 }
117 
118 static void
eft_close(fmd_hdl_t * hdl,fmd_case_t * fmcase)119 eft_close(fmd_hdl_t *hdl, fmd_case_t *fmcase)
120 {
121 	out(O_ALTFP, "eft_close called for case %s",
122 	    fmd_case_uuid(hdl, fmcase));
123 	fme_close_case(hdl, fmcase);
124 }
125 
126 /*
127  * The "serd_override" property allows the N and T parameters of specified serd
128  * engines to be overridden. The property is a string consisting of one or more
129  * space separated triplets. Each triplet is of the form "name,N,T" where "name"
130  * is the name of the serd engine and N and T are the new paremeters to use.
131  * For example "serd.io.device.nonfatal,5,3h" would set the parameters for the
132  * serd.io.device.nonfatal engine to 5 in 3 hours.
133  */
134 static const fmd_prop_t eft_props[] = {
135 	{ "estats", FMD_TYPE_BOOL, "false" },
136 	{ "hesitate", FMD_TYPE_INT64, "10000000000" },
137 	{ "serd_override", FMD_TYPE_STRING, NULL },
138 	{ "verbose", FMD_TYPE_INT32, "0" },
139 	{ "warn", FMD_TYPE_BOOL, "false" },
140 	{ "status", FMD_TYPE_STRING, NULL },
141 	{ "maxfme", FMD_TYPE_INT32, "0" },
142 	{ NULL, 0, NULL }
143 };
144 
145 /*ARGSUSED*/
146 static void
eft_topo_change(fmd_hdl_t * hdl,topo_hdl_t * thp)147 eft_topo_change(fmd_hdl_t *hdl, topo_hdl_t *thp)
148 {
149 	fme_receive_topology_change();
150 }
151 
152 static const fmd_hdl_ops_t eft_ops = {
153 	eft_recv,	/* fmdo_recv */
154 	eft_timeout,	/* fmdo_timeout */
155 	eft_close,	/* fmdo_close */
156 	NULL,		/* fmdo_stats */
157 	NULL,		/* fmdo_gc */
158 	NULL,		/* fmdo_send */
159 	eft_topo_change	/* fmdo_topo_change */
160 };
161 
162 #define	xstr(s) str(s)
163 #define	str(s) #s
164 
165 static const fmd_hdl_info_t fmd_info = {
166 	"eft diagnosis engine",
167 	xstr(VERSION_MAJOR) "." xstr(VERSION_MINOR),
168 	&eft_ops, eft_props
169 };
170 
171 /*
172  * ename_strdup -- like strdup but ename comes in and class string goes out
173  */
174 static char *
ename_strdup(struct node * np)175 ename_strdup(struct node *np)
176 {
177 	struct node *mynp;
178 	int len;
179 	char *buf;
180 
181 	/* calculate length of buffer required */
182 	len = 0;
183 	for (mynp = np; mynp; mynp = mynp->u.name.next)
184 		len += strlen(mynp->u.name.s) + 1;	/* +1 for dot or NULL */
185 
186 	buf = MALLOC(len);
187 	buf[0] = '\0';
188 
189 	/* now build the string */
190 	while (np) {
191 		(void) strcat(buf, np->u.name.s);
192 		np = np->u.name.next;
193 		if (np)
194 			(void) strcat(buf, ".");
195 	}
196 
197 	return (buf);
198 }
199 
200 /*ARGSUSED*/
201 static void
dosubscribe(struct node * lhs,struct node * rhs,void * arg)202 dosubscribe(struct node *lhs, struct node *rhs, void *arg)
203 {
204 	char *ename = ename_strdup(lhs);
205 
206 	fmd_hdl_subscribe(Hdl, ename);
207 	FREE(ename);
208 }
209 
210 /*ARGSUSED*/
211 static void
dodiscardprint(struct node * lhs,struct node * rhs,void * arg)212 dodiscardprint(struct node *lhs, struct node *rhs, void *arg)
213 {
214 	char *ename = (char *)lhs;
215 
216 	out(O_VERB, "allow silent discard_if_config_unknown: \"%s\"", ename);
217 }
218 
219 extern struct stats *Filecount;
220 
221 /*
222  * Call all of the _fini() routines to clean up the exiting DE
223  */
224 void
call_finis(void)225 call_finis(void)
226 {
227 	platform_free_eft_files(Efts);
228 	Efts = NULL;
229 	platform_fini();
230 	fme_fini();
231 	itree_fini();
232 	ipath_fini();
233 	iexpr_fini();
234 	istat_fini();
235 	serd_fini();
236 	lex_free();
237 	check_fini();
238 	tree_fini();
239 	lut_fini();
240 	literals_fini();
241 	stable_fini();
242 	stats_fini();
243 	out_fini();
244 	alloc_fini();
245 }
246 
247 /*ARGSUSED*/
248 static void
doopendict(const char * lhs,void * rhs,void * arg)249 doopendict(const char *lhs, void *rhs, void *arg)
250 {
251 	out(O_VERB, "opendict: \"%s\"", lhs);
252 	fmd_hdl_opendict(Hdl, lhs);
253 }
254 
255 void
_fmd_init(fmd_hdl_t * hdl)256 _fmd_init(fmd_hdl_t *hdl)
257 {
258 	fmd_case_t *casep = NULL;
259 	int count;
260 	char *fname;
261 
262 	(void) fmd_hdl_register(hdl, FMD_API_VERSION, &fmd_info);
263 
264 	/* keep handle for routines like out() which need it */
265 	Hdl = hdl;
266 
267 	/* set up out(O_ALTFP) first things so it is available for debug */
268 	alloc_init();
269 	out_init("eft");
270 	if ((fname = fmd_prop_get_string(hdl, "status")) != NULL) {
271 		FILE *fp;
272 
273 		if ((fp = fopen(fname, "a")) == NULL) {
274 			fmd_prop_free_string(hdl, fname);
275 			out(O_DIE|O_SYS, "status property file: %s", fname);
276 		}
277 
278 		(void) setlinebuf(fp);
279 		out_altfp(fp);
280 
281 		out(O_DEBUG, "appending status changes to \"%s\"", fname);
282 		fmd_prop_free_string(hdl, fname);
283 
284 		out(O_ALTFP|O_STAMP, "\neft.so startup");
285 	}
286 
287 
288 	Estats = fmd_prop_get_int32(hdl, "estats");
289 	stats_init(Estats);
290 
291 	stable_init(0);
292 	literals_init();
293 	platform_init();
294 	lut_init();
295 	tree_init();
296 	ipath_init();
297 	iexpr_init();
298 	Efts = platform_get_eft_files();
299 	lex_init(Efts, NULL, 0);
300 	check_init();
301 
302 	/*
303 	 *  If we read no .eft files, we can't do any
304 	 *  diagnosing, so we just unload ourselves
305 	 */
306 	if (stats_counter_value(Filecount) == 0) {
307 		(void) lex_fini();
308 		call_finis();
309 		fmd_hdl_debug(hdl, "no fault trees provided.");
310 		fmd_hdl_unregister(hdl);
311 		return;
312 	}
313 
314 	yyparse();
315 	(void) lex_fini();
316 	tree_report();
317 	if (count = out_errcount())
318 		out(O_DIE, "%d language error%s encountered, exiting.",
319 		    OUTS(count));
320 
321 	/* subscribe to events we expect to consume */
322 	lut_walk(Ereportenames, (lut_cb)dosubscribe, NULL);
323 	lut_walk(Ereportenames_discard, (lut_cb)dodiscardprint, NULL);
324 
325 	/* subscribe to repair events so we can clear state on repair */
326 	fmd_hdl_subscribe(hdl, "list.repaired");
327 
328 	/* open dictionaries referenced by all .eft files */
329 	lut_walk(Dicts, (lut_cb)doopendict, (void *)0);
330 
331 	Verbose = fmd_prop_get_int32(hdl, "verbose");
332 	Warn = fmd_prop_get_int32(hdl, "warn");
333 	Hesitate = fmd_prop_get_int64(hdl, "hesitate");
334 	Serd_Override = fmd_prop_get_string(hdl, "serd_override");
335 	Max_fme = fmd_prop_get_int32(hdl, "maxfme");
336 
337 	out(O_DEBUG, "initialized, verbose %d warn %d maxfme %d",
338 	    Verbose, Warn, Max_fme);
339 
340 	fme_istat_load(hdl);
341 	fme_serd_load(hdl);
342 
343 	out(O_VERB, "reconstituting any existing fmes");
344 	while ((casep = fmd_case_next(hdl, casep)) != NULL) {
345 		fme_restart(hdl, casep);
346 	}
347 }
348 
349 /*ARGSUSED*/
350 void
_fmd_fini(fmd_hdl_t * hdl)351 _fmd_fini(fmd_hdl_t *hdl)
352 {
353 	call_finis();
354 }
355