2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A
2N/A/*
2N/A * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A#include <alloca.h>
2N/A
2N/A#include "libfmnotify.h"
2N/A
2N/A/*ARGSUSED*/
2N/Avoid
2N/And_cleanup(nd_hdl_t *nhdl)
2N/A{
2N/A nd_debug(nhdl, "Cleaning up ...");
2N/A if (nhdl->nh_evhdl)
2N/A (void) fmev_shdl_fini(nhdl->nh_evhdl);
2N/A
2N/A if (nhdl->nh_msghdl)
2N/A fmd_msg_fini(nhdl->nh_msghdl);
2N/A
2N/A (void) fclose(nhdl->nh_log_fd);
2N/A}
2N/A
2N/Astatic void
2N/Aget_timestamp(char *buf, size_t bufsize)
2N/A{
2N/A time_t utc_time;
2N/A struct tm *p_tm;
2N/A
2N/A (void) time(&utc_time);
2N/A p_tm = localtime(&utc_time);
2N/A
2N/A (void) strftime(buf, bufsize, "%b %d %H:%M:%S", p_tm);
2N/A}
2N/A
2N/A/* PRINTFLIKE2 */
2N/Avoid
2N/And_debug(nd_hdl_t *nhdl, const char *format, ...)
2N/A{
2N/A char timestamp[64];
2N/A va_list ap;
2N/A
2N/A if (nhdl->nh_debug) {
2N/A get_timestamp(timestamp, sizeof (timestamp));
2N/A (void) fprintf(nhdl->nh_log_fd, "[ %s ", timestamp);
2N/A va_start(ap, format);
2N/A (void) vfprintf(nhdl->nh_log_fd, format, ap);
2N/A va_end(ap);
2N/A (void) fprintf(nhdl->nh_log_fd, " ]\n");
2N/A }
2N/A (void) fflush(nhdl->nh_log_fd);
2N/A}
2N/A
2N/Avoid
2N/And_dump_nvlist(nd_hdl_t *nhdl, nvlist_t *nvl)
2N/A{
2N/A if (nhdl->nh_debug)
2N/A nvlist_print(nhdl->nh_log_fd, nvl);
2N/A}
2N/A
2N/A/* PRINTFLIKE2 */
2N/Avoid
2N/And_error(nd_hdl_t *nhdl, const char *format, ...)
2N/A{
2N/A char timestamp[64];
2N/A va_list ap;
2N/A
2N/A get_timestamp(timestamp, sizeof (timestamp));
2N/A (void) fprintf(nhdl->nh_log_fd, "[ %s ", timestamp);
2N/A va_start(ap, format);
2N/A (void) vfprintf(nhdl->nh_log_fd, format, ap);
2N/A va_end(ap);
2N/A (void) fprintf(nhdl->nh_log_fd, " ]\n");
2N/A (void) fflush(nhdl->nh_log_fd);
2N/A}
2N/A
2N/A/* PRINTFLIKE2 */
2N/Avoid
2N/And_abort(nd_hdl_t *nhdl, const char *format, ...)
2N/A{
2N/A char timestamp[64];
2N/A va_list ap;
2N/A
2N/A get_timestamp(timestamp, sizeof (timestamp));
2N/A (void) fprintf(nhdl->nh_log_fd, "[ %s ", timestamp);
2N/A va_start(ap, format);
2N/A (void) vfprintf(nhdl->nh_log_fd, format, ap);
2N/A va_end(ap);
2N/A (void) fprintf(nhdl->nh_log_fd, " ]\n");
2N/A (void) fflush(nhdl->nh_log_fd);
2N/A nd_cleanup(nhdl);
2N/A exit(1);
2N/A}
2N/A
2N/Avoid
2N/And_daemonize(nd_hdl_t *nhdl)
2N/A{
2N/A pid_t pid;
2N/A
2N/A if ((pid = fork()) < 0)
2N/A nd_abort(nhdl, "Failed to fork child (%s)", strerror(errno));
2N/A else if (pid > 0)
2N/A exit(0);
2N/A
2N/A (void) setsid();
2N/A (void) close(0);
2N/A (void) close(1);
2N/A /*
2N/A * We leave stderr open so we can write debug/err messages to the SMF
2N/A * service log
2N/A */
2N/A nhdl->nh_is_daemon = B_TRUE;
2N/A}
2N/A
2N/A/*
2N/A * This function returns a pointer to the specified SMF property group for the
2N/A * specified SMF service. The caller is responsible for freeing the property
2N/A * group. On failure, the function returns NULL.
2N/A */
2N/Astatic scf_propertygroup_t *
2N/And_get_pg(nd_hdl_t *nhdl, scf_handle_t *handle, const char *svcname,
2N/A const char *pgname)
2N/A{
2N/A scf_scope_t *sc = NULL;
2N/A scf_service_t *svc = NULL;
2N/A scf_propertygroup_t *pg = NULL, *ret = NULL;
2N/A
2N/A sc = scf_scope_create(handle);
2N/A svc = scf_service_create(handle);
2N/A pg = scf_pg_create(handle);
2N/A
2N/A if (sc == NULL || svc == NULL || pg == NULL) {
2N/A nd_error(nhdl, "Failed to allocate libscf structures");
2N/A scf_pg_destroy(pg);
2N/A goto get_pg_done;
2N/A }
2N/A
2N/A if (scf_handle_bind(handle) != -1 &&
2N/A scf_handle_get_scope(handle, SCF_SCOPE_LOCAL, sc) != -1 &&
2N/A scf_scope_get_service(sc, svcname, svc) != -1 &&
2N/A scf_service_get_pg(svc, pgname, pg) != -1)
2N/A ret = pg;
2N/A else
2N/A scf_pg_destroy(pg);
2N/A
2N/Aget_pg_done:
2N/A scf_service_destroy(svc);
2N/A scf_scope_destroy(sc);
2N/A
2N/A return (ret);
2N/A}
2N/A
2N/Aint
2N/And_get_astring_prop(nd_hdl_t *nhdl, const char *svcname, const char *pgname,
2N/A const char *propname, char **val)
2N/A{
2N/A scf_handle_t *handle = NULL;
2N/A scf_propertygroup_t *pg;
2N/A scf_property_t *prop = NULL;
2N/A scf_value_t *value = NULL;
2N/A char strval[255];
2N/A int ret = -1;
2N/A
2N/A if ((handle = scf_handle_create(SCF_VERSION)) == NULL)
2N/A return (ret);
2N/A
2N/A if ((pg = nd_get_pg(nhdl, handle, svcname, pgname)) == NULL) {
2N/A nd_error(nhdl, "Failed to read retrieve %s "
2N/A "property group for %s", pgname, svcname);
2N/A goto astring_done;
2N/A }
2N/A prop = scf_property_create(handle);
2N/A value = scf_value_create(handle);
2N/A if (prop == NULL || value == NULL) {
2N/A nd_error(nhdl, "Failed to allocate SMF structures");
2N/A goto astring_done;
2N/A }
2N/A if (scf_pg_get_property(pg, propname, prop) == -1 ||
2N/A scf_property_get_value(prop, value) == -1 ||
2N/A scf_value_get_astring(value, strval, 255) == -1) {
2N/A nd_error(nhdl, "Failed to retrieve %s prop (%s)", propname,
2N/A scf_strerror(scf_error()));
2N/A goto astring_done;
2N/A }
2N/A *val = strdup(strval);
2N/A ret = 0;
2N/A
2N/Aastring_done:
2N/A scf_value_destroy(value);
2N/A scf_property_destroy(prop);
2N/A scf_pg_destroy(pg);
2N/A scf_handle_destroy(handle);
2N/A
2N/A return (ret);
2N/A}
2N/A
2N/Aint
2N/And_get_boolean_prop(nd_hdl_t *nhdl, const char *svcname, const char *pgname,
2N/A const char *propname, uint8_t *val)
2N/A{
2N/A scf_handle_t *handle = NULL;
2N/A scf_propertygroup_t *pg;
2N/A scf_property_t *prop = NULL;
2N/A scf_value_t *value = NULL;
2N/A int ret = -1;
2N/A
2N/A if ((handle = scf_handle_create(SCF_VERSION)) == NULL)
2N/A return (ret);
2N/A
2N/A if ((pg = nd_get_pg(nhdl, handle, svcname, pgname)) == NULL) {
2N/A nd_error(nhdl, "Failed to read retrieve %s "
2N/A "property group for %s", pgname, svcname);
2N/A goto bool_done;
2N/A }
2N/A prop = scf_property_create(handle);
2N/A value = scf_value_create(handle);
2N/A if (prop == NULL || value == NULL) {
2N/A nd_error(nhdl, "Failed to allocate SMF structures");
2N/A goto bool_done;
2N/A }
2N/A if (scf_pg_get_property(pg, propname, prop) == -1 ||
2N/A scf_property_get_value(prop, value) == -1 ||
2N/A scf_value_get_boolean(value, val) == -1) {
2N/A nd_error(nhdl, "Failed to retrieve %s prop (%s)", propname,
2N/A scf_strerror(scf_error()));
2N/A goto bool_done;
2N/A }
2N/A ret = 0;
2N/A
2N/Abool_done:
2N/A scf_value_destroy(value);
2N/A scf_property_destroy(prop);
2N/A scf_pg_destroy(pg);
2N/A scf_handle_destroy(handle);
2N/A
2N/A return (ret);
2N/A}
2N/A
2N/Achar *
2N/And_get_event_fmri(nd_hdl_t *nhdl, fmev_t ev)
2N/A{
2N/A nvlist_t *ev_nvl, *attr_nvl;
2N/A char *svcname;
2N/A
2N/A if ((ev_nvl = fmev_attr_list(ev)) == NULL) {
2N/A nd_error(nhdl, "Failed to lookup event attr nvlist");
2N/A return (NULL);
2N/A }
2N/A if (nvlist_lookup_nvlist(ev_nvl, "attr", &attr_nvl) ||
2N/A nvlist_lookup_string(attr_nvl, "svc-string", &svcname)) {
2N/A nd_error(nhdl, "Malformed event 0x%p", (void *)ev_nvl);
2N/A return (NULL);
2N/A }
2N/A
2N/A return (strdup((const char *)svcname));
2N/A}
2N/A
2N/Aint
2N/And_get_notify_prefs(nd_hdl_t *nhdl, const char *mech, fmev_t ev,
2N/A nvlist_t ***pref_nvl, uint_t *nprefs)
2N/A{
2N/A nvlist_t *ev_nvl, *top_nvl, **np_nvlarr, *mech_nvl;
2N/A int ret = 1;
2N/A uint_t nelem;
2N/A
2N/A if ((ev_nvl = fmev_attr_list(ev)) == NULL) {
2N/A nd_error(nhdl, "Failed to lookup event attr nvlist");
2N/A return (-1);
2N/A }
2N/A
2N/A if ((ret = smf_notify_get_params(&top_nvl, ev_nvl)) != SCF_SUCCESS) {
2N/A ret = scf_error();
2N/A if (ret == SCF_ERROR_NOT_FOUND) {
2N/A nd_debug(nhdl, "No notification preferences specified "
2N/A "for this event");
2N/A goto pref_done;
2N/A } else {
2N/A nd_error(nhdl, "Error looking up notification "
2N/A "preferences (%s)", scf_strerror(ret));
2N/A nd_dump_nvlist(nhdl, top_nvl);
2N/A goto pref_done;
2N/A }
2N/A }
2N/A
2N/A if (nvlist_lookup_nvlist_array(top_nvl, SCF_NOTIFY_PARAMS, &np_nvlarr,
2N/A &nelem) != 0) {
2N/A nd_error(nhdl, "Malformed nvlist");
2N/A nd_dump_nvlist(nhdl, top_nvl);
2N/A ret = 1;
2N/A goto pref_done;
2N/A }
2N/A *pref_nvl = malloc(nelem * sizeof (nvlist_t *));
2N/A *nprefs = 0;
2N/A
2N/A for (int i = 0; i < nelem; i++) {
2N/A if (nvlist_lookup_nvlist(np_nvlarr[i], mech, &mech_nvl) == 0) {
2N/A (void) nvlist_dup(mech_nvl, *pref_nvl + *nprefs, 0);
2N/A ++*nprefs;
2N/A }
2N/A }
2N/A
2N/A if (*nprefs == 0) {
2N/A nd_debug(nhdl, "No %s notification preferences specified",
2N/A mech);
2N/A free(*pref_nvl);
2N/A ret = SCF_ERROR_NOT_FOUND;
2N/A goto pref_done;
2N/A }
2N/A ret = 0;
2N/Apref_done:
2N/A nvlist_free(top_nvl);
2N/A return (ret);
2N/A}
2N/A
2N/Astatic int
2N/And_seq_search(char *key, char **list, uint_t nelem)
2N/A{
2N/A for (int i = 0; i < nelem; i++)
2N/A if (strcmp(key, list[i]) == 0)
2N/A return (1);
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * This function takes a single string list and splits it into
2N/A * an string array (analogous to PERL split)
2N/A *
2N/A * The caller is responsible for freeing the array.
2N/A */
2N/Aint
2N/And_split_list(nd_hdl_t *nhdl, char *list, char *delim, char ***arr,
2N/A uint_t *nelem)
2N/A{
2N/A char *item, *tmpstr;
2N/A int i = 1, size = 1;
2N/A
2N/A tmpstr = strdup(list);
2N/A item = strtok(tmpstr, delim);
2N/A while (item && strtok(NULL, delim) != NULL)
2N/A size++;
2N/A free(tmpstr);
2N/A
2N/A if ((*arr = calloc(size, sizeof (char *))) == NULL) {
2N/A nd_error(nhdl, "Error allocating memory (%s)", strerror(errno));
2N/A return (-1);
2N/A }
2N/A if (size == 1)
2N/A (*arr)[0] = strdup(list);
2N/A else {
2N/A tmpstr = strdup(list);
2N/A item = strtok(tmpstr, delim);
2N/A (*arr)[0] = strdup(item);
2N/A while ((item = strtok(NULL, delim)) != NULL)
2N/A (*arr)[i++] = strdup(item);
2N/A free(tmpstr);
2N/A }
2N/A *nelem = size;
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * This function merges two string arrays into a single array, removing any
2N/A * duplicates
2N/A *
2N/A * The caller is responsible for freeing the merged array.
2N/A */
2N/Aint
2N/And_merge_strarray(nd_hdl_t *nhdl, char **arr1, uint_t n1, char **arr2,
2N/A uint_t n2, char ***buf)
2N/A{
2N/A char **tmparr;
2N/A int uniq = -1;
2N/A
2N/A tmparr = alloca((n1 + n2) * sizeof (char *));
2N/A bzero(tmparr, (n1 + n2) * sizeof (char *));
2N/A
2N/A while (++uniq < n1)
2N/A tmparr[uniq] = strdup(arr1[uniq]);
2N/A
2N/A for (int j = 0; j < n2; j++)
2N/A if (!nd_seq_search(arr2[j], tmparr, uniq))
2N/A tmparr[uniq++] = strdup(arr2[j]);
2N/A
2N/A if ((*buf = calloc(uniq, sizeof (char *))) == NULL) {
2N/A nd_error(nhdl, "Error allocating memory (%s)", strerror(errno));
2N/A for (int j = 0; j < uniq; j++) {
2N/A if (tmparr[j])
2N/A free(tmparr[j]);
2N/A }
2N/A return (-1);
2N/A }
2N/A
2N/A bcopy(tmparr, *buf, uniq * sizeof (char *));
2N/A return (uniq);
2N/A}
2N/A
2N/Avoid
2N/And_free_strarray(char **arr, uint_t arrsz)
2N/A{
2N/A for (uint_t i = 0; i < arrsz; i++)
2N/A free(arr[i]);
2N/A free(arr);
2N/A}
2N/A
2N/A/*
2N/A * This function joins all the strings in a string array into a single string
2N/A * Each element will be delimited by a comma
2N/A *
2N/A * The caller is responsible for freeing the joined string.
2N/A */
2N/Aint
2N/And_join_strarray(nd_hdl_t *nhdl, char **arr, uint_t arrsz, char **buf)
2N/A{
2N/A uint_t len = 0;
2N/A char *jbuf;
2N/A int i;
2N/A
2N/A /*
2N/A * First, figure out how much space we need to allocate to store the
2N/A * joined string.
2N/A */
2N/A for (i = 0; i < arrsz; i++)
2N/A len += strlen(arr[i]) + 1;
2N/A
2N/A if ((jbuf = calloc(len, sizeof (char))) == NULL) {
2N/A nd_error(nhdl, "Error allocating memory (%s)", strerror(errno));
2N/A return (-1);
2N/A }
2N/A
2N/A (void) snprintf(jbuf, len, "%s", arr[0]);
2N/A for (i = 1; i < arrsz; i++)
2N/A (void) snprintf(jbuf, len, "%s,%s", jbuf, arr[i]);
2N/A
2N/A *buf = jbuf;
2N/A return (0);
2N/A}
2N/A
2N/Avoid
2N/And_free_nvlarray(nvlist_t **arr, uint_t arrsz)
2N/A{
2N/A for (uint_t i = 0; i < arrsz; i++)
2N/A nvlist_free(arr[i]);
2N/A free(arr);
2N/A}
2N/A
2N/A/*
2N/A * This function takes a dictionary name and event class and then uses
2N/A * libdiagcode to compute the MSG ID. We need this for looking up messages
2N/A * for the committed ireport.* events. For FMA list.* events, the MSG ID is
2N/A * is contained in the event payload.
2N/A */
2N/Aint
2N/And_get_diagcode(nd_hdl_t *nhdl, const char *dict, const char *class, char *buf,
2N/A size_t buflen)
2N/A{
2N/A fm_dc_handle_t *dhp;
2N/A size_t dlen;
2N/A char *dirpath;
2N/A const char *key[2];
2N/A int ret = 0;
2N/A
2N/A dlen = (strlen(nhdl->nh_rootdir) + strlen(ND_DICTDIR) + 2);
2N/A dirpath = alloca(dlen);
2N/A (void) snprintf(dirpath, dlen, "%s/%s", nhdl->nh_rootdir, ND_DICTDIR);
2N/A
2N/A if ((dhp = fm_dc_opendict(FM_DC_VERSION, dirpath, dict)) == NULL) {
2N/A nd_error(nhdl, "fm_dc_opendict failed for %s/%s",
2N/A dirpath, dict);
2N/A return (-1);
2N/A }
2N/A
2N/A key[0] = class;
2N/A key[1] = NULL;
2N/A if (fm_dc_key2code(dhp, key, buf, buflen) < 0) {
2N/A nd_error(nhdl, "fm_dc_key2code failed for %s", key[0]);
2N/A ret = -1;
2N/A }
2N/A fm_dc_closedict(dhp);
2N/A return (ret);
2N/A}
2N/A
2N/A/*
2N/A * This function takes an event and extracts the bits of the event payload that
2N/A * are of interest to notification daemons and conveniently tucks them into a
2N/A * single struct.
2N/A *
2N/A * The caller is responsible for freeing ev_info and any contained strings and
2N/A * nvlists. A convenience function, nd_free_event_info(), is provided for this
2N/A * purpose.
2N/A */
2N/Aint
2N/And_get_event_info(nd_hdl_t *nhdl, const char *class, fmev_t ev,
2N/A nd_ev_info_t **ev_info)
2N/A{
2N/A nvlist_t *ev_nvl, *attr_nvl;
2N/A nd_ev_info_t *evi;
2N/A char *code, *uuid, *fmri, *from_state, *to_state, *reason;
2N/A
2N/A if ((evi = calloc(1, sizeof (nd_ev_info_t))) == NULL) {
2N/A nd_error(nhdl, "Failed to allocate memory");
2N/A return (-1);
2N/A }
2N/A
2N/A /*
2N/A * Hold event; class and payload will be valid for as long as
2N/A * we hold the event.
2N/A */
2N/A fmev_hold(ev);
2N/A evi->ei_ev = ev;
2N/A ev_nvl = fmev_attr_list(ev);
2N/A
2N/A /*
2N/A * Lookup the MSGID, event description and severity and KA URL
2N/A *
2N/A * For FMA list.* events we just pull it out of the the event nvlist.
2N/A * For all other events we call a utility function that computes the
2N/A * diagcode using the dict name and class.
2N/A */
2N/A evi->ei_diagcode = calloc(32, sizeof (char));
2N/A if ((nvlist_lookup_string(ev_nvl, FM_SUSPECT_DIAG_CODE, &code) == 0 &&
2N/A strcpy(evi->ei_diagcode, code))) {
2N/A evi->ei_severity = fmd_msg_getitem_nv(nhdl->nh_msghdl,
2N/A NULL, ev_nvl, FMD_MSG_ITEM_SEVERITY);
2N/A evi->ei_descr = fmd_msg_getitem_nv(nhdl->nh_msghdl,
2N/A NULL, ev_nvl, FMD_MSG_ITEM_DESC);
2N/A evi->ei_url = fmd_msg_getitem_nv(nhdl->nh_msghdl,
2N/A NULL, ev_nvl, FMD_MSG_ITEM_URL);
2N/A } else if (nd_get_diagcode(nhdl, "SMF", class, evi->ei_diagcode, 32)
2N/A == 0) {
2N/A evi->ei_severity = fmd_msg_getitem_id(nhdl->nh_msghdl,
2N/A NULL, evi->ei_diagcode, FMD_MSG_ITEM_SEVERITY);
2N/A evi->ei_descr = fmd_msg_getitem_id(nhdl->nh_msghdl,
2N/A NULL, evi->ei_diagcode, FMD_MSG_ITEM_DESC);
2N/A evi->ei_url = fmd_msg_getitem_id(nhdl->nh_msghdl,
2N/A NULL, evi->ei_diagcode, FMD_MSG_ITEM_URL);
2N/A } else {
2N/A (void) strcpy(evi->ei_diagcode, ND_UNKNOWN);
2N/A }
2N/A
2N/A if (!evi->ei_severity)
2N/A evi->ei_severity = strdup(ND_UNKNOWN);
2N/A if (!evi->ei_descr)
2N/A evi->ei_descr = strdup(ND_UNKNOWN);
2N/A if (!evi->ei_url)
2N/A evi->ei_url = strdup(ND_UNKNOWN);
2N/A
2N/A evi->ei_payload = ev_nvl;
2N/A evi->ei_class = fmev_class(ev);
2N/A if (nvlist_lookup_string(ev_nvl, FM_SUSPECT_UUID, &uuid) == 0)
2N/A evi->ei_uuid = strdup(uuid);
2N/A else {
2N/A nd_error(nhdl, "Malformed event");
2N/A nd_dump_nvlist(nhdl, evi->ei_payload);
2N/A nd_free_event_info(evi);
2N/A return (-1);
2N/A }
2N/A
2N/A if (strncmp(class, "ireport.os.smf", 14) == 0) {
2N/A if ((fmri = nd_get_event_fmri(nhdl, ev)) == NULL) {
2N/A nd_error(nhdl, "Failed to get fmri from event payload");
2N/A nd_free_event_info(evi);
2N/A return (-1);
2N/A }
2N/A if (nvlist_lookup_nvlist(evi->ei_payload, "attr", &attr_nvl) ||
2N/A nvlist_lookup_string(attr_nvl, "from-state", &from_state) ||
2N/A nvlist_lookup_string(attr_nvl, "to-state", &to_state) ||
2N/A nvlist_lookup_string(attr_nvl, "reason-long", &reason)) {
2N/A nd_error(nhdl, "Malformed event");
2N/A nd_dump_nvlist(nhdl, evi->ei_payload);
2N/A nd_free_event_info(evi);
2N/A free(fmri);
2N/A return (-1);
2N/A }
2N/A evi->ei_fmri = fmri;
2N/A evi->ei_to_state = strdup(to_state);
2N/A evi->ei_from_state = strdup(from_state);
2N/A evi->ei_reason = strdup(reason);
2N/A }
2N/A *ev_info = evi;
2N/A return (0);
2N/A}
2N/A
2N/Astatic void
2N/Acondfree(void *buf)
2N/A{
2N/A if (buf != NULL)
2N/A free(buf);
2N/A}
2N/A
2N/Avoid
2N/And_free_event_info(nd_ev_info_t *ev_info)
2N/A{
2N/A condfree(ev_info->ei_severity);
2N/A condfree(ev_info->ei_descr);
2N/A condfree(ev_info->ei_diagcode);
2N/A condfree(ev_info->ei_url);
2N/A condfree(ev_info->ei_uuid);
2N/A condfree(ev_info->ei_fmri);
2N/A condfree(ev_info->ei_from_state);
2N/A condfree(ev_info->ei_to_state);
2N/A condfree(ev_info->ei_reason);
2N/A fmev_rele(ev_info->ei_ev);
2N/A free(ev_info);
2N/A}