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  * Simple-minded raw event publication from user context.  See extensive
  28  * comments in libfmevent.h.  These interfaces remain Project Private -
  29  * they have to evolve before rollout to Public levels.
  30  *
  31  * Events are dispatched synchronously using the GPEC sysevent mechanism.
  32  * The caller context must therefore be one in which a sysevent_evc_publish
  33  * (and possibly sysevent_evc_bind if not already bound) is safe.  We will
  34  * also allocate and manipulate nvlists.
  35  *
  36  * Since we use GPEC, which has no least privilege awareness, these interfaces
  37  * will only work for would-be producers running as root.
  38  *
  39  * There is no event rate throttling applied, so we rely on producers
  40  * to throttle themselves.  A future refinement should apply mandatory
  41  * but tuneable throttling on a per-producer basis.  In this first version
  42  * the only throttle is the publication event queue depth - we'll drop
  43  * events when the queue is full.
  44  *
  45  * We can publish over four channels, for privileged/non-privileged and
  46  * high/low priority.  Since only privileged producers will work now
  47  * (see above) we hardcode priv == B_TRUE and so only two channels are
  48  * actually used, separating higher and lower value streams from privileged
  49  * producers.
  50  */
  51 
  52 #include <stdarg.h>
  53 #include <unistd.h>
  54 #include <stdlib.h>
  55 #include <atomic.h>
  56 #include <errno.h>
  57 #include <pthread.h>
  58 #include <strings.h>
  59 
  60 #include "fmev_impl.h"
  61 
  62 static struct {
  63         const char *name;               /* channel name */
  64         evchan_t *binding;              /* GPEC binding, once bound */
  65         const uint32_t flags;           /* flags to use in binding */
  66 } chaninfo[] = {
  67         { FMEV_CHAN_USER_NOPRIV_LV, NULL, 0 },
  68         { FMEV_CHAN_USER_NOPRIV_HV, NULL, 0 },
  69         { FMEV_CHAN_USER_PRIV_LV, NULL, EVCH_HOLD_PEND_INDEF },
  70         { FMEV_CHAN_USER_PRIV_HV, NULL, EVCH_HOLD_PEND_INDEF}
  71 };
  72 
  73 #define CHANIDX(priv, pri) (2 * ((priv) != 0) + (pri == FMEV_HIPRI))
  74 
  75 #define CHAN_NAME(priv, pri) (chaninfo[CHANIDX(priv, pri)].name)
  76 #define CHAN_BINDING(priv, pri) (chaninfo[CHANIDX(priv, pri)].binding)
  77 #define CHAN_FLAGS(priv, pri) (chaninfo[CHANIDX(priv, pri)].flags)
  78 
  79 /*
  80  * Called after fork in the new child.  We clear the cached event
  81  * channel bindings which are only valid in the process that created
  82  * them.
  83  */
  84 static void
  85 clear_bindings(void)
  86 {
  87         int i;
  88 
  89         for (i = 0; i < sizeof (chaninfo) / sizeof chaninfo[0]; i++)
  90                 chaninfo[i].binding = NULL;
  91 }
  92 
  93 #pragma init(_fmev_publish_init)
  94 
  95 static void
  96 _fmev_publish_init(void)
  97 {
  98         (void) pthread_atfork(NULL, NULL, clear_bindings);
  99 }
 100 
 101 static evchan_t *
 102 bind_channel(boolean_t priv, fmev_pri_t pri)
 103 {
 104         evchan_t **evcpp = &CHAN_BINDING(priv, pri);
 105         evchan_t *evc;
 106 
 107         if (*evcpp != NULL)
 108                 return (*evcpp);
 109 
 110         if (sysevent_evc_bind(CHAN_NAME(priv, pri), &evc,
 111             EVCH_CREAT | CHAN_FLAGS(priv, pri)) != 0)
 112                 return (NULL);
 113 
 114         if (atomic_cas_ptr(evcpp, NULL, evc) != NULL)
 115                 (void) sysevent_evc_unbind(evc);
 116 
 117         return (*evcpp);
 118 }
 119 
 120 static fmev_err_t
 121 vrfy_ruleset(const char *ruleset)
 122 {
 123         if (ruleset != NULL &&
 124             strnlen(ruleset, FMEV_MAX_RULESET_LEN) == FMEV_MAX_RULESET_LEN)
 125                 return (FMEVERR_STRING2BIG);
 126 
 127         return (FMEV_OK);
 128 
 129 }
 130 
 131 static fmev_err_t
 132 vrfy_class(const char *class)
 133 {
 134         if (class == NULL || *class == '\0')
 135                 return (FMEVERR_API);
 136 
 137         if (strnlen(class, FMEV_PUB_MAXCLASSLEN) == FMEV_PUB_MAXCLASSLEN)
 138                 return (FMEVERR_STRING2BIG);
 139 
 140         return (FMEV_OK);
 141 }
 142 
 143 static fmev_err_t
 144 vrfy_subclass(const char *subclass)
 145 {
 146         if (subclass == NULL || *subclass == '\0')
 147                 return (FMEVERR_API);
 148 
 149         if (strnlen(subclass, FMEV_PUB_MAXSUBCLASSLEN) ==
 150             FMEV_PUB_MAXSUBCLASSLEN)
 151                 return (FMEVERR_STRING2BIG);
 152 
 153         return (FMEV_OK);
 154 }
 155 
 156 static fmev_err_t
 157 vrfy_pri(fmev_pri_t pri)
 158 {
 159         return (pri == FMEV_LOPRI || pri == FMEV_HIPRI ?
 160             FMEV_OK : FMEVERR_API);
 161 }
 162 
 163 const char *
 164 fmev_pri_string(fmev_pri_t pri)
 165 {
 166         static const char *pristr[] = { "low", "high" };
 167 
 168         if (vrfy_pri(pri) != FMEV_OK)
 169                 return (NULL);
 170 
 171         return (pristr[pri - FMEV_LOPRI]);
 172 }
 173 
 174 static fmev_err_t
 175 vrfy(const char **rulesetp, const char **classp, const char **subclassp,
 176     fmev_pri_t *prip)
 177 {
 178         fmev_err_t rc = FMEV_OK;
 179 
 180         if (rulesetp && (rc = vrfy_ruleset(*rulesetp)) != FMEV_OK)
 181                 return (rc);
 182 
 183         if (classp && (rc = vrfy_class(*classp)) != FMEV_OK ||
 184             subclassp && (rc = vrfy_subclass(*subclassp)) != FMEV_OK ||
 185             prip && (rc = vrfy_pri(*prip)) != FMEV_OK)
 186                 return (rc);
 187 
 188         return (FMEV_OK);
 189 }
 190 
 191 uint_t fmev_va2nvl_maxtuples = 100;
 192 
 193 fmev_err_t
 194 va2nvl(nvlist_t **nvlp, va_list ap, uint_t ntuples)
 195 {
 196         nvlist_t *nvl = NULL;
 197         uint_t processed = 0;
 198         char *name;
 199 
 200         if (ntuples == 0)
 201                 return (FMEVERR_INTERNAL);
 202 
 203         if ((name = va_arg(ap, char *)) == NULL || name == FMEV_ARG_TERM)
 204                 return (FMEVERR_VARARGS_MALFORMED);
 205 
 206         if (ntuples > fmev_va2nvl_maxtuples)
 207                 return (FMEVERR_VARARGS_TOOLONG);
 208 
 209         if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0)
 210                 return (FMEVERR_ALLOC);
 211 
 212         while (name != NULL && name != FMEV_ARG_TERM && processed <= ntuples) {
 213                 data_type_t type;
 214                 int err, nelem;
 215 
 216                 type = va_arg(ap, data_type_t);
 217 
 218                 switch (type) {
 219                 case DATA_TYPE_BYTE:
 220                         err = nvlist_add_byte(nvl, name,
 221                             va_arg(ap, uint_t));
 222                         break;
 223                 case DATA_TYPE_BYTE_ARRAY:
 224                         nelem = va_arg(ap, int);
 225                         err = nvlist_add_byte_array(nvl, name,
 226                             va_arg(ap, uchar_t *), nelem);
 227                         break;
 228                 case DATA_TYPE_BOOLEAN_VALUE:
 229                         err = nvlist_add_boolean_value(nvl, name,
 230                             va_arg(ap, boolean_t));
 231                         break;
 232                 case DATA_TYPE_BOOLEAN_ARRAY:
 233                         nelem = va_arg(ap, int);
 234                         err = nvlist_add_boolean_array(nvl, name,
 235                             va_arg(ap, boolean_t *), nelem);
 236                         break;
 237                 case DATA_TYPE_INT8:
 238                         err = nvlist_add_int8(nvl, name,
 239                             va_arg(ap, int));
 240                         break;
 241                 case DATA_TYPE_INT8_ARRAY:
 242                         nelem = va_arg(ap, int);
 243                         err = nvlist_add_int8_array(nvl, name,
 244                             va_arg(ap, int8_t *), nelem);
 245                         break;
 246                 case DATA_TYPE_UINT8:
 247                         err = nvlist_add_uint8(nvl, name,
 248                             va_arg(ap, uint_t));
 249                         break;
 250                 case DATA_TYPE_UINT8_ARRAY:
 251                         nelem = va_arg(ap, int);
 252                         err = nvlist_add_uint8_array(nvl, name,
 253                             va_arg(ap, uint8_t *), nelem);
 254                         break;
 255                 case DATA_TYPE_INT16:
 256                         err = nvlist_add_int16(nvl, name,
 257                             va_arg(ap, int));
 258                         break;
 259                 case DATA_TYPE_INT16_ARRAY:
 260                         nelem = va_arg(ap, int);
 261                         err = nvlist_add_int16_array(nvl, name,
 262                             va_arg(ap, int16_t *), nelem);
 263                         break;
 264                 case DATA_TYPE_UINT16:
 265                         err = nvlist_add_uint16(nvl, name,
 266                             va_arg(ap, uint_t));
 267                         break;
 268                 case DATA_TYPE_UINT16_ARRAY:
 269                         nelem = va_arg(ap, int);
 270                         err = nvlist_add_uint16_array(nvl, name,
 271                             va_arg(ap, uint16_t *), nelem);
 272                         break;
 273                 case DATA_TYPE_INT32:
 274                         err = nvlist_add_int32(nvl, name,
 275                             va_arg(ap, int32_t));
 276                         break;
 277                 case DATA_TYPE_INT32_ARRAY:
 278                         nelem = va_arg(ap, int);
 279                         err = nvlist_add_int32_array(nvl, name,
 280                             va_arg(ap, int32_t *), nelem);
 281                         break;
 282                 case DATA_TYPE_UINT32:
 283                         err = nvlist_add_uint32(nvl, name,
 284                             va_arg(ap, uint32_t));
 285                         break;
 286                 case DATA_TYPE_UINT32_ARRAY:
 287                         nelem = va_arg(ap, int);
 288                         err = nvlist_add_uint32_array(nvl, name,
 289                             va_arg(ap, uint32_t *), nelem);
 290                         break;
 291                 case DATA_TYPE_INT64:
 292                         err = nvlist_add_int64(nvl, name,
 293                             va_arg(ap, int64_t));
 294                         break;
 295                 case DATA_TYPE_INT64_ARRAY:
 296                         nelem = va_arg(ap, int);
 297                         err = nvlist_add_int64_array(nvl, name,
 298                             va_arg(ap, int64_t *), nelem);
 299                         break;
 300                 case DATA_TYPE_UINT64:
 301                         err = nvlist_add_uint64(nvl, name,
 302                             va_arg(ap, uint64_t));
 303                         break;
 304                 case DATA_TYPE_UINT64_ARRAY:
 305                         nelem = va_arg(ap, int);
 306                         err = nvlist_add_uint64_array(nvl, name,
 307                             va_arg(ap, uint64_t *), nelem);
 308                         break;
 309                 case DATA_TYPE_STRING:
 310                         err = nvlist_add_string(nvl, name,
 311                             va_arg(ap, char *));
 312                         break;
 313                 case DATA_TYPE_STRING_ARRAY:
 314                         nelem = va_arg(ap, int);
 315                         err = nvlist_add_string_array(nvl, name,
 316                             va_arg(ap, char **), nelem);
 317                         break;
 318                 case DATA_TYPE_NVLIST:
 319                         err = nvlist_add_nvlist(nvl, name,
 320                             va_arg(ap, nvlist_t *));
 321                         break;
 322                 case DATA_TYPE_NVLIST_ARRAY:
 323                         nelem = va_arg(ap, int);
 324                         err = nvlist_add_nvlist_array(nvl, name,
 325                             va_arg(ap, nvlist_t **), nelem);
 326                         break;
 327                 case DATA_TYPE_HRTIME:
 328                         err = nvlist_add_hrtime(nvl, name,
 329                             va_arg(ap, hrtime_t));
 330                         break;
 331                 case DATA_TYPE_DOUBLE:
 332                         err = nvlist_add_double(nvl, name,
 333                             va_arg(ap, double));
 334                         break;
 335                 default:
 336                         err = EINVAL;
 337                 }
 338 
 339                 if (err)
 340                         break;  /* terminate on first error */
 341 
 342                 processed++;
 343                 name = va_arg(ap, char *);
 344         }
 345 
 346         if (name != FMEV_ARG_TERM || processed != ntuples) {
 347                 *nvlp = NULL;
 348                 nvlist_free(nvl);
 349                 return (FMEVERR_VARARGS_MALFORMED);
 350         }
 351 
 352         *nvlp = nvl;
 353         return (FMEV_SUCCESS);
 354 }
 355 
 356 static fmev_err_t
 357 do_publish(const char *file, const char *func, int64_t line,
 358     const char *ruleset, const char *class, const char *subclass,
 359     fmev_pri_t pri, nvlist_t *nvl, uint_t ntuples, va_list ap)
 360 {
 361         fmev_err_t rc = FMEVERR_INTERNAL;
 362         boolean_t priv = B_TRUE;
 363         nvlist_t *tmpnvl = NULL;
 364         nvlist_t *pub;
 365         evchan_t *evc;
 366 
 367         if (nvl) {
 368                 ASSERT(ntuples == 0);
 369 
 370                 /*
 371                  * Enforce NV_UNIQUE_NAME
 372                  */
 373                 if ((nvlist_nvflag(nvl) & NV_UNIQUE_NAME) != NV_UNIQUE_NAME)
 374                         return (FMEVERR_NVLIST);
 375 
 376                 pub = nvl;
 377 
 378         } else if (ntuples != 0) {
 379                 fmev_err_t err;
 380 
 381                 err = va2nvl(&tmpnvl, ap, ntuples);
 382                 if (err != FMEV_SUCCESS)
 383                         return (err);
 384 
 385                 pub = tmpnvl;
 386         } else {
 387                 /*
 388                  * Even if the caller has no tuples to publish (just an event
 389                  * class and subclass), we are going to add some detector
 390                  * information so we need some nvlist.
 391                  */
 392                 if (nvlist_alloc(&tmpnvl, NV_UNIQUE_NAME, 0) != 0)
 393                         return (FMEVERR_ALLOC);
 394 
 395                 pub = tmpnvl;
 396         }
 397 
 398         evc = bind_channel(priv, pri);
 399 
 400         if (evc == NULL) {
 401                 rc = FMEVERR_INTERNAL;
 402                 goto done;
 403         }
 404 
 405 
 406         /*
 407          * Add detector information
 408          */
 409         if (file && nvlist_add_string(pub, "__fmev_file", file) != 0 ||
 410             func && nvlist_add_string(pub, "__fmev_func", func) != 0 ||
 411             line != -1 && nvlist_add_int64(pub, "__fmev_line", line) != 0 ||
 412             nvlist_add_int32(pub, "__fmev_pid", getpid()) != 0 ||
 413             nvlist_add_string(pub, "__fmev_execname", getexecname()) != 0) {
 414                 rc = FMEVERR_ALLOC;
 415                 goto done;
 416         }
 417 
 418         if (ruleset == NULL)
 419                 ruleset = FMEV_RULESET_DEFAULT;
 420 
 421         /*
 422          * We abuse the GPEC publication arguments as follows:
 423          *
 424          * GPEC argument        Our usage
 425          * -------------------- -----------------
 426          * const char *class    Raw class
 427          * const char *subclass Raw subclass
 428          * const char *vendor   Ruleset name
 429          * const char *pub_name Unused
 430          * nvlist_t *attr_list  Event attributes
 431          */
 432         rc = (sysevent_evc_publish(evc, class, subclass, ruleset, "",
 433             pub, EVCH_NOSLEEP) == 0) ? FMEV_SUCCESS : FMEVERR_TRANSPORT;
 434 
 435 done:
 436         /* Free a passed in nvlist iff success */
 437         if (rc == FMEV_SUCCESS)
 438                 nvlist_free(nvl);
 439 
 440         nvlist_free(tmpnvl);
 441 
 442         return (rc);
 443 }
 444 
 445 fmev_err_t
 446 _i_fmev_publish_nvl(
 447     const char *file, const char *func, int64_t line,
 448     const char *ruleset, const char *class, const char *subclass,
 449     fmev_pri_t pri, nvlist_t *attr)
 450 {
 451         fmev_err_t rc;
 452 
 453         if ((rc = vrfy(&ruleset, &class, &subclass, &pri)) != FMEV_OK)
 454                 return (rc);            /* any attr not freed */
 455 
 456         return (do_publish(file, func, line,
 457             ruleset, class, subclass,
 458             pri, attr, 0, NULL));       /* any attr freed iff success */
 459 }
 460 
 461 fmev_err_t
 462 _i_fmev_publish(
 463     const char *file, const char *func, int64_t line,
 464     const char *ruleset, const char *class, const char *subclass,
 465     fmev_pri_t pri,
 466     uint_t ntuples, ...)
 467 {
 468         va_list ap;
 469         fmev_err_t rc;
 470 
 471         if ((rc = vrfy(&ruleset, &class, &subclass, &pri)) != FMEV_OK)
 472                 return (rc);
 473 
 474         if (ntuples != 0)
 475                 va_start(ap, ntuples);
 476 
 477         rc = do_publish(file, func, line,
 478             ruleset, class, subclass,
 479             pri, NULL, ntuples, ap);
 480 
 481         if (ntuples != 0)
 482                 va_end(ap);
 483 
 484         return (rc);
 485 }
 486 
 487 
 488 #pragma weak fmev_publish = _fmev_publish
 489 #pragma weak fmev_rspublish = _fmev_rspublish
 490 
 491 static fmev_err_t
 492 _fmev_publish(const char *class, const char *subclass, fmev_pri_t pri,
 493     uint_t ntuples, ...)
 494 {
 495         fmev_err_t rc;
 496         va_list ap;
 497 
 498         if ((rc = vrfy(NULL, &class, &subclass, &pri)) != FMEV_OK)
 499                 return (rc);
 500 
 501         if (ntuples != 0)
 502                 va_start(ap, ntuples);
 503 
 504         rc = do_publish(NULL, NULL, -1,
 505             FMEV_RULESET_DEFAULT, class, subclass,
 506             pri, NULL, ntuples, ap);
 507 
 508         if (ntuples != 0)
 509                 va_end(ap);
 510 
 511         return (rc);
 512 }
 513 
 514 static fmev_err_t
 515 _fmev_rspublish(const char *ruleset, const char *class, const char *subclass,
 516     fmev_pri_t pri, uint_t ntuples, ...)
 517 {
 518         fmev_err_t rc;
 519         va_list ap;
 520 
 521         if ((rc = vrfy(&ruleset, &class, &subclass, &pri)) != FMEV_OK)
 522                 return (rc);
 523 
 524         if (ntuples != 0)
 525                 va_start(ap, ntuples);
 526 
 527         rc = do_publish(NULL, NULL, -1,
 528             ruleset, class, subclass,
 529             pri, NULL, ntuples, ap);
 530 
 531         if (ntuples != 0)
 532                 va_end(ap);
 533 
 534         return (rc);
 535 }