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 } 536