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 2007 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 
  27 #pragma ident   "%Z%%M% %I%     %E% SMI"
  28 
  29 #include <sys/fm/protocol.h>
  30 #include <sys/types.h>
  31 #include <sys/systeminfo.h>
  32 #include <fm/fmd_snmp.h>
  33 #include <fm/libtopo.h>
  34 #include <net-snmp/net-snmp-config.h>
  35 #include <net-snmp/net-snmp-includes.h>
  36 #include <net-snmp/agent/net-snmp-agent-includes.h>
  37 #include <libnvpair.h>
  38 #include <limits.h>
  39 #include <strings.h>
  40 #include <stddef.h>
  41 #include <unistd.h>
  42 #include <dlfcn.h>
  43 #include <errno.h>
  44 
  45 #define SCHEMEDIR_BASE  "/usr/lib/fm/fmd/schemes"
  46 
  47 #if defined(__sparcv9)
  48 #define DEFAULTSCHEMEDIR        SCHEMEDIR_BASE "/sparcv9"
  49 #elif defined(__amd64)
  50 #define DEFAULTSCHEMEDIR        SCHEMEDIR_BASE "/amd64"
  51 #else
  52 #define DEFAULTSCHEMEDIR        SCHEMEDIR_BASE
  53 #endif
  54 
  55 typedef struct fmd_scheme_ops {
  56         int (*sop_init)(void);
  57         void (*sop_fini)(void);
  58         ssize_t (*sop_nvl2str)(nvlist_t *, char *, size_t);
  59 } fmd_scheme_ops_t;
  60 
  61 typedef struct fmd_scheme_opd {
  62         const char *opd_name;           /* symbol name of scheme function */
  63         size_t opd_off;                 /* offset within fmd_scheme_ops_t */
  64 } fmd_scheme_opd_t;
  65 
  66 typedef struct fmd_scheme {
  67         struct fmd_scheme *sch_next;    /* next scheme on list of schemes */
  68         char *sch_name;                 /* name of this scheme (fmri prefix) */
  69         void *sch_dlp;                  /* libdl(3DL) shared library handle */
  70         int sch_err;                    /* if negative entry, errno to return */
  71         fmd_scheme_ops_t sch_ops;       /* scheme function pointers */
  72 } fmd_scheme_t;
  73 
  74 static fmd_scheme_t *sch_list;          /* list of cached schemes */
  75 static char *g_root;                    /* fmd root dir */
  76 static struct topo_hdl *g_thp;
  77 
  78 static long
  79 fmd_scheme_notsup(void)
  80 {
  81         errno = ENOTSUP;
  82         return (-1);
  83 }
  84 
  85 static int
  86 fmd_scheme_nop(void)
  87 {
  88         return (0);
  89 }
  90 
  91 /*
  92  * Default values for the scheme ops.  If a scheme function is not defined in
  93  * the module, then this operation is implemented using the default function.
  94  */
  95 static const fmd_scheme_ops_t _fmd_scheme_default_ops = {
  96         (int (*)())fmd_scheme_nop,              /* sop_init */
  97         (void (*)())fmd_scheme_nop,             /* sop_fini */
  98         (ssize_t (*)())fmd_scheme_notsup,       /* sop_nvl2str */
  99 };
 100 
 101 /*
 102  * Scheme ops descriptions.  These names and offsets are used by the function
 103  * fmd_scheme_rtld_init(), defined below, to load up a fmd_scheme_ops_t.
 104  */
 105 static const fmd_scheme_opd_t _fmd_scheme_ops[] = {
 106         { "fmd_fmri_init", offsetof(fmd_scheme_ops_t, sop_init) },
 107         { "fmd_fmri_fini", offsetof(fmd_scheme_ops_t, sop_fini) },
 108         { "fmd_fmri_nvl2str", offsetof(fmd_scheme_ops_t, sop_nvl2str) },
 109         { NULL, 0 }
 110 };
 111 
 112 static fmd_scheme_t *
 113 fmd_scheme_create(const char *name)
 114 {
 115         fmd_scheme_t *sp;
 116 
 117         if ((sp = malloc(sizeof (fmd_scheme_t))) == NULL ||
 118             (sp->sch_name = strdup(name)) == NULL) {
 119                 free(sp);
 120                 return (NULL);
 121         }
 122 
 123         sp->sch_next = sch_list;
 124         sp->sch_dlp = NULL;
 125         sp->sch_err = 0;
 126         sp->sch_ops = _fmd_scheme_default_ops;
 127 
 128         sch_list = sp;
 129         return (sp);
 130 }
 131 
 132 static int
 133 fmd_scheme_rtld_init(fmd_scheme_t *sp)
 134 {
 135         const fmd_scheme_opd_t *opd;
 136         void *p;
 137 
 138         for (opd = _fmd_scheme_ops; opd->opd_name != NULL; opd++) {
 139                 if ((p = dlsym(sp->sch_dlp, opd->opd_name)) != NULL)
 140                         *(void **)((uintptr_t)&sp->sch_ops + opd->opd_off) = p;
 141         }
 142 
 143         return (sp->sch_ops.sop_init());
 144 }
 145 
 146 static fmd_scheme_t *
 147 fmd_scheme_lookup(const char *dir, const char *name)
 148 {
 149         fmd_scheme_t *sp;
 150         char path[PATH_MAX];
 151 
 152         for (sp = sch_list; sp != NULL; sp = sp->sch_next) {
 153                 if (strcmp(name, sp->sch_name) == 0)
 154                         return (sp);
 155         }
 156 
 157         if ((sp = fmd_scheme_create(name)) == NULL)
 158                 return (NULL); /* errno is set for us */
 159 
 160         (void) snprintf(path, sizeof (path), "%s%s/%s.so",
 161             g_root ? g_root : "", dir, name);
 162 
 163         if (access(path, F_OK) != 0) {
 164                 sp->sch_err = errno;
 165                 return (sp);
 166         }
 167 
 168         if ((sp->sch_dlp = dlopen(path, RTLD_LOCAL | RTLD_NOW | RTLD_PARENT)) ==
 169             NULL) {
 170                 sp->sch_err = ELIBACC;
 171                 return (sp);
 172         }
 173 
 174         if (fmd_scheme_rtld_init(sp) != 0) {
 175                 sp->sch_err = errno;
 176                 (void) dlclose(sp->sch_dlp);
 177                 sp->sch_dlp = NULL;
 178         }
 179 
 180         return (sp);
 181 }
 182 
 183 char *
 184 sunFm_nvl2str(nvlist_t *nvl)
 185 {
 186         fmd_scheme_t *sp;
 187         char c, *name, *s = NULL;
 188         ssize_t len;
 189 
 190         if (nvlist_lookup_string(nvl, FM_FMRI_SCHEME, &name) != 0) {
 191                 DEBUGMSGTL((MODNAME_STR, "fmri does not contain required "
 192                     "'%s' nvpair\n", FM_FMRI_SCHEME));
 193                 return (NULL);
 194         }
 195 
 196         if ((sp = fmd_scheme_lookup(DEFAULTSCHEMEDIR, name)) == NULL ||
 197             sp->sch_dlp == NULL || sp->sch_err != 0) {
 198                 const char *msg =
 199                     sp->sch_err == ELIBACC ? dlerror() : strerror(sp->sch_err);
 200                 DEBUGMSGTL((MODNAME_STR, "cannot init '%s' scheme library to "
 201                     "format fmri: %s\n", name, msg ? msg : "unknown error"));
 202                 return (NULL);
 203         }
 204 
 205         if ((len = sp->sch_ops.sop_nvl2str(nvl, &c, sizeof (c))) == -1 ||
 206             (s = malloc(len + 1)) == NULL ||
 207             sp->sch_ops.sop_nvl2str(nvl, s, len + 1) == -1) {
 208                 DEBUGMSGTL((MODNAME_STR, "cannot format fmri using scheme '%s'",
 209                     name));
 210                 free(s);
 211                 return (NULL);
 212         }
 213 
 214         return (s);
 215 }
 216 
 217 void *
 218 fmd_fmri_alloc(size_t size)
 219 {
 220         return (malloc(size));
 221 }
 222 
 223 void *
 224 fmd_fmri_zalloc(size_t size)
 225 {
 226         void *data;
 227 
 228         if ((data = malloc(size)) != NULL)
 229                 bzero(data, size);
 230 
 231         return (data);
 232 }
 233 
 234 /*ARGSUSED*/
 235 void
 236 fmd_fmri_free(void *data, size_t size)
 237 {
 238         free(data);
 239 }
 240 
 241 int
 242 fmd_fmri_error(int err)
 243 {
 244         errno = err;
 245         return (-1);
 246 }
 247 
 248 char *
 249 fmd_fmri_strescape(const char *s)
 250 {
 251         return (strdup(s));
 252 }
 253 
 254 char *
 255 fmd_fmri_strdup(const char *s)
 256 {
 257         return (strdup(s));
 258 }
 259 
 260 void
 261 fmd_fmri_strfree(char *s)
 262 {
 263         free(s);
 264 }
 265 
 266 const char *
 267 fmd_fmri_get_rootdir(void)
 268 {
 269         return (g_root ? g_root : "");
 270 }
 271 
 272 const char *
 273 fmd_fmri_get_platform(void)
 274 {
 275         static char platform[MAXNAMELEN];
 276 
 277         if (platform[0] == '\0')
 278                 (void) sysinfo(SI_PLATFORM, platform, sizeof (platform));
 279 
 280         return (platform);
 281 }
 282 
 283 uint64_t
 284 fmd_fmri_get_drgen(void)
 285 {
 286         return (0);
 287 }
 288 
 289 int
 290 fmd_fmri_set_errno(int err)
 291 {
 292         errno = err;
 293         return (-1);
 294 }
 295 
 296 /*ARGSUSED*/
 297 void
 298 fmd_fmri_warn(const char *format, ...)
 299 {
 300 }
 301 
 302 struct topo_hdl *
 303 fmd_fmri_topo_hold(int version)
 304 {
 305         int err;
 306 
 307         if (version != TOPO_VERSION)
 308                 return (NULL);
 309 
 310         if (g_thp == NULL) {
 311                 if ((g_thp = topo_open(TOPO_VERSION, "/", &err)) == NULL) {
 312                         DEBUGMSGTL((MODNAME_STR, "topo_open failed: %s\n",
 313                             topo_strerror(err)));
 314                         return (NULL);
 315                 }
 316         }
 317 
 318         return (g_thp);
 319 }
 320 
 321 /*ARGSUSED*/
 322 void
 323 fmd_fmri_topo_rele(struct topo_hdl *thp)
 324 {
 325         /* nothing to do */
 326 }