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