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 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 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 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 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 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 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 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 * 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 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 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 * 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 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 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 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 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 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 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) strlcat(jbuf, ",", len); 452 (void) strlcat(jbuf, arr[i], len); 453 } 454 455 *buf = jbuf; 456 return (0); 457 } 458 459 void 460 nd_free_nvlarray(nvlist_t **arr, uint_t arrsz) 461 { 462 for (uint_t i = 0; i < arrsz; i++) 463 nvlist_free(arr[i]); 464 free(arr); 465 } 466 467 /* 468 * This function takes a dictionary name and event class and then uses 469 * libdiagcode to compute the MSG ID. We need this for looking up messages 470 * for the committed ireport.* events. For FMA list.* events, the MSG ID is 471 * is contained in the event payload. 472 */ 473 int 474 nd_get_diagcode(nd_hdl_t *nhdl, const char *dict, const char *class, char *buf, 475 size_t buflen) 476 { 477 fm_dc_handle_t *dhp; 478 size_t dlen; 479 char *dirpath; 480 const char *key[2]; 481 int ret = 0; 482 483 dlen = (strlen(nhdl->nh_rootdir) + strlen(ND_DICTDIR) + 2); 484 dirpath = alloca(dlen); 485 (void) snprintf(dirpath, dlen, "%s/%s", nhdl->nh_rootdir, ND_DICTDIR); 486 487 if ((dhp = fm_dc_opendict(FM_DC_VERSION, dirpath, dict)) == NULL) { 488 nd_error(nhdl, "fm_dc_opendict failed for %s/%s", 489 dirpath, dict); 490 return (-1); 491 } 492 493 key[0] = class; 494 key[1] = NULL; 495 if (fm_dc_key2code(dhp, key, buf, buflen) < 0) { 496 nd_error(nhdl, "fm_dc_key2code failed for %s", key[0]); 497 ret = -1; 498 } 499 fm_dc_closedict(dhp); 500 return (ret); 501 } 502 503 /* 504 * This function takes an event and extracts the bits of the event payload that 505 * are of interest to notification daemons and conveniently tucks them into a 506 * single struct. 507 * 508 * The caller is responsible for freeing ev_info and any contained strings and 509 * nvlists. A convenience function, nd_free_event_info(), is provided for this 510 * purpose. 511 */ 512 int 513 nd_get_event_info(nd_hdl_t *nhdl, const char *class, fmev_t ev, 514 nd_ev_info_t **ev_info) 515 { 516 nvlist_t *ev_nvl, *attr_nvl; 517 nd_ev_info_t *evi; 518 char *code, *uuid, *fmri, *from_state, *to_state, *reason; 519 520 if ((evi = calloc(1, sizeof (nd_ev_info_t))) == NULL) { 521 nd_error(nhdl, "Failed to allocate memory"); 522 return (-1); 523 } 524 525 /* 526 * Hold event; class and payload will be valid for as long as 527 * we hold the event. 528 */ 529 fmev_hold(ev); 530 evi->ei_ev = ev; 531 ev_nvl = fmev_attr_list(ev); 532 533 /* 534 * Lookup the MSGID, event description and severity and KA URL 535 * 536 * For FMA list.* events we just pull it out of the the event nvlist. 537 * For all other events we call a utility function that computes the 538 * diagcode using the dict name and class. 539 */ 540 evi->ei_diagcode = calloc(32, sizeof (char)); 541 if ((nvlist_lookup_string(ev_nvl, FM_SUSPECT_DIAG_CODE, &code) == 0 && 542 strcpy(evi->ei_diagcode, code)) || 543 nd_get_diagcode(nhdl, "SMF", class, evi->ei_diagcode, 32) 544 == 0) { 545 evi->ei_severity = fmd_msg_getitem_id(nhdl->nh_msghdl, 546 NULL, evi->ei_diagcode, FMD_MSG_ITEM_SEVERITY); 547 evi->ei_descr = fmd_msg_getitem_id(nhdl->nh_msghdl, 548 NULL, evi->ei_diagcode, FMD_MSG_ITEM_DESC); 549 evi->ei_url = fmd_msg_getitem_id(nhdl->nh_msghdl, 550 NULL, evi->ei_diagcode, FMD_MSG_ITEM_URL); 551 } else 552 (void) strcpy(evi->ei_diagcode, ND_UNKNOWN); 553 554 if (!evi->ei_severity) 555 evi->ei_severity = strdup(ND_UNKNOWN); 556 if (!evi->ei_descr) 557 evi->ei_descr = strdup(ND_UNKNOWN); 558 if (!evi->ei_url) 559 evi->ei_url = strdup(ND_UNKNOWN); 560 561 evi->ei_payload = ev_nvl; 562 evi->ei_class = fmev_class(ev); 563 if (nvlist_lookup_string(ev_nvl, FM_SUSPECT_UUID, &uuid) == 0) 564 evi->ei_uuid = strdup(uuid); 565 else { 566 nd_error(nhdl, "Malformed event"); 567 nd_dump_nvlist(nhdl, evi->ei_payload); 568 nd_free_event_info(evi); 569 return (-1); 570 } 571 572 if (strncmp(class, "ireport.os.smf", 14) == 0) { 573 if ((fmri = nd_get_event_fmri(nhdl, ev)) == NULL) { 574 nd_error(nhdl, "Failed to get fmri from event payload"); 575 nd_free_event_info(evi); 576 return (-1); 577 } 578 if (nvlist_lookup_nvlist(evi->ei_payload, "attr", &attr_nvl) || 579 nvlist_lookup_string(attr_nvl, "from-state", &from_state) || 580 nvlist_lookup_string(attr_nvl, "to-state", &to_state) || 581 nvlist_lookup_string(attr_nvl, "reason-long", &reason)) { 582 nd_error(nhdl, "Malformed event"); 583 nd_dump_nvlist(nhdl, evi->ei_payload); 584 nd_free_event_info(evi); 585 free(fmri); 586 return (-1); 587 } 588 evi->ei_fmri = fmri; 589 evi->ei_to_state = strdup(to_state); 590 evi->ei_from_state = strdup(from_state); 591 evi->ei_reason = strdup(reason); 592 } 593 *ev_info = evi; 594 return (0); 595 } 596 597 void 598 nd_free_event_info(nd_ev_info_t *ev_info) 599 { 600 free(ev_info->ei_severity); 601 free(ev_info->ei_descr); 602 free(ev_info->ei_diagcode); 603 free(ev_info->ei_url); 604 free(ev_info->ei_uuid); 605 free(ev_info->ei_fmri); 606 free(ev_info->ei_from_state); 607 free(ev_info->ei_to_state); 608 free(ev_info->ei_reason); 609 fmev_rele(ev_info->ei_ev); 610 free(ev_info); 611 } 612