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 #include <alloca.h>
26
27 #include "libfmnotify.h"
28
29 /*ARGSUSED*/
30 void
nd_cleanup(nd_hdl_t * nhdl)31 nd_cleanup(nd_hdl_t *nhdl)
32 {
33 nd_debug(nhdl, "Cleaning up ...");
34 if (nhdl->nh_evhdl)
35 (void) fmev_shdl_fini(nhdl->nh_evhdl);
36
37 if (nhdl->nh_msghdl)
38 fmd_msg_fini(nhdl->nh_msghdl);
39
40 nhdl->nh_keep_running = B_FALSE;
41 (void) fclose(nhdl->nh_log_fd);
42 }
43
44 static void
get_timestamp(char * buf,size_t bufsize)45 get_timestamp(char *buf, size_t bufsize)
46 {
47 time_t utc_time;
48 struct tm *p_tm;
49
50 (void) time(&utc_time);
51 p_tm = localtime(&utc_time);
52
53 (void) strftime(buf, bufsize, "%b %d %H:%M:%S", p_tm);
54 }
55
56 /* PRINTFLIKE2 */
57 void
nd_debug(nd_hdl_t * nhdl,const char * format,...)58 nd_debug(nd_hdl_t *nhdl, const char *format, ...)
59 {
60 char timestamp[64];
61 va_list ap;
62
63 if (nhdl->nh_debug) {
64 get_timestamp(timestamp, sizeof (timestamp));
65 (void) fprintf(nhdl->nh_log_fd, "[ %s ", timestamp);
66 va_start(ap, format);
67 (void) vfprintf(nhdl->nh_log_fd, format, ap);
68 va_end(ap);
69 (void) fprintf(nhdl->nh_log_fd, " ]\n");
70 }
71 (void) fflush(nhdl->nh_log_fd);
72 }
73
74 void
nd_dump_nvlist(nd_hdl_t * nhdl,nvlist_t * nvl)75 nd_dump_nvlist(nd_hdl_t *nhdl, nvlist_t *nvl)
76 {
77 if (nhdl->nh_debug)
78 nvlist_print(nhdl->nh_log_fd, nvl);
79 }
80
81 /* PRINTFLIKE2 */
82 void
nd_error(nd_hdl_t * nhdl,const char * format,...)83 nd_error(nd_hdl_t *nhdl, const char *format, ...)
84 {
85 char timestamp[64];
86 va_list ap;
87
88 get_timestamp(timestamp, sizeof (timestamp));
89 (void) fprintf(nhdl->nh_log_fd, "[ %s ", timestamp);
90 va_start(ap, format);
91 (void) vfprintf(nhdl->nh_log_fd, format, ap);
92 va_end(ap);
93 (void) fprintf(nhdl->nh_log_fd, " ]\n");
94 (void) fflush(nhdl->nh_log_fd);
95 }
96
97 /* PRINTFLIKE2 */
98 void
nd_abort(nd_hdl_t * nhdl,const char * format,...)99 nd_abort(nd_hdl_t *nhdl, const char *format, ...)
100 {
101 char timestamp[64];
102 va_list ap;
103
104 get_timestamp(timestamp, sizeof (timestamp));
105 (void) fprintf(nhdl->nh_log_fd, "[ %s ", timestamp);
106 va_start(ap, format);
107 (void) vfprintf(nhdl->nh_log_fd, format, ap);
108 va_end(ap);
109 (void) fprintf(nhdl->nh_log_fd, " ]\n");
110 (void) fflush(nhdl->nh_log_fd);
111 nd_cleanup(nhdl);
112 }
113
114 void
nd_daemonize(nd_hdl_t * nhdl)115 nd_daemonize(nd_hdl_t *nhdl)
116 {
117 pid_t pid;
118
119 if ((pid = fork()) < 0)
120 nd_abort(nhdl, "Failed to fork child (%s)", strerror(errno));
121 else if (pid > 0)
122 exit(0);
123
124 (void) setsid();
125 (void) close(0);
126 (void) close(1);
127 /*
128 * We leave stderr open so we can write debug/err messages to the SMF
129 * service log
130 */
131 nhdl->nh_is_daemon = B_TRUE;
132 }
133
134 /*
135 * This function returns a pointer to the specified SMF property group for the
136 * specified SMF service. The caller is responsible for freeing the property
137 * group. On failure, the function returns NULL.
138 */
139 static scf_propertygroup_t *
nd_get_pg(nd_hdl_t * nhdl,scf_handle_t * handle,const char * svcname,const char * pgname)140 nd_get_pg(nd_hdl_t *nhdl, scf_handle_t *handle, const char *svcname,
141 const char *pgname)
142 {
143 scf_scope_t *sc = NULL;
144 scf_service_t *svc = NULL;
145 scf_propertygroup_t *pg = NULL, *ret = NULL;
146
147 sc = scf_scope_create(handle);
148 svc = scf_service_create(handle);
149 pg = scf_pg_create(handle);
150
151 if (sc == NULL || svc == NULL || pg == NULL) {
152 nd_error(nhdl, "Failed to allocate libscf structures");
153 scf_pg_destroy(pg);
154 goto get_pg_done;
155 }
156
157 if (scf_handle_bind(handle) != -1 &&
158 scf_handle_get_scope(handle, SCF_SCOPE_LOCAL, sc) != -1 &&
159 scf_scope_get_service(sc, svcname, svc) != -1 &&
160 scf_service_get_pg(svc, pgname, pg) != -1)
161 ret = pg;
162 else
163 scf_pg_destroy(pg);
164
165 get_pg_done:
166 scf_service_destroy(svc);
167 scf_scope_destroy(sc);
168
169 return (ret);
170 }
171
172 int
nd_get_astring_prop(nd_hdl_t * nhdl,const char * svcname,const char * pgname,const char * propname,char ** val)173 nd_get_astring_prop(nd_hdl_t *nhdl, const char *svcname, const char *pgname,
174 const char *propname, char **val)
175 {
176 scf_handle_t *handle = NULL;
177 scf_propertygroup_t *pg;
178 scf_property_t *prop = NULL;
179 scf_value_t *value = NULL;
180 char strval[255];
181 int ret = -1;
182
183 if ((handle = scf_handle_create(SCF_VERSION)) == NULL)
184 return (ret);
185
186 if ((pg = nd_get_pg(nhdl, handle, svcname, pgname)) == NULL) {
187 nd_error(nhdl, "Failed to read retrieve %s "
188 "property group for %s", pgname, svcname);
189 goto astring_done;
190 }
191 prop = scf_property_create(handle);
192 value = scf_value_create(handle);
193 if (prop == NULL || value == NULL) {
194 nd_error(nhdl, "Failed to allocate SMF structures");
195 goto astring_done;
196 }
197 if (scf_pg_get_property(pg, propname, prop) == -1 ||
198 scf_property_get_value(prop, value) == -1 ||
199 scf_value_get_astring(value, strval, 255) == -1) {
200 nd_error(nhdl, "Failed to retrieve %s prop (%s)", propname,
201 scf_strerror(scf_error()));
202 goto astring_done;
203 }
204 *val = strdup(strval);
205 ret = 0;
206
207 astring_done:
208 scf_value_destroy(value);
209 scf_property_destroy(prop);
210 scf_pg_destroy(pg);
211 scf_handle_destroy(handle);
212
213 return (ret);
214 }
215
216 int
nd_get_boolean_prop(nd_hdl_t * nhdl,const char * svcname,const char * pgname,const char * propname,uint8_t * val)217 nd_get_boolean_prop(nd_hdl_t *nhdl, const char *svcname, const char *pgname,
218 const char *propname, uint8_t *val)
219 {
220 scf_handle_t *handle = NULL;
221 scf_propertygroup_t *pg;
222 scf_property_t *prop = NULL;
223 scf_value_t *value = NULL;
224 int ret = -1;
225
226 if ((handle = scf_handle_create(SCF_VERSION)) == NULL)
227 return (ret);
228
229 if ((pg = nd_get_pg(nhdl, handle, svcname, pgname)) == NULL) {
230 nd_error(nhdl, "Failed to read retrieve %s "
231 "property group for %s", pgname, svcname);
232 goto bool_done;
233 }
234 prop = scf_property_create(handle);
235 value = scf_value_create(handle);
236 if (prop == NULL || value == NULL) {
237 nd_error(nhdl, "Failed to allocate SMF structures");
238 goto bool_done;
239 }
240 if (scf_pg_get_property(pg, propname, prop) == -1 ||
241 scf_property_get_value(prop, value) == -1 ||
242 scf_value_get_boolean(value, val) == -1) {
243 nd_error(nhdl, "Failed to retrieve %s prop (%s)", propname,
244 scf_strerror(scf_error()));
245 goto bool_done;
246 }
247 ret = 0;
248
249 bool_done:
250 scf_value_destroy(value);
251 scf_property_destroy(prop);
252 scf_pg_destroy(pg);
253 scf_handle_destroy(handle);
254
255 return (ret);
256 }
257
258 char *
nd_get_event_fmri(nd_hdl_t * nhdl,fmev_t ev)259 nd_get_event_fmri(nd_hdl_t *nhdl, fmev_t ev)
260 {
261 nvlist_t *ev_nvl, *attr_nvl;
262 char *svcname;
263
264 if ((ev_nvl = fmev_attr_list(ev)) == NULL) {
265 nd_error(nhdl, "Failed to lookup event attr nvlist");
266 return (NULL);
267 }
268 if (nvlist_lookup_nvlist(ev_nvl, "attr", &attr_nvl) ||
269 nvlist_lookup_string(attr_nvl, "svc-string", &svcname)) {
270 nd_error(nhdl, "Malformed event 0x%p", (void *)ev_nvl);
271 return (NULL);
272 }
273
274 return (strdup((const char *)svcname));
275 }
276
277 int
nd_get_notify_prefs(nd_hdl_t * nhdl,const char * mech,fmev_t ev,nvlist_t *** pref_nvl,uint_t * nprefs)278 nd_get_notify_prefs(nd_hdl_t *nhdl, const char *mech, fmev_t ev,
279 nvlist_t ***pref_nvl, uint_t *nprefs)
280 {
281 nvlist_t *ev_nvl, *top_nvl, **np_nvlarr, *mech_nvl;
282 int ret = 1;
283 uint_t nelem;
284
285 if ((ev_nvl = fmev_attr_list(ev)) == NULL) {
286 nd_error(nhdl, "Failed to lookup event attr nvlist");
287 return (-1);
288 }
289
290 if ((ret = smf_notify_get_params(&top_nvl, ev_nvl)) != SCF_SUCCESS) {
291 ret = scf_error();
292 if (ret == SCF_ERROR_NOT_FOUND) {
293 nd_debug(nhdl, "No notification preferences specified "
294 "for this event");
295 goto pref_done;
296 } else {
297 nd_error(nhdl, "Error looking up notification "
298 "preferences (%s)", scf_strerror(ret));
299 nd_dump_nvlist(nhdl, top_nvl);
300 goto pref_done;
301 }
302 }
303
304 if (nvlist_lookup_nvlist_array(top_nvl, SCF_NOTIFY_PARAMS, &np_nvlarr,
305 &nelem) != 0) {
306 nd_error(nhdl, "Malformed nvlist");
307 nd_dump_nvlist(nhdl, top_nvl);
308 ret = 1;
309 goto pref_done;
310 }
311 *pref_nvl = malloc(nelem * sizeof (nvlist_t *));
312 *nprefs = 0;
313
314 for (int i = 0; i < nelem; i++) {
315 if (nvlist_lookup_nvlist(np_nvlarr[i], mech, &mech_nvl) == 0) {
316 (void) nvlist_dup(mech_nvl, *pref_nvl + *nprefs, 0);
317 ++*nprefs;
318 }
319 }
320
321 if (*nprefs == 0) {
322 nd_debug(nhdl, "No %s notification preferences specified",
323 mech);
324 free(*pref_nvl);
325 ret = SCF_ERROR_NOT_FOUND;
326 goto pref_done;
327 }
328 ret = 0;
329 pref_done:
330 nvlist_free(top_nvl);
331 return (ret);
332 }
333
334 static int
nd_seq_search(char * key,char ** list,uint_t nelem)335 nd_seq_search(char *key, char **list, uint_t nelem)
336 {
337 for (int i = 0; i < nelem; i++)
338 if (strcmp(key, list[i]) == 0)
339 return (1);
340 return (0);
341 }
342
343 /*
344 * This function takes a single string list and splits it into
345 * an string array (analogous to PERL split)
346 *
347 * The caller is responsible for freeing the array.
348 */
349 int
nd_split_list(nd_hdl_t * nhdl,char * list,char * delim,char *** arr,uint_t * nelem)350 nd_split_list(nd_hdl_t *nhdl, char *list, char *delim, char ***arr,
351 uint_t *nelem)
352 {
353 char *item, *tmpstr;
354 int i = 1, size = 1;
355
356 tmpstr = strdup(list);
357 item = strtok(tmpstr, delim);
358 while (item && strtok(NULL, delim) != NULL)
359 size++;
360 free(tmpstr);
361
362 if ((*arr = calloc(size, sizeof (char *))) == NULL) {
363 nd_error(nhdl, "Error allocating memory (%s)", strerror(errno));
364 return (-1);
365 }
366 if (size == 1)
367 (*arr)[0] = strdup(list);
368 else {
369 tmpstr = strdup(list);
370 item = strtok(tmpstr, delim);
371 (*arr)[0] = strdup(item);
372 while ((item = strtok(NULL, delim)) != NULL)
373 (*arr)[i++] = strdup(item);
374 free(tmpstr);
375 }
376 *nelem = size;
377 return (0);
378 }
379
380 /*
381 * This function merges two string arrays into a single array, removing any
382 * duplicates
383 *
384 * The caller is responsible for freeing the merged array.
385 */
386 int
nd_merge_strarray(nd_hdl_t * nhdl,char ** arr1,uint_t n1,char ** arr2,uint_t n2,char *** buf)387 nd_merge_strarray(nd_hdl_t *nhdl, char **arr1, uint_t n1, char **arr2,
388 uint_t n2, char ***buf)
389 {
390 char **tmparr;
391 int uniq = -1;
392
393 tmparr = alloca((n1 + n2) * sizeof (char *));
394 bzero(tmparr, (n1 + n2) * sizeof (char *));
395
396 while (++uniq < n1)
397 tmparr[uniq] = strdup(arr1[uniq]);
398
399 for (int j = 0; j < n2; j++)
400 if (!nd_seq_search(arr2[j], tmparr, uniq))
401 tmparr[uniq++] = strdup(arr2[j]);
402
403 if ((*buf = calloc(uniq, sizeof (char *))) == NULL) {
404 nd_error(nhdl, "Error allocating memory (%s)", strerror(errno));
405 for (int j = 0; j < uniq; j++) {
406 if (tmparr[j])
407 free(tmparr[j]);
408 }
409 return (-1);
410 }
411
412 bcopy(tmparr, *buf, uniq * sizeof (char *));
413 return (uniq);
414 }
415
416 void
nd_free_strarray(char ** arr,uint_t arrsz)417 nd_free_strarray(char **arr, uint_t arrsz)
418 {
419 for (uint_t i = 0; i < arrsz; i++)
420 free(arr[i]);
421 free(arr);
422 }
423
424 /*
425 * This function joins all the strings in a string array into a single string
426 * Each element will be delimited by a comma
427 *
428 * The caller is responsible for freeing the joined string.
429 */
430 int
nd_join_strarray(nd_hdl_t * nhdl,char ** arr,uint_t arrsz,char ** buf)431 nd_join_strarray(nd_hdl_t *nhdl, char **arr, uint_t arrsz, char **buf)
432 {
433 uint_t len = 0;
434 char *jbuf;
435 int i;
436
437 /*
438 * First, figure out how much space we need to allocate to store the
439 * joined string.
440 */
441 for (i = 0; i < arrsz; i++)
442 len += strlen(arr[i]) + 1;
443
444 if ((jbuf = calloc(len, sizeof (char))) == NULL) {
445 nd_error(nhdl, "Error allocating memory (%s)", strerror(errno));
446 return (-1);
447 }
448
449 (void) snprintf(jbuf, len, "%s", arr[0]);
450 for (i = 1; i < arrsz; i++)
451 (void) snprintf(jbuf, len, "%s,%s", jbuf, arr[i]);
452
453 *buf = jbuf;
454 return (0);
455 }
456
457 void
nd_free_nvlarray(nvlist_t ** arr,uint_t arrsz)458 nd_free_nvlarray(nvlist_t **arr, uint_t arrsz)
459 {
460 for (uint_t i = 0; i < arrsz; i++)
461 nvlist_free(arr[i]);
462 free(arr);
463 }
464
465 /*
466 * This function takes a dictionary name and event class and then uses
467 * libdiagcode to compute the MSG ID. We need this for looking up messages
468 * for the committed ireport.* events. For FMA list.* events, the MSG ID is
469 * is contained in the event payload.
470 */
471 int
nd_get_diagcode(nd_hdl_t * nhdl,const char * dict,const char * class,char * buf,size_t buflen)472 nd_get_diagcode(nd_hdl_t *nhdl, const char *dict, const char *class, char *buf,
473 size_t buflen)
474 {
475 fm_dc_handle_t *dhp;
476 size_t dlen;
477 char *dirpath;
478 const char *key[2];
479 int ret = 0;
480
481 dlen = (strlen(nhdl->nh_rootdir) + strlen(ND_DICTDIR) + 2);
482 dirpath = alloca(dlen);
483 (void) snprintf(dirpath, dlen, "%s/%s", nhdl->nh_rootdir, ND_DICTDIR);
484
485 if ((dhp = fm_dc_opendict(FM_DC_VERSION, dirpath, dict)) == NULL) {
486 nd_error(nhdl, "fm_dc_opendict failed for %s/%s",
487 dirpath, dict);
488 return (-1);
489 }
490
491 key[0] = class;
492 key[1] = NULL;
493 if (fm_dc_key2code(dhp, key, buf, buflen) < 0) {
494 nd_error(nhdl, "fm_dc_key2code failed for %s", key[0]);
495 ret = -1;
496 }
497 fm_dc_closedict(dhp);
498 return (ret);
499 }
500
501 /*
502 * This function takes an event and extracts the bits of the event payload that
503 * are of interest to notification daemons and conveniently tucks them into a
504 * single struct.
505 *
506 * The caller is responsible for freeing ev_info and any contained strings and
507 * nvlists. A convenience function, nd_free_event_info(), is provided for this
508 * purpose.
509 */
510 int
nd_get_event_info(nd_hdl_t * nhdl,const char * class,fmev_t ev,nd_ev_info_t ** ev_info)511 nd_get_event_info(nd_hdl_t *nhdl, const char *class, fmev_t ev,
512 nd_ev_info_t **ev_info)
513 {
514 nvlist_t *ev_nvl, *attr_nvl;
515 nd_ev_info_t *evi;
516 char *code, *uuid, *fmri, *from_state, *to_state, *reason;
517
518 if ((evi = calloc(1, sizeof (nd_ev_info_t))) == NULL) {
519 nd_error(nhdl, "Failed to allocate memory");
520 return (-1);
521 }
522
523 /*
524 * Hold event; class and payload will be valid for as long as
525 * we hold the event.
526 */
527 fmev_hold(ev);
528 evi->ei_ev = ev;
529 ev_nvl = fmev_attr_list(ev);
530
531 /*
532 * Lookup the MSGID, event description and severity and KA URL
533 *
534 * For FMA list.* events we just pull it out of the the event nvlist.
535 * For all other events we call a utility function that computes the
536 * diagcode using the dict name and class.
537 */
538 evi->ei_diagcode = calloc(32, sizeof (char));
539 if ((nvlist_lookup_string(ev_nvl, FM_SUSPECT_DIAG_CODE, &code) == 0 &&
540 strcpy(evi->ei_diagcode, code)) ||
541 nd_get_diagcode(nhdl, "SMF", class, evi->ei_diagcode, 32)
542 == 0) {
543 evi->ei_severity = fmd_msg_getitem_id(nhdl->nh_msghdl,
544 NULL, evi->ei_diagcode, FMD_MSG_ITEM_SEVERITY);
545 evi->ei_descr = fmd_msg_getitem_id(nhdl->nh_msghdl,
546 NULL, evi->ei_diagcode, FMD_MSG_ITEM_DESC);
547 evi->ei_url = fmd_msg_getitem_id(nhdl->nh_msghdl,
548 NULL, evi->ei_diagcode, FMD_MSG_ITEM_URL);
549 } else
550 (void) strcpy(evi->ei_diagcode, ND_UNKNOWN);
551
552 if (!evi->ei_severity)
553 evi->ei_severity = strdup(ND_UNKNOWN);
554 if (!evi->ei_descr)
555 evi->ei_descr = strdup(ND_UNKNOWN);
556 if (!evi->ei_url)
557 evi->ei_url = strdup(ND_UNKNOWN);
558
559 evi->ei_payload = ev_nvl;
560 evi->ei_class = fmev_class(ev);
561 if (nvlist_lookup_string(ev_nvl, FM_SUSPECT_UUID, &uuid) == 0)
562 evi->ei_uuid = strdup(uuid);
563 else {
564 nd_error(nhdl, "Malformed event");
565 nd_dump_nvlist(nhdl, evi->ei_payload);
566 nd_free_event_info(evi);
567 return (-1);
568 }
569
570 if (strncmp(class, "ireport.os.smf", 14) == 0) {
571 if ((fmri = nd_get_event_fmri(nhdl, ev)) == NULL) {
572 nd_error(nhdl, "Failed to get fmri from event payload");
573 nd_free_event_info(evi);
574 return (-1);
575 }
576 if (nvlist_lookup_nvlist(evi->ei_payload, "attr", &attr_nvl) ||
577 nvlist_lookup_string(attr_nvl, "from-state", &from_state) ||
578 nvlist_lookup_string(attr_nvl, "to-state", &to_state) ||
579 nvlist_lookup_string(attr_nvl, "reason-long", &reason)) {
580 nd_error(nhdl, "Malformed event");
581 nd_dump_nvlist(nhdl, evi->ei_payload);
582 nd_free_event_info(evi);
583 free(fmri);
584 return (-1);
585 }
586 evi->ei_fmri = fmri;
587 evi->ei_to_state = strdup(to_state);
588 evi->ei_from_state = strdup(from_state);
589 evi->ei_reason = strdup(reason);
590 }
591 *ev_info = evi;
592 return (0);
593 }
594
595 static void
condfree(void * buf)596 condfree(void *buf)
597 {
598 if (buf != NULL)
599 free(buf);
600 }
601
602 void
nd_free_event_info(nd_ev_info_t * ev_info)603 nd_free_event_info(nd_ev_info_t *ev_info)
604 {
605 condfree(ev_info->ei_severity);
606 condfree(ev_info->ei_descr);
607 condfree(ev_info->ei_diagcode);
608 condfree(ev_info->ei_url);
609 condfree(ev_info->ei_uuid);
610 condfree(ev_info->ei_fmri);
611 condfree(ev_info->ei_from_state);
612 condfree(ev_info->ei_to_state);
613 condfree(ev_info->ei_reason);
614 fmev_rele(ev_info->ei_ev);
615 free(ev_info);
616 }
617