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 2009 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 
  27 /*
  28  * Copyright 2018 Nexenta Systems, Inc.
  29  */
  30 
  31 #include <sys/fm/protocol.h>
  32 
  33 #include <fm/fmd_msg.h>
  34 
  35 #include <alloca.h>
  36 #include <stdio.h>
  37 #include <strings.h>
  38 
  39 #include <fmd_protocol.h>
  40 #include <fmd_module.h>
  41 #include <fmd_conf.h>
  42 #include <fmd_subr.h>
  43 #include <fmd_error.h>
  44 #include <fmd_time.h>
  45 #include <fmd.h>
  46 
  47 /*
  48  * Create an FMRI authority element for the environment in which this instance
  49  * of fmd is deployed.  This function is called once and the result is cached.
  50  */
  51 nvlist_t *
  52 fmd_protocol_authority(void)
  53 {
  54         const char *str;
  55         nvlist_t *nvl;
  56         int err = 0;
  57 
  58         if (nvlist_xalloc(&nvl, NV_UNIQUE_NAME, &fmd.d_nva) != 0)
  59                 fmd_panic("failed to xalloc authority nvlist");
  60 
  61         err |= nvlist_add_uint8(nvl, FM_VERSION, FM_FMRI_AUTH_VERSION);
  62 
  63         if ((str = fmd_conf_getnzstr(fmd.d_conf, "product")) == NULL)
  64                 str = fmd_conf_getnzstr(fmd.d_conf, "platform");
  65 
  66         if (str != NULL)
  67                 err |= nvlist_add_string(nvl, FM_FMRI_AUTH_PRODUCT, str);
  68 
  69         if ((str = fmd_conf_getnzstr(fmd.d_conf, "product_sn")) != NULL)
  70                 err |= nvlist_add_string(nvl, FM_FMRI_AUTH_PRODUCT_SN, str);
  71 
  72         if ((str = fmd_conf_getnzstr(fmd.d_conf, "chassis")) != NULL)
  73                 err |= nvlist_add_string(nvl, FM_FMRI_AUTH_CHASSIS, str);
  74 
  75         if ((str = fmd_conf_getnzstr(fmd.d_conf, "domain")) != NULL)
  76                 err |= nvlist_add_string(nvl, FM_FMRI_AUTH_DOMAIN, str);
  77 
  78         if ((str = fmd_conf_getnzstr(fmd.d_conf, "server")) != NULL)
  79                 err |= nvlist_add_string(nvl, FM_FMRI_AUTH_SERVER, str);
  80 
  81         if (err != 0)
  82                 fmd_panic("failed to populate nvlist: %s\n", fmd_strerror(err));
  83 
  84         return (nvl);
  85 }
  86 
  87 /*
  88  * Create an FMRI for the specified module.  We use the cached authority
  89  * nvlist saved in fmd.d_auth to fill in the authority member.
  90  */
  91 nvlist_t *
  92 fmd_protocol_fmri_module(fmd_module_t *mp)
  93 {
  94         nvlist_t *nvl;
  95         int err = 0;
  96 
  97         if (nvlist_xalloc(&nvl, NV_UNIQUE_NAME, &fmd.d_nva) != 0)
  98                 fmd_panic("failed to xalloc diag-engine fmri nvlist");
  99 
 100         err |= nvlist_add_uint8(nvl, FM_VERSION, FM_FMD_SCHEME_VERSION);
 101         err |= nvlist_add_string(nvl, FM_FMRI_SCHEME, FM_FMRI_SCHEME_FMD);
 102         err |= nvlist_add_nvlist(nvl, FM_FMRI_AUTHORITY, fmd.d_auth);
 103         err |= nvlist_add_string(nvl, FM_FMRI_FMD_NAME, mp->mod_name);
 104 
 105         if (mp->mod_info != NULL) {
 106                 err |= nvlist_add_string(nvl,
 107                     FM_FMRI_FMD_VERSION, mp->mod_info->fmdi_vers);
 108         } else if (mp == fmd.d_rmod) {
 109                 err |= nvlist_add_string(nvl,
 110                     FM_FMRI_FMD_VERSION, fmd.d_version);
 111         }
 112 
 113         if (err != 0)
 114                 fmd_panic("failed to populate nvlist: %s\n", fmd_strerror(err));
 115 
 116         return (nvl);
 117 }
 118 
 119 nvlist_t *
 120 fmd_protocol_fault(const char *class, uint8_t certainty,
 121     nvlist_t *asru, nvlist_t *fru, nvlist_t *resource, const char *location)
 122 {
 123         nvlist_t *nvl;
 124         int err = 0;
 125 
 126         if (nvlist_xalloc(&nvl, NV_UNIQUE_NAME, &fmd.d_nva) != 0)
 127                 fmd_panic("failed to xalloc fault nvlist");
 128 
 129         err |= nvlist_add_uint8(nvl, FM_VERSION, FM_FAULT_VERSION);
 130         err |= nvlist_add_string(nvl, FM_CLASS, class);
 131         err |= nvlist_add_uint8(nvl, FM_FAULT_CERTAINTY, certainty);
 132 
 133         if (asru != NULL)
 134                 err |= nvlist_add_nvlist(nvl, FM_FAULT_ASRU, asru);
 135         if (fru != NULL)
 136                 err |= nvlist_add_nvlist(nvl, FM_FAULT_FRU, fru);
 137         if (resource != NULL)
 138                 err |= nvlist_add_nvlist(nvl, FM_FAULT_RESOURCE, resource);
 139         if (location != NULL)
 140                 err |= nvlist_add_string(nvl, FM_FAULT_LOCATION, location);
 141 
 142         if (err != 0)
 143                 fmd_panic("failed to populate nvlist: %s\n", fmd_strerror(err));
 144 
 145         return (nvl);
 146 }
 147 
 148 nvlist_t *
 149 fmd_protocol_list(const char *class, nvlist_t *de_fmri, const char *uuid,
 150     const char *code, uint_t argc, nvlist_t **argv, uint8_t *flagv, int domsg,
 151     struct timeval *tvp, int injected)
 152 {
 153         int64_t tod[2];
 154         nvlist_t *nvl;
 155         int err = 0;
 156         fmd_msg_hdl_t *msghdl;
 157         char *item;
 158 
 159         tod[0] = tvp->tv_sec;
 160         tod[1] = tvp->tv_usec;
 161 
 162         if (nvlist_xalloc(&nvl, NV_UNIQUE_NAME, &fmd.d_nva) != 0)
 163                 fmd_panic("failed to xalloc suspect list nvlist");
 164 
 165         err |= nvlist_add_uint8(nvl, FM_VERSION, FM_SUSPECT_VERSION);
 166         err |= nvlist_add_string(nvl, FM_CLASS, class);
 167         err |= nvlist_add_string(nvl, FM_SUSPECT_UUID, uuid);
 168         err |= nvlist_add_string(nvl, FM_SUSPECT_DIAG_CODE, code);
 169         err |= nvlist_add_int64_array(nvl, FM_SUSPECT_DIAG_TIME, tod, 2);
 170         err |= nvlist_add_nvlist(nvl, FM_SUSPECT_DE, de_fmri);
 171         err |= nvlist_add_uint32(nvl, FM_SUSPECT_FAULT_SZ, argc);
 172 
 173         if (injected)
 174                 err |= nvlist_add_boolean_value(nvl, FM_SUSPECT_INJECTED,
 175                     B_TRUE);
 176 
 177         if (!domsg) {
 178                 err |= nvlist_add_boolean_value(nvl,
 179                     FM_SUSPECT_MESSAGE, B_FALSE);
 180         }
 181 
 182         if (argc != 0) {
 183                 err |= nvlist_add_nvlist_array(nvl,
 184                     FM_SUSPECT_FAULT_LIST, argv, argc);
 185                 err |= nvlist_add_uint8_array(nvl,
 186                     FM_SUSPECT_FAULT_STATUS, flagv, argc);
 187         }
 188 
 189         /*
 190          * Attempt to lookup the type, severity, and description associated with
 191          * this diagnosis from the portable object file using the diag code.
 192          * Failure to init libfmd_msg or add to the nvlist will be treated as
 193          * fatal.  However, we won't treat a fmd_msg_getitem_id failure as fatal
 194          * since during development it's not uncommon to be working with po/dict
 195          * files that haven't yet been updated with newly added diagnoses.
 196          */
 197         msghdl = fmd_msg_init(fmd.d_rootdir, FMD_MSG_VERSION);
 198         if (msghdl == NULL)
 199                 fmd_panic("failed to initialize libfmd_msg\n");
 200 
 201         if ((item = fmd_msg_getitem_id(msghdl, NULL, code,
 202             FMD_MSG_ITEM_TYPE)) != NULL) {
 203                 err |= nvlist_add_string(nvl, FM_SUSPECT_TYPE, item);
 204                 free(item);
 205         }
 206         if ((item = fmd_msg_getitem_id(msghdl, NULL, code,
 207             FMD_MSG_ITEM_SEVERITY)) != NULL) {
 208                 err |= nvlist_add_string(nvl, FM_SUSPECT_SEVERITY, item);
 209                 free(item);
 210         }
 211         if ((item = fmd_msg_getitem_id(msghdl, NULL, code,
 212             FMD_MSG_ITEM_DESC)) != NULL) {
 213                 err |= nvlist_add_string(nvl, FM_SUSPECT_DESC, item);
 214                 free(item);
 215         }
 216         fmd_msg_fini(msghdl);
 217 
 218         if (err != 0)
 219                 fmd_panic("failed to populate nvlist: %s\n", fmd_strerror(err));
 220 
 221         return (nvl);
 222 }
 223 
 224 nvlist_t *
 225 fmd_protocol_rsrc_asru(const char *class,
 226     nvlist_t *fmri, const char *uuid, const char *code,
 227     boolean_t faulty, boolean_t unusable, boolean_t message, nvlist_t *event,
 228     struct timeval *tvp, boolean_t repaired, boolean_t replaced,
 229     boolean_t acquitted, boolean_t resolved, nvlist_t *diag_de,
 230     boolean_t injected)
 231 {
 232         nvlist_t *nvl;
 233         int64_t tod[2];
 234         int err = 0;
 235 
 236         tod[0] = tvp->tv_sec;
 237         tod[1] = tvp->tv_usec;
 238 
 239         if (nvlist_xalloc(&nvl, NV_UNIQUE_NAME, &fmd.d_nva) != 0)
 240                 fmd_panic("failed to xalloc resource nvlist");
 241 
 242         err |= nvlist_add_uint8(nvl, FM_VERSION, FM_RSRC_VERSION);
 243         err |= nvlist_add_string(nvl, FM_CLASS, class);
 244         if (fmri != NULL)
 245                 err |= nvlist_add_nvlist(nvl, FM_RSRC_RESOURCE, fmri);
 246 
 247         if (uuid != NULL)
 248                 err |= nvlist_add_string(nvl, FM_RSRC_ASRU_UUID, uuid);
 249 
 250         if (code != NULL)
 251                 err |= nvlist_add_string(nvl, FM_RSRC_ASRU_CODE, code);
 252 
 253         err |= nvlist_add_boolean_value(nvl, FM_RSRC_ASRU_FAULTY, faulty);
 254         err |= nvlist_add_boolean_value(nvl, FM_RSRC_ASRU_REPAIRED, repaired);
 255         err |= nvlist_add_boolean_value(nvl, FM_RSRC_ASRU_REPLACED, replaced);
 256         err |= nvlist_add_boolean_value(nvl, FM_RSRC_ASRU_ACQUITTED, acquitted);
 257         err |= nvlist_add_boolean_value(nvl, FM_RSRC_ASRU_RESOLVED, resolved);
 258         err |= nvlist_add_boolean_value(nvl, FM_RSRC_ASRU_UNUSABLE, unusable);
 259         err |= nvlist_add_boolean_value(nvl, FM_SUSPECT_MESSAGE, message);
 260         err |= nvlist_add_int64_array(nvl, FM_SUSPECT_DIAG_TIME, tod, 2);
 261 
 262         if (diag_de != NULL)
 263                 err |= nvlist_add_nvlist(nvl, FM_SUSPECT_DE, diag_de);
 264         if (injected)
 265                 err |= nvlist_add_boolean_value(nvl, FM_SUSPECT_INJECTED,
 266                     B_TRUE);
 267 
 268         if (event != NULL)
 269                 err |= nvlist_add_nvlist(nvl, FM_RSRC_ASRU_EVENT, event);
 270 
 271         if (err != 0)
 272                 fmd_panic("failed to populate nvlist: %s\n", fmd_strerror(err));
 273 
 274         return (nvl);
 275 }
 276 
 277 nvlist_t *
 278 fmd_protocol_fmderror(int errnum, const char *format, va_list ap)
 279 {
 280         uint64_t ena = fmd_ena();
 281         nvlist_t *nvl;
 282         int err = 0;
 283         char c, *msg;
 284         size_t len;
 285 
 286         if (nvlist_xalloc(&nvl, NV_UNIQUE_NAME, &fmd.d_nva) != 0)
 287                 return (NULL);
 288 
 289         len = vsnprintf(&c, 1, format, ap);
 290         msg = alloca(len + 1);
 291         (void) vsnprintf(msg, len + 1, format, ap);
 292 
 293         if (msg[len] == '\n')
 294                 msg[len] = '\0';
 295 
 296         err |= nvlist_add_uint8(nvl, FM_VERSION, FM_EREPORT_VERSION);
 297         err |= nvlist_add_string(nvl, FM_CLASS, fmd_errclass(errnum));
 298         err |= nvlist_add_uint64(nvl, FM_EREPORT_ENA, ena);
 299         err |= nvlist_add_string(nvl, FMD_ERR_MOD_MSG, msg);
 300 
 301         if (err != 0) {
 302                 nvlist_free(nvl);
 303                 return (NULL);
 304         }
 305 
 306         return (nvl);
 307 }
 308 
 309 nvlist_t *
 310 fmd_protocol_moderror(fmd_module_t *mp, int oserr, const char *msg)
 311 {
 312         uint64_t ena = fmd_ena();
 313         nvlist_t *nvl, *fmri;
 314         int err = 0;
 315 
 316         if (nvlist_xalloc(&nvl, NV_UNIQUE_NAME, &fmd.d_nva) != 0)
 317                 fmd_panic("failed to xalloc module error nvlist");
 318 
 319         if (mp->mod_fmri == NULL)
 320                 fmri = fmd_protocol_fmri_module(mp);
 321         else
 322                 fmri = mp->mod_fmri;
 323 
 324         err |= nvlist_add_uint8(nvl, FM_VERSION, FM_EREPORT_VERSION);
 325         err |= nvlist_add_string(nvl, FM_CLASS, fmd_errclass(EFMD_MODULE));
 326         err |= nvlist_add_nvlist(nvl, FM_EREPORT_DETECTOR, fmri);
 327         err |= nvlist_add_uint64(nvl, FM_EREPORT_ENA, ena);
 328         err |= nvlist_add_string(nvl, FMD_ERR_MOD_MSG, msg);
 329 
 330         if (mp->mod_fmri == NULL)
 331                 nvlist_free(fmri);
 332 
 333         if (oserr != 0) {
 334                 err |= nvlist_add_int32(nvl, FMD_ERR_MOD_ERRNO, oserr);
 335                 err |= nvlist_add_string(nvl, FMD_ERR_MOD_ERRCLASS,
 336                     fmd_errclass(oserr));
 337         }
 338 
 339         if (err != 0)
 340                 fmd_panic("failed to populate nvlist: %s\n", fmd_strerror(err));
 341 
 342         return (nvl);
 343 }
 344 
 345 nvlist_t *
 346 fmd_protocol_xprt_ctl(fmd_module_t *mp, const char *class, uint8_t version)
 347 {
 348         nvlist_t *nvl;
 349         int err = 0;
 350 
 351         if (nvlist_xalloc(&nvl, NV_UNIQUE_NAME, &fmd.d_nva) != 0)
 352                 fmd_panic("failed to xalloc rsrc xprt nvlist");
 353 
 354         err |= nvlist_add_uint8(nvl, FM_VERSION, version);
 355         err |= nvlist_add_string(nvl, FM_CLASS, class);
 356         err |= nvlist_add_nvlist(nvl, FM_RSRC_RESOURCE, mp->mod_fmri);
 357 
 358         if (err != 0)
 359                 fmd_panic("failed to populate nvlist: %s\n", fmd_strerror(err));
 360 
 361         return (nvl);
 362 }
 363 
 364 nvlist_t *
 365 fmd_protocol_xprt_sub(fmd_module_t *mp,
 366     const char *class, uint8_t version, const char *subclass)
 367 {
 368         nvlist_t *nvl = fmd_protocol_xprt_ctl(mp, class, version);
 369         int err = nvlist_add_string(nvl, FM_RSRC_XPRT_SUBCLASS, subclass);
 370 
 371         if (err != 0)
 372                 fmd_panic("failed to populate nvlist: %s\n", fmd_strerror(err));
 373 
 374         return (nvl);
 375 }
 376 
 377 nvlist_t *
 378 fmd_protocol_xprt_uuclose(fmd_module_t *mp, const char *class, uint8_t version,
 379     const char *uuid)
 380 {
 381         nvlist_t *nvl = fmd_protocol_xprt_ctl(mp, class, version);
 382         int err = nvlist_add_string(nvl, FM_RSRC_XPRT_UUID, uuid);
 383 
 384         if (err != 0)
 385                 fmd_panic("failed to populate nvlist: %s\n", fmd_strerror(err));
 386 
 387         return (nvl);
 388 }
 389 
 390 nvlist_t *
 391 fmd_protocol_xprt_uuresolved(fmd_module_t *mp, const char *class,
 392     uint8_t version, const char *uuid)
 393 {
 394         nvlist_t *nvl = fmd_protocol_xprt_ctl(mp, class, version);
 395         int err = nvlist_add_string(nvl, FM_RSRC_XPRT_UUID, uuid);
 396 
 397         if (err != 0)
 398                 fmd_panic("failed to populate nvlist: %s\n", fmd_strerror(err));
 399 
 400         return (nvl);
 401 }
 402 
 403 nvlist_t *
 404 fmd_protocol_xprt_updated(fmd_module_t *mp, const char *class, uint8_t version,
 405     const char *uuid, uint8_t *statusp, uint8_t *has_asrup, uint_t nelem)
 406 {
 407         nvlist_t *nvl = fmd_protocol_xprt_ctl(mp, class, version);
 408         int err = nvlist_add_string(nvl, FM_RSRC_XPRT_UUID, uuid);
 409 
 410         err |= nvlist_add_uint8_array(nvl, FM_RSRC_XPRT_FAULT_STATUS, statusp,
 411             nelem);
 412         if (has_asrup)
 413                 err |= nvlist_add_uint8_array(nvl, FM_RSRC_XPRT_FAULT_HAS_ASRU,
 414                     has_asrup, nelem);
 415 
 416         if (err != 0)
 417                 fmd_panic("failed to populate nvlist: %s\n", fmd_strerror(err));
 418 
 419         return (nvl);
 420 }