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