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