5754 prtconf: assertion failure in libpcidb if vendor cannot be looked up
[illumos-gate.git] / usr / src / lib / libsysevent / libsysevent.c
blob28fdc6ca5017d15e222c21e3bbcaf21d4af2e542
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
23 * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
26 #include <stdio.h>
27 #include <fcntl.h>
28 #include <errno.h>
29 #include <door.h>
30 #include <unistd.h>
31 #include <stddef.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <strings.h>
35 #include <synch.h>
36 #include <pthread.h>
37 #include <signal.h>
38 #include <thread.h>
39 #include <libnvpair.h>
40 #include <assert.h>
41 #include <sys/stat.h>
42 #include <sys/types.h>
43 #include <sys/modctl.h>
44 #include <sys/mnttab.h>
45 #include <sys/sysevent.h>
46 #include <sys/sysevent_impl.h>
48 #include "libsysevent.h"
49 #include "libsysevent_impl.h"
52 * libsysevent - The system event framework library
54 * This library provides routines to help with marshalling
55 * and unmarshalling of data contained in a sysevent event
56 * buffer.
59 #define SE_ENCODE_METHOD NV_ENCODE_NATIVE
61 #define dprint if (libsysevent_debug) (void) printf
62 static int libsysevent_debug = 0;
64 static sysevent_t *se_unpack(sysevent_t *);
65 static int cleanup_id(sysevent_handle_t *shp, uint32_t id, int type);
68 * The following routines allow system event publication to the sysevent
69 * framework.
73 * sysevent_alloc - allocate a sysevent buffer
75 static sysevent_t *
76 sysevent_alloc(char *class, int class_sz, char *subclass, int subclass_sz,
77 char *pub, int pub_sz, nvlist_t *attr_list)
79 int payload_sz;
80 int aligned_class_sz, aligned_subclass_sz, aligned_pub_sz;
81 size_t nvlist_sz = 0;
82 char *attr;
83 uint64_t attr_offset;
84 sysevent_t *ev;
86 if (attr_list != NULL) {
87 if (nvlist_size(attr_list, &nvlist_sz, SE_ENCODE_METHOD)
88 != 0) {
89 return (NULL);
94 * Calculate and reserve space for the class, subclass and
95 * publisher strings in the event buffer
98 /* String sizes must be 64-bit aligned in the event buffer */
99 aligned_class_sz = SE_ALIGN(class_sz);
100 aligned_subclass_sz = SE_ALIGN(subclass_sz);
101 aligned_pub_sz = SE_ALIGN(pub_sz);
103 payload_sz = (aligned_class_sz - sizeof (uint64_t)) +
104 (aligned_subclass_sz - sizeof (uint64_t)) +
105 (aligned_pub_sz - sizeof (uint64_t)) - sizeof (uint64_t) +
106 nvlist_sz;
109 * Allocate event buffer plus additional payload overhead.
111 ev = calloc(1, sizeof (sysevent_impl_t) + payload_sz);
112 if (ev == NULL) {
113 return (NULL);
116 /* Initialize the event buffer data */
117 SE_VERSION(ev) = SYS_EVENT_VERSION;
118 (void) bcopy(class, SE_CLASS_NAME(ev), class_sz);
120 SE_SUBCLASS_OFF(ev) = SE_ALIGN(offsetof(sysevent_impl_t, se_class_name))
121 + aligned_class_sz;
122 (void) bcopy(subclass, SE_SUBCLASS_NAME(ev), subclass_sz);
124 SE_PUB_OFF(ev) = SE_SUBCLASS_OFF(ev) + aligned_subclass_sz;
125 (void) bcopy(pub, SE_PUB_NAME(ev), pub_sz);
127 SE_PAYLOAD_SZ(ev) = payload_sz;
128 SE_ATTR_PTR(ev) = (uint64_t)0;
130 /* Check for attribute list */
131 if (attr_list == NULL) {
132 return (ev);
135 /* Copy attribute data to contiguous memory */
136 SE_FLAG(ev) = SE_PACKED_BUF;
137 attr_offset = SE_ATTR_OFF(ev);
138 attr = (char *)((caddr_t)ev + attr_offset);
139 if (nvlist_pack(attr_list, &attr, &nvlist_sz, SE_ENCODE_METHOD,
140 0) != 0) {
141 free(ev);
142 return (NULL);
145 return (ev);
149 * sysevent_post_event - generate a system event via the sysevent framework
152 sysevent_post_event(char *class, char *subclass, char *vendor, char *pub_name,
153 nvlist_t *attr_list, sysevent_id_t *eid)
155 int error;
156 sysevent_t *ev;
158 ev = sysevent_alloc_event(class, subclass, vendor, pub_name, attr_list);
159 if (ev == NULL) {
160 return (-1);
163 error = modctl(MODEVENTS, (uintptr_t)MODEVENTS_POST_EVENT,
164 (uintptr_t)ev, (uintptr_t)SE_SIZE(ev), (uintptr_t)eid, 0);
166 sysevent_free(ev);
168 if (error) {
169 errno = EIO;
170 return (-1);
173 return (0);
177 * The following routines are used to free or duplicate a
178 * sysevent event buffer.
182 * sysevent_dup - Allocate and copy an event buffer
183 * Copies both packed and unpacked to unpacked sysevent.
185 sysevent_t *
186 sysevent_dup(sysevent_t *ev)
188 nvlist_t *nvl, *cnvl = NULL;
189 uint64_t attr_offset;
190 sysevent_t *copy;
192 if (SE_FLAG(ev) == SE_PACKED_BUF)
193 return (se_unpack(ev));
195 /* Copy event header information */
196 attr_offset = SE_ATTR_OFF(ev);
197 copy = calloc(1, attr_offset);
198 if (copy == NULL)
199 return (NULL);
200 bcopy(ev, copy, attr_offset);
202 nvl = (nvlist_t *)(uintptr_t)SE_ATTR_PTR(ev);
203 if (nvl && nvlist_dup(nvl, &cnvl, 0) != 0) {
204 free(copy);
205 return (NULL);
208 SE_ATTR_PTR(copy) = (uintptr_t)cnvl;
209 SE_FLAG(copy) = 0; /* unpacked */
210 return (copy);
214 * sysevent_free - Free memory allocated for an event buffer
216 void
217 sysevent_free(sysevent_t *ev)
219 nvlist_t *attr_list = (nvlist_t *)(uintptr_t)SE_ATTR_PTR(ev);
221 if (attr_list)
222 nvlist_free(attr_list);
223 free(ev);
227 * The following routines are used to extract attribute data from a sysevent
228 * handle.
232 * sysevent_get_attr_list - allocate and return an attribute associated with
233 * the given sysevent buffer.
236 sysevent_get_attr_list(sysevent_t *ev, nvlist_t **nvlist)
238 int error;
239 caddr_t attr;
240 size_t attr_len;
241 uint64_t attr_offset;
242 nvlist_t *nvl;
244 *nvlist = NULL;
246 /* Duplicate attribute for an unpacked sysevent buffer */
247 if (SE_FLAG(ev) != SE_PACKED_BUF) {
248 nvl = (nvlist_t *)(uintptr_t)SE_ATTR_PTR(ev);
249 if (nvl == NULL) {
250 return (0);
252 if ((error = nvlist_dup(nvl, nvlist, 0)) != 0) {
253 if (error == ENOMEM) {
254 errno = error;
255 } else {
256 errno = EINVAL;
258 return (-1);
260 return (0);
263 attr_offset = SE_ATTR_OFF(ev);
264 if (SE_SIZE(ev) == attr_offset) {
265 return (0);
268 /* unpack nvlist */
269 attr = (caddr_t)ev + attr_offset;
270 attr_len = SE_SIZE(ev) - attr_offset;
271 if ((error = nvlist_unpack(attr, attr_len, nvlist, 0)) != 0) {
272 if (error == ENOMEM) {
273 errno = error;
274 } else {
275 errno = EINVAL;
277 return (-1);
280 return (0);
284 * sysevent_attr_name - Get name of attribute
286 char *
287 sysevent_attr_name(sysevent_attr_t *attr)
289 if (attr == NULL) {
290 errno = EINVAL;
291 return (NULL);
293 return (nvpair_name((nvpair_t *)attr));
297 * sysevent_attr_value - Get attribute value data and type
300 sysevent_attr_value(sysevent_attr_t *attr, sysevent_value_t *se_value)
302 nvpair_t *nvp = attr;
304 if (nvp == NULL)
305 return (EINVAL);
307 /* Convert DATA_TYPE_* to SE_DATA_TYPE_* */
308 switch (nvpair_type(nvp)) {
309 case DATA_TYPE_BYTE:
310 se_value->value_type = SE_DATA_TYPE_BYTE;
311 (void) nvpair_value_byte(nvp, &se_value->value.sv_byte);
312 break;
313 case DATA_TYPE_INT16:
314 se_value->value_type = SE_DATA_TYPE_INT16;
315 (void) nvpair_value_int16(nvp, &se_value->value.sv_int16);
316 break;
317 case DATA_TYPE_UINT16:
318 se_value->value_type = SE_DATA_TYPE_UINT16;
319 (void) nvpair_value_uint16(nvp, &se_value->value.sv_uint16);
320 break;
321 case DATA_TYPE_INT32:
322 se_value->value_type = SE_DATA_TYPE_INT32;
323 (void) nvpair_value_int32(nvp, &se_value->value.sv_int32);
324 break;
325 case DATA_TYPE_UINT32:
326 se_value->value_type = SE_DATA_TYPE_UINT32;
327 (void) nvpair_value_uint32(nvp, &se_value->value.sv_uint32);
328 break;
329 case DATA_TYPE_INT64:
330 se_value->value_type = SE_DATA_TYPE_INT64;
331 (void) nvpair_value_int64(nvp, &se_value->value.sv_int64);
332 break;
333 case DATA_TYPE_UINT64:
334 se_value->value_type = SE_DATA_TYPE_UINT64;
335 (void) nvpair_value_uint64(nvp, &se_value->value.sv_uint64);
336 break;
337 case DATA_TYPE_STRING:
338 se_value->value_type = SE_DATA_TYPE_STRING;
339 (void) nvpair_value_string(nvp, &se_value->value.sv_string);
340 break;
341 case DATA_TYPE_BYTE_ARRAY:
342 se_value->value_type = SE_DATA_TYPE_BYTES;
343 (void) nvpair_value_byte_array(nvp,
344 &se_value->value.sv_bytes.data,
345 (uint_t *)&se_value->value.sv_bytes.size);
346 break;
347 case DATA_TYPE_HRTIME:
348 se_value->value_type = SE_DATA_TYPE_TIME;
349 (void) nvpair_value_hrtime(nvp, &se_value->value.sv_time);
350 break;
351 default:
352 return (ENOTSUP);
354 return (0);
358 * sysevent_attr_next - Get next attribute in event attribute list
360 sysevent_attr_t *
361 sysevent_attr_next(sysevent_t *ev, sysevent_attr_t *attr)
363 nvlist_t *nvl;
364 nvpair_t *nvp = attr;
366 /* all user visible sysevent_t's are unpacked */
367 assert(SE_FLAG(ev) != SE_PACKED_BUF);
369 if (SE_ATTR_PTR(ev) == (uint64_t)0) {
370 return (NULL);
373 nvl = (nvlist_t *)(uintptr_t)SE_ATTR_PTR(ev);
374 return (nvlist_next_nvpair(nvl, nvp));
378 * sysevent_lookup_attr - Lookup attribute by name and datatype.
381 sysevent_lookup_attr(sysevent_t *ev, char *name, int datatype,
382 sysevent_value_t *se_value)
384 nvpair_t *nvp;
385 nvlist_t *nvl;
387 assert(SE_FLAG(ev) != SE_PACKED_BUF);
389 if (SE_ATTR_PTR(ev) == (uint64_t)0) {
390 return (ENOENT);
394 * sysevent matches on both name and datatype
395 * nvlist_look mataches name only. So we walk
396 * nvlist manually here.
398 nvl = (nvlist_t *)(uintptr_t)SE_ATTR_PTR(ev);
399 nvp = nvlist_next_nvpair(nvl, NULL);
400 while (nvp) {
401 if ((strcmp(name, nvpair_name(nvp)) == 0) &&
402 (sysevent_attr_value(nvp, se_value) == 0) &&
403 (se_value->value_type == datatype))
404 return (0);
405 nvp = nvlist_next_nvpair(nvl, nvp);
407 return (ENOENT);
410 /* Routines to extract event header information */
413 * sysevent_get_class - Get class id
416 sysevent_get_class(sysevent_t *ev)
418 return (SE_CLASS(ev));
422 * sysevent_get_subclass - Get subclass id
425 sysevent_get_subclass(sysevent_t *ev)
427 return (SE_SUBCLASS(ev));
431 * sysevent_get_class_name - Get class name string
433 char *
434 sysevent_get_class_name(sysevent_t *ev)
436 return (SE_CLASS_NAME(ev));
439 typedef enum {
440 PUB_VEND,
441 PUB_KEYWD,
442 PUB_NAME,
443 PUB_PID
444 } se_pub_id_t;
447 * sysevent_get_pub - Get publisher name string
449 char *
450 sysevent_get_pub(sysevent_t *ev)
452 return (SE_PUB_NAME(ev));
456 * Get the requested string pointed by the token.
458 * Return NULL if not found or for insufficient memory.
460 static char *
461 parse_pub_id(sysevent_t *ev, se_pub_id_t token)
463 int i;
464 char *pub_id, *pub_element, *str, *next;
466 next = pub_id = strdup(sysevent_get_pub(ev));
467 for (i = 0; i <= token; ++i) {
468 str = strtok_r(next, ":", &next);
469 if (str == NULL) {
470 free(pub_id);
471 return (NULL);
475 pub_element = strdup(str);
476 free(pub_id);
477 return (pub_element);
481 * Return a pointer to the string following the token
483 * Note: This is a dedicated function for parsing
484 * publisher strings and not for general purpose.
486 static const char *
487 pub_idx(const char *pstr, int token)
489 int i;
491 for (i = 1; i <= token; i++) {
492 if ((pstr = index(pstr, ':')) == NULL)
493 return (NULL);
494 pstr++;
497 /* String might be empty */
498 if (pstr) {
499 if (*pstr == '\0' || *pstr == ':')
500 return (NULL);
502 return (pstr);
505 char *
506 sysevent_get_vendor_name(sysevent_t *ev)
508 return (parse_pub_id(ev, PUB_VEND));
511 char *
512 sysevent_get_pub_name(sysevent_t *ev)
514 return (parse_pub_id(ev, PUB_NAME));
518 * Provide the pid encoded in the publisher string
519 * w/o allocating any resouces.
521 void
522 sysevent_get_pid(sysevent_t *ev, pid_t *pid)
524 const char *part_str;
525 const char *pub_str = sysevent_get_pub(ev);
527 *pid = (pid_t)SE_KERN_PID;
529 part_str = pub_idx(pub_str, PUB_KEYWD);
530 if (part_str != NULL && strstr(part_str, SE_KERN_PUB) != NULL)
531 return;
533 if ((part_str = pub_idx(pub_str, PUB_PID)) == NULL)
534 return;
536 *pid = (pid_t)atoi(part_str);
540 * sysevent_get_subclass_name - Get subclass name string
542 char *
543 sysevent_get_subclass_name(sysevent_t *ev)
545 return (SE_SUBCLASS_NAME(ev));
549 * sysevent_get_seq - Get event sequence id
551 uint64_t
552 sysevent_get_seq(sysevent_t *ev)
554 return (SE_SEQ(ev));
558 * sysevent_get_time - Get event timestamp
560 void
561 sysevent_get_time(sysevent_t *ev, hrtime_t *etime)
563 *etime = SE_TIME(ev);
567 * sysevent_get_size - Get event buffer size
569 size_t
570 sysevent_get_size(sysevent_t *ev)
572 return ((size_t)SE_SIZE(ev));
576 * The following routines are used by devfsadm_mod.c to propagate event
577 * buffers to devfsadmd. These routines will serve as the basis for
578 * event channel publication and subscription.
582 * sysevent_alloc_event -
583 * allocate a sysevent buffer for sending through an established event
584 * channel.
586 sysevent_t *
587 sysevent_alloc_event(char *class, char *subclass, char *vendor, char *pub_name,
588 nvlist_t *attr_list)
590 int class_sz, subclass_sz, pub_sz;
591 char *pub_id;
592 sysevent_t *ev;
594 if ((class == NULL) || (subclass == NULL) || (vendor == NULL) ||
595 (pub_name == NULL)) {
596 errno = EINVAL;
597 return (NULL);
600 class_sz = strlen(class) + 1;
601 subclass_sz = strlen(subclass) + 1;
602 if ((class_sz > MAX_CLASS_LEN) ||
603 (subclass_sz > MAX_SUBCLASS_LEN)) {
604 errno = EINVAL;
605 return (NULL);
609 * Calculate the publisher size plus string seperators and maximum
610 * pid characters
612 pub_sz = strlen(vendor) + sizeof (SE_USR_PUB) + strlen(pub_name) + 14;
613 if (pub_sz > MAX_PUB_LEN) {
614 errno = EINVAL;
615 return (NULL);
617 pub_id = malloc(pub_sz);
618 if (pub_id == NULL) {
619 errno = ENOMEM;
620 return (NULL);
622 if (snprintf(pub_id, pub_sz, "%s:%s%s:%d", vendor, SE_USR_PUB,
623 pub_name, (int)getpid()) >= pub_sz) {
624 free(pub_id);
625 errno = EINVAL;
626 return (NULL);
628 pub_sz = strlen(pub_id) + 1;
630 ev = sysevent_alloc(class, class_sz, subclass, subclass_sz,
631 pub_id, pub_sz, attr_list);
632 free(pub_id);
633 if (ev == NULL) {
634 errno = ENOMEM;
635 return (NULL);
638 return (ev);
642 * se_unpack - unpack nvlist to a searchable list.
643 * If already unpacked, will do a dup.
645 static sysevent_t *
646 se_unpack(sysevent_t *ev)
648 caddr_t attr;
649 size_t attr_len;
650 nvlist_t *attrp = NULL;
651 uint64_t attr_offset;
652 sysevent_t *copy;
654 assert(SE_FLAG(ev) == SE_PACKED_BUF);
656 /* Copy event header information */
657 attr_offset = SE_ATTR_OFF(ev);
658 copy = calloc(1, attr_offset);
659 if (copy == NULL)
660 return (NULL);
661 bcopy(ev, copy, attr_offset);
662 SE_FLAG(copy) = 0; /* unpacked */
664 /* unpack nvlist */
665 attr = (caddr_t)ev + attr_offset;
666 attr_len = SE_SIZE(ev) - attr_offset;
667 if (attr_len == 0) {
668 return (copy);
670 if (nvlist_unpack(attr, attr_len, &attrp, 0) != 0) {
671 free(copy);
672 return (NULL);
675 SE_ATTR_PTR(copy) = (uintptr_t)attrp;
676 return (copy);
680 * se_print - Prints elements in an event buffer
682 void
683 se_print(FILE *fp, sysevent_t *ev)
685 char *vendor, *pub;
686 pid_t pid;
687 hrtime_t hrt;
688 nvlist_t *attr_list = NULL;
690 (void) sysevent_get_time(ev, &hrt);
691 (void) fprintf(fp, "received sysevent id = 0X%llx:%llx\n",
692 hrt, (longlong_t)sysevent_get_seq(ev));
693 (void) fprintf(fp, "\tclass = %s\n", sysevent_get_class_name(ev));
694 (void) fprintf(fp, "\tsubclass = %s\n", sysevent_get_subclass_name(ev));
695 if ((vendor = sysevent_get_vendor_name(ev)) != NULL) {
696 (void) fprintf(fp, "\tvendor = %s\n", vendor);
697 free(vendor);
699 if ((pub = sysevent_get_pub_name(ev)) != NULL) {
700 sysevent_get_pid(ev, &pid);
701 (void) fprintf(fp, "\tpublisher = %s:%d\n", pub, (int)pid);
702 free(pub);
705 if (sysevent_get_attr_list(ev, &attr_list) == 0 && attr_list != NULL) {
706 nvlist_print(fp, attr_list);
707 nvlist_free(attr_list);
712 * The following routines are provided to support establishment and use
713 * of sysevent channels. A sysevent channel is established between
714 * publishers and subscribers of sysevents for an agreed upon channel name.
715 * These routines currently support sysevent channels between user-level
716 * applications running on the same system.
718 * Sysevent channels may be created by a single publisher or subscriber process.
719 * Once established, up to MAX_SUBSRCIBERS subscribers may subscribe interest in
720 * receiving sysevent notifications on the named channel. At present, only
721 * one publisher is allowed per sysevent channel.
723 * The registration information for each channel is kept in the kernel. A
724 * kernel-based registration was chosen for persistence and reliability reasons.
725 * If either a publisher or a subscriber exits for any reason, the channel
726 * properties are maintained until all publishers and subscribers have exited.
727 * Additionally, an in-kernel registration allows the API to be extended to
728 * include kernel subscribers as well as userland subscribers in the future.
730 * To insure fast lookup of subscriptions, a cached copy of the registration
731 * is kept and maintained for the publisher process. Updates are made
732 * everytime a change is made in the kernel. Changes to the registration are
733 * expected to be infrequent.
735 * Channel communication between publisher and subscriber processes is
736 * implemented primarily via doors. Each publisher creates a door for
737 * registration notifications and each subscriber creates a door for event
738 * delivery.
740 * Most of these routines are used by syseventd(1M), the sysevent publisher
741 * for the syseventd channel. Processes wishing to receive sysevent
742 * notifications from syseventd may use a set of public
743 * APIs designed to subscribe to syseventd sysevents. The subscription
744 * APIs are implemented in accordance with PSARC/2001/076.
749 * Door handlers for the channel subscribers
753 * subscriber_event_handler - generic event handling wrapper for subscribers
754 * This handler is used to process incoming sysevent
755 * notifications from channel publishers.
756 * It is created as a seperate thread in each subscriber
757 * process per subscription.
759 static void
760 subscriber_event_handler(sysevent_handle_t *shp)
762 subscriber_priv_t *sub_info;
763 sysevent_queue_t *evqp;
765 sub_info = (subscriber_priv_t *)SH_PRIV_DATA(shp);
767 /* See hack alert in sysevent_bind_subscriber_cmn */
768 if (sub_info->sp_handler_tid == NULL)
769 sub_info->sp_handler_tid = thr_self();
771 (void) mutex_lock(&sub_info->sp_qlock);
772 for (;;) {
773 while (sub_info->sp_evq_head == NULL && SH_BOUND(shp)) {
774 (void) cond_wait(&sub_info->sp_cv, &sub_info->sp_qlock);
776 evqp = sub_info->sp_evq_head;
777 while (evqp) {
778 (void) mutex_unlock(&sub_info->sp_qlock);
779 (void) sub_info->sp_func(evqp->sq_ev);
780 (void) mutex_lock(&sub_info->sp_qlock);
781 sub_info->sp_evq_head = sub_info->sp_evq_head->sq_next;
782 free(evqp->sq_ev);
783 free(evqp);
784 evqp = sub_info->sp_evq_head;
786 if (!SH_BOUND(shp)) {
787 (void) mutex_unlock(&sub_info->sp_qlock);
788 return;
792 /* NOTREACHED */
796 * Data structure used to communicate event subscription cache updates
797 * to publishers via a registration door
799 struct reg_args {
800 uint32_t ra_sub_id;
801 uint32_t ra_op;
802 uint64_t ra_buf_ptr;
807 * event_deliver_service - generic event delivery service routine. This routine
808 * is called in response to a door call to post an event.
811 /*ARGSUSED*/
812 static void
813 event_deliver_service(void *cookie, char *args, size_t alen,
814 door_desc_t *ddp, uint_t ndid)
816 int ret = 0;
817 subscriber_priv_t *sub_info;
818 sysevent_handle_t *shp;
819 sysevent_queue_t *new_eq;
821 if (args == NULL || alen < sizeof (uint32_t)) {
822 ret = EINVAL;
823 goto return_from_door;
826 /* Publisher checking on subscriber */
827 if (alen == sizeof (uint32_t)) {
828 ret = 0;
829 goto return_from_door;
832 shp = (sysevent_handle_t *)cookie;
833 if (shp == NULL) {
834 ret = EBADF;
835 goto return_from_door;
839 * Mustn't block if we are trying to update the registration with
840 * the publisher
842 if (mutex_trylock(SH_LOCK(shp)) != 0) {
843 ret = EAGAIN;
844 goto return_from_door;
847 if (!SH_BOUND(shp)) {
848 ret = EBADF;
849 (void) mutex_unlock(SH_LOCK(shp));
850 goto return_from_door;
853 sub_info = (subscriber_priv_t *)SH_PRIV_DATA(shp);
854 if (sub_info == NULL) {
855 ret = EBADF;
856 (void) mutex_unlock(SH_LOCK(shp));
857 goto return_from_door;
860 new_eq = (sysevent_queue_t *)calloc(1,
861 sizeof (sysevent_queue_t));
862 if (new_eq == NULL) {
863 ret = EAGAIN;
864 (void) mutex_unlock(SH_LOCK(shp));
865 goto return_from_door;
869 * Allocate and copy the event buffer into the subscriber's
870 * address space
872 new_eq->sq_ev = calloc(1, alen);
873 if (new_eq->sq_ev == NULL) {
874 free(new_eq);
875 ret = EAGAIN;
876 (void) mutex_unlock(SH_LOCK(shp));
877 goto return_from_door;
879 (void) bcopy(args, new_eq->sq_ev, alen);
881 (void) mutex_lock(&sub_info->sp_qlock);
882 if (sub_info->sp_evq_head == NULL) {
883 sub_info->sp_evq_head = new_eq;
884 } else {
885 sub_info->sp_evq_tail->sq_next = new_eq;
887 sub_info->sp_evq_tail = new_eq;
889 (void) cond_signal(&sub_info->sp_cv);
890 (void) mutex_unlock(&sub_info->sp_qlock);
891 (void) mutex_unlock(SH_LOCK(shp));
893 return_from_door:
894 (void) door_return((void *)&ret, sizeof (ret), NULL, 0);
895 (void) door_return(NULL, 0, NULL, 0);
899 * Sysevent subscription information is maintained in the kernel. Updates
900 * to the in-kernel registration database is expected to be infrequent and
901 * offers consistency for publishers and subscribers that may come and go
902 * for a given channel.
904 * To expedite registration lookups by publishers, a cached copy of the
905 * kernel registration database is kept per-channel. Caches are invalidated
906 * and refreshed upon state changes to the in-kernel registration database.
908 * To prevent stale subscriber data, publishers may remove subsriber
909 * registrations from the in-kernel registration database in the event
910 * that a particular subscribing process is unresponsive.
912 * The following routines provide a mechanism to update publisher and subscriber
913 * information for a specified channel.
917 * clnt_deliver_event - Deliver an event through the consumer's event
918 * delivery door
920 * Returns -1 if message not delivered. With errno set to cause of error.
921 * Returns 0 for success with the results returned in posting buffer.
923 static int
924 clnt_deliver_event(int service_door, void *data, size_t datalen,
925 void *result, size_t rlen)
927 int error = 0;
928 door_arg_t door_arg;
930 door_arg.rbuf = result;
931 door_arg.rsize = rlen;
932 door_arg.data_ptr = data;
933 door_arg.data_size = datalen;
934 door_arg.desc_ptr = NULL;
935 door_arg.desc_num = 0;
938 * Make door call
940 while ((error = door_call(service_door, &door_arg)) != 0) {
941 if (errno == EAGAIN || errno == EINTR) {
942 continue;
943 } else {
944 error = errno;
945 break;
949 return (error);
952 static int
953 update_publisher_cache(subscriber_priv_t *sub_info, int update_op,
954 uint32_t sub_id, size_t datasz, uchar_t *data)
956 int pub_fd;
957 uint32_t result = 0;
958 struct reg_args *rargs;
960 rargs = (struct reg_args *)calloc(1, sizeof (struct reg_args) +
961 datasz);
962 if (rargs == NULL) {
963 errno = ENOMEM;
964 return (-1);
967 rargs->ra_sub_id = sub_id;
968 rargs->ra_op = update_op;
969 bcopy(data, (char *)&rargs->ra_buf_ptr, datasz);
971 pub_fd = open(sub_info->sp_door_name, O_RDONLY);
972 (void) clnt_deliver_event(pub_fd, (void *)rargs,
973 sizeof (struct reg_args) + datasz, &result, sizeof (result));
974 (void) close(pub_fd);
976 free(rargs);
977 if (result != 0) {
978 errno = result;
979 return (-1);
982 return (0);
987 * update_kernel_registration - update the in-kernel registration for the
988 * given channel.
990 static int
991 update_kernel_registration(sysevent_handle_t *shp, int update_type,
992 int update_op, uint32_t *sub_id, size_t datasz, uchar_t *data)
994 int error;
995 char *channel_name = SH_CHANNEL_NAME(shp);
996 se_pubsub_t udata;
998 udata.ps_channel_name_len = strlen(channel_name) + 1;
999 udata.ps_op = update_op;
1000 udata.ps_type = update_type;
1001 udata.ps_buflen = datasz;
1002 udata.ps_id = *sub_id;
1004 if ((error = modctl(MODEVENTS, (uintptr_t)MODEVENTS_REGISTER_EVENT,
1005 (uintptr_t)channel_name, (uintptr_t)data, (uintptr_t)&udata, 0))
1006 != 0) {
1007 return (error);
1010 *sub_id = udata.ps_id;
1012 return (error);
1016 * get_kernel_registration - get the current subscriber registration for
1017 * the given channel
1019 static nvlist_t *
1020 get_kernel_registration(char *channel_name, uint32_t class_id)
1022 char *nvlbuf;
1023 nvlist_t *nvl;
1024 se_pubsub_t udata;
1026 nvlbuf = calloc(1, MAX_SUBSCRIPTION_SZ);
1027 if (nvlbuf == NULL) {
1028 return (NULL);
1031 udata.ps_buflen = MAX_SUBSCRIPTION_SZ;
1032 udata.ps_channel_name_len = strlen(channel_name) + 1;
1033 udata.ps_id = class_id;
1034 udata.ps_op = SE_GET_REGISTRATION;
1035 udata.ps_type = PUBLISHER;
1037 if (modctl(MODEVENTS, (uintptr_t)MODEVENTS_REGISTER_EVENT,
1038 (uintptr_t)channel_name, (uintptr_t)nvlbuf, (uintptr_t)&udata, 0)
1039 != 0) {
1041 /* Need a bigger buffer to hold channel registration */
1042 if (errno == EAGAIN) {
1043 free(nvlbuf);
1044 nvlbuf = calloc(1, udata.ps_buflen);
1045 if (nvlbuf == NULL)
1046 return (NULL);
1048 /* Try again */
1049 if (modctl(MODEVENTS,
1050 (uintptr_t)MODEVENTS_REGISTER_EVENT,
1051 (uintptr_t)channel_name, (uintptr_t)nvlbuf,
1052 (uintptr_t)&udata, 0) != 0) {
1053 free(nvlbuf);
1054 return (NULL);
1056 } else {
1057 free(nvlbuf);
1058 return (NULL);
1062 if (nvlist_unpack(nvlbuf, udata.ps_buflen, &nvl, 0) != 0) {
1063 free(nvlbuf);
1064 return (NULL);
1066 free(nvlbuf);
1068 return (nvl);
1072 * The following routines provide a mechanism for publishers to maintain
1073 * subscriber information.
1076 static void
1077 dealloc_subscribers(sysevent_handle_t *shp)
1079 int i;
1080 subscriber_data_t *sub;
1082 for (i = 1; i <= MAX_SUBSCRIBERS; ++i) {
1083 sub = SH_SUBSCRIBER(shp, i);
1084 if (sub != NULL) {
1085 free(sub->sd_door_name);
1086 free(sub);
1088 SH_SUBSCRIBER(shp, i) = NULL;
1092 /*ARGSUSED*/
1093 static int
1094 alloc_subscriber(sysevent_handle_t *shp, uint32_t sub_id, int oflag)
1096 subscriber_data_t *sub;
1097 char door_name[MAXPATHLEN];
1099 if (SH_SUBSCRIBER(shp, sub_id) != NULL) {
1100 return (0);
1103 /* Allocate and initialize the subscriber data */
1104 sub = (subscriber_data_t *)calloc(1,
1105 sizeof (subscriber_data_t));
1106 if (sub == NULL) {
1107 return (-1);
1109 if (snprintf(door_name, MAXPATHLEN, "%s/%d",
1110 SH_CHANNEL_PATH(shp), sub_id) >= MAXPATHLEN) {
1111 free(sub);
1112 return (-1);
1115 sub->sd_flag = ACTIVE;
1116 sub->sd_door_name = strdup(door_name);
1117 if (sub->sd_door_name == NULL) {
1118 free(sub);
1119 return (-1);
1122 SH_SUBSCRIBER(shp, sub_id) = sub;
1123 return (0);
1128 * The following routines are used to update and maintain the registration cache
1129 * for a particular sysevent channel.
1132 static uint32_t
1133 hash_func(const char *s)
1135 uint32_t result = 0;
1136 uint_t g;
1138 while (*s != '\0') {
1139 result <<= 4;
1140 result += (uint32_t)*s++;
1141 g = result & 0xf0000000;
1142 if (g != 0) {
1143 result ^= g >> 24;
1144 result ^= g;
1148 return (result);
1151 subclass_lst_t *
1152 cache_find_subclass(class_lst_t *c_list, char *subclass)
1154 subclass_lst_t *sc_list;
1156 if (c_list == NULL)
1157 return (NULL);
1159 sc_list = c_list->cl_subclass_list;
1161 while (sc_list != NULL) {
1162 if (strcmp(sc_list->sl_name, subclass) == 0) {
1163 return (sc_list);
1165 sc_list = sc_list->sl_next;
1168 return (NULL);
1172 static class_lst_t *
1173 cache_find_class(sysevent_handle_t *shp, char *class)
1175 int index;
1176 class_lst_t *c_list;
1177 class_lst_t **class_hash = SH_CLASS_HASH(shp);
1179 if (strcmp(class, EC_ALL) == 0) {
1180 return (class_hash[0]);
1183 index = CLASS_HASH(class);
1184 c_list = class_hash[index];
1185 while (c_list != NULL) {
1186 if (strcmp(class, c_list->cl_name) == 0) {
1187 break;
1189 c_list = c_list->cl_next;
1192 return (c_list);
1195 static int
1196 cache_insert_subclass(class_lst_t *c_list, char **subclass_names,
1197 int subclass_num, uint32_t sub_id)
1199 int i;
1200 subclass_lst_t *sc_list;
1202 for (i = 0; i < subclass_num; ++i) {
1203 if ((sc_list = cache_find_subclass(c_list, subclass_names[i]))
1204 != NULL) {
1205 sc_list->sl_num[sub_id] = 1;
1206 } else {
1207 sc_list = (subclass_lst_t *)calloc(1,
1208 sizeof (subclass_lst_t));
1209 if (sc_list == NULL)
1210 return (-1);
1212 sc_list->sl_name = strdup(subclass_names[i]);
1213 if (sc_list->sl_name == NULL) {
1214 free(sc_list);
1215 return (-1);
1218 sc_list->sl_num[sub_id] = 1;
1219 sc_list->sl_next = c_list->cl_subclass_list;
1220 c_list->cl_subclass_list = sc_list;
1224 return (0);
1227 static int
1228 cache_insert_class(sysevent_handle_t *shp, char *class,
1229 char **subclass_names, int subclass_num, uint32_t sub_id)
1231 class_lst_t *c_list;
1233 if (strcmp(class, EC_ALL) == 0) {
1234 char *subclass_all = EC_SUB_ALL;
1236 (void) cache_insert_subclass(SH_CLASS_HASH(shp)[0],
1237 (char **)&subclass_all, 1, sub_id);
1238 return (0);
1241 /* New class, add to the registration cache */
1242 if ((c_list = cache_find_class(shp, class)) == NULL) {
1244 c_list = (class_lst_t *)calloc(1, sizeof (class_lst_t));
1245 if (c_list == NULL) {
1246 return (1);
1248 c_list->cl_name = strdup(class);
1249 if (c_list->cl_name == NULL) {
1250 free(c_list);
1251 return (1);
1254 c_list->cl_subclass_list = (subclass_lst_t *)
1255 calloc(1, sizeof (subclass_lst_t));
1256 if (c_list->cl_subclass_list == NULL) {
1257 free(c_list->cl_name);
1258 free(c_list);
1259 return (1);
1261 c_list->cl_subclass_list->sl_name = strdup(EC_SUB_ALL);
1262 if (c_list->cl_subclass_list->sl_name == NULL) {
1263 free(c_list->cl_subclass_list);
1264 free(c_list->cl_name);
1265 free(c_list);
1266 return (1);
1268 c_list->cl_next = SH_CLASS_HASH(shp)[CLASS_HASH(class)];
1269 SH_CLASS_HASH(shp)[CLASS_HASH(class)] = c_list;
1273 /* Update the subclass list */
1274 if (cache_insert_subclass(c_list, subclass_names, subclass_num,
1275 sub_id) != 0)
1276 return (1);
1278 return (0);
1281 static void
1282 cache_remove_all_class(sysevent_handle_t *shp, uint32_t sub_id)
1284 int i;
1285 class_lst_t *c_list;
1286 subclass_lst_t *sc_list;
1288 for (i = 0; i < CLASS_HASH_SZ + 1; ++i) {
1289 c_list = SH_CLASS_HASH(shp)[i];
1290 while (c_list != NULL) {
1291 sc_list = c_list->cl_subclass_list;
1292 while (sc_list != NULL) {
1293 sc_list->sl_num[sub_id] = 0;
1294 sc_list = sc_list->sl_next;
1296 c_list = c_list->cl_next;
1301 static void
1302 cache_remove_class(sysevent_handle_t *shp, char *class, uint32_t sub_id)
1304 class_lst_t *c_list;
1305 subclass_lst_t *sc_list;
1307 if (strcmp(class, EC_ALL) == 0) {
1308 cache_remove_all_class(shp, sub_id);
1309 return;
1312 if ((c_list = cache_find_class(shp, class)) == NULL) {
1313 return;
1316 sc_list = c_list->cl_subclass_list;
1317 while (sc_list != NULL) {
1318 sc_list->sl_num[sub_id] = 0;
1319 sc_list = sc_list->sl_next;
1323 static void
1324 free_cached_registration(sysevent_handle_t *shp)
1326 int i;
1327 class_lst_t *clist, *next_clist;
1328 subclass_lst_t *sc_list, *next_sc;
1330 for (i = 0; i < CLASS_HASH_SZ + 1; i++) {
1331 clist = SH_CLASS_HASH(shp)[i];
1332 while (clist != NULL) {
1333 sc_list = clist->cl_subclass_list;
1334 while (sc_list != NULL) {
1335 free(sc_list->sl_name);
1336 next_sc = sc_list->sl_next;
1337 free(sc_list);
1338 sc_list = next_sc;
1340 free(clist->cl_name);
1341 next_clist = clist->cl_next;
1342 free(clist);
1343 clist = next_clist;
1345 SH_CLASS_HASH(shp)[i] = NULL;
1349 static int
1350 create_cached_registration(sysevent_handle_t *shp,
1351 class_lst_t **class_hash)
1353 int i, j, new_class;
1354 char *class_name;
1355 uint_t num_elem;
1356 uchar_t *subscribers;
1357 nvlist_t *nvl;
1358 nvpair_t *nvpair;
1359 class_lst_t *clist;
1360 subclass_lst_t *sc_list;
1362 for (i = 0; i < CLASS_HASH_SZ + 1; ++i) {
1364 if ((nvl = get_kernel_registration(SH_CHANNEL_NAME(shp), i))
1365 == NULL) {
1366 if (errno == ENOENT) {
1367 class_hash[i] = NULL;
1368 continue;
1369 } else {
1370 goto create_failed;
1375 nvpair = NULL;
1376 if ((nvpair = nvlist_next_nvpair(nvl, nvpair)) == NULL) {
1377 goto create_failed;
1380 new_class = 1;
1381 while (new_class) {
1382 /* Extract the class name from the nvpair */
1383 if (nvpair_value_string(nvpair, &class_name) != 0) {
1384 goto create_failed;
1386 clist = (class_lst_t *)
1387 calloc(1, sizeof (class_lst_t));
1388 if (clist == NULL) {
1389 goto create_failed;
1392 clist->cl_name = strdup(class_name);
1393 if (clist->cl_name == NULL) {
1394 free(clist);
1395 goto create_failed;
1399 * Extract the subclass name and registration
1400 * from the nvpair
1402 if ((nvpair = nvlist_next_nvpair(nvl, nvpair))
1403 == NULL) {
1404 free(clist->cl_name);
1405 free(clist);
1406 goto create_failed;
1409 clist->cl_next = class_hash[i];
1410 class_hash[i] = clist;
1412 for (;;) {
1414 sc_list = (subclass_lst_t *)calloc(1,
1415 sizeof (subclass_lst_t));
1416 if (sc_list == NULL) {
1417 goto create_failed;
1420 sc_list->sl_next = clist->cl_subclass_list;
1421 clist->cl_subclass_list = sc_list;
1423 sc_list->sl_name = strdup(nvpair_name(nvpair));
1424 if (sc_list->sl_name == NULL) {
1425 goto create_failed;
1428 if (nvpair_value_byte_array(nvpair,
1429 &subscribers, &num_elem) != 0) {
1430 goto create_failed;
1432 bcopy(subscribers, (uchar_t *)sc_list->sl_num,
1433 MAX_SUBSCRIBERS + 1);
1435 for (j = 1; j <= MAX_SUBSCRIBERS; ++j) {
1436 if (sc_list->sl_num[j] == 0)
1437 continue;
1439 if (alloc_subscriber(shp, j, 1) != 0) {
1440 goto create_failed;
1445 * Check next nvpair - either subclass or
1446 * class
1448 if ((nvpair = nvlist_next_nvpair(nvl, nvpair))
1449 == NULL) {
1450 new_class = 0;
1451 break;
1452 } else if (strcmp(nvpair_name(nvpair),
1453 CLASS_NAME) == 0) {
1454 break;
1458 nvlist_free(nvl);
1460 return (0);
1462 create_failed:
1463 dealloc_subscribers(shp);
1464 free_cached_registration(shp);
1465 if (nvl)
1466 nvlist_free(nvl);
1467 return (-1);
1472 * cache_update_service - generic event publisher service routine. This routine
1473 * is called in response to a registration cache update.
1476 /*ARGSUSED*/
1477 static void
1478 cache_update_service(void *cookie, char *args, size_t alen,
1479 door_desc_t *ddp, uint_t ndid)
1481 int ret = 0;
1482 uint_t num_elem;
1483 char *class, **event_list;
1484 size_t datalen;
1485 uint32_t sub_id;
1486 nvlist_t *nvl;
1487 nvpair_t *nvpair = NULL;
1488 struct reg_args *rargs;
1489 sysevent_handle_t *shp;
1490 subscriber_data_t *sub;
1492 if (alen < sizeof (struct reg_args) || cookie == NULL) {
1493 ret = EINVAL;
1494 goto return_from_door;
1497 /* LINTED: E_BAD_PTR_CAST_ALIGN */
1498 rargs = (struct reg_args *)args;
1499 shp = (sysevent_handle_t *)cookie;
1501 datalen = alen - sizeof (struct reg_args);
1502 sub_id = rargs->ra_sub_id;
1504 (void) mutex_lock(SH_LOCK(shp));
1506 switch (rargs->ra_op) {
1507 case SE_UNREGISTER:
1508 class = (char *)&rargs->ra_buf_ptr;
1509 cache_remove_class(shp, (char *)class,
1510 sub_id);
1511 break;
1512 case SE_UNBIND_REGISTRATION:
1514 sub = SH_SUBSCRIBER(shp, sub_id);
1515 if (sub == NULL)
1516 break;
1518 free(sub->sd_door_name);
1519 free(sub);
1520 cache_remove_class(shp, EC_ALL, sub_id);
1521 SH_SUBSCRIBER(shp, sub_id) = NULL;
1523 break;
1524 case SE_BIND_REGISTRATION:
1526 /* New subscriber */
1527 if (alloc_subscriber(shp, sub_id, 0) != 0) {
1528 ret = ENOMEM;
1529 break;
1531 break;
1532 case SE_REGISTER:
1534 if (SH_SUBSCRIBER(shp, sub_id) == NULL) {
1535 ret = EINVAL;
1536 break;
1538 /* Get new registration data */
1539 if (nvlist_unpack((char *)&rargs->ra_buf_ptr, datalen,
1540 &nvl, 0) != 0) {
1541 ret = EFAULT;
1542 break;
1544 if ((nvpair = nvlist_next_nvpair(nvl, nvpair)) == NULL) {
1545 nvlist_free(nvl);
1546 ret = EFAULT;
1547 break;
1549 if (nvpair_value_string_array(nvpair, &event_list, &num_elem)
1550 != 0) {
1551 nvlist_free(nvl);
1552 ret = EFAULT;
1553 break;
1555 class = nvpair_name(nvpair);
1557 ret = cache_insert_class(shp, class,
1558 event_list, num_elem, sub_id);
1559 if (ret != 0) {
1560 cache_remove_class(shp, class, sub_id);
1561 nvlist_free(nvl);
1562 ret = EFAULT;
1563 break;
1566 nvlist_free(nvl);
1568 break;
1569 case SE_CLEANUP:
1570 /* Cleanup stale subscribers */
1571 sysevent_cleanup_subscribers(shp);
1572 break;
1573 default:
1574 ret = EINVAL;
1577 (void) mutex_unlock(SH_LOCK(shp));
1579 return_from_door:
1580 (void) door_return((void *)&ret, sizeof (ret), NULL, 0);
1581 (void) door_return(NULL, 0, NULL, 0);
1585 * sysevent_send_event -
1586 * Send an event via the communication channel associated with the sysevent
1587 * handle. Event notifications are broadcast to all subscribers based upon
1588 * the event class and subclass. The handle must have been previously
1589 * allocated and bound by
1590 * sysevent_open_channel() and sysevent_bind_publisher()
1593 sysevent_send_event(sysevent_handle_t *shp, sysevent_t *ev)
1595 int i, error, sub_fd, result = 0;
1596 int deliver_error = 0;
1597 int subscribers_sent = 0;
1598 int want_resend, resend_cnt = 0;
1599 char *event_class, *event_subclass;
1600 uchar_t *all_class_subscribers, *all_subclass_subscribers;
1601 uchar_t *subclass_subscribers;
1602 subscriber_data_t *sub;
1603 subclass_lst_t *sc_lst;
1605 /* Check for proper registration */
1606 event_class = sysevent_get_class_name(ev);
1607 event_subclass = sysevent_get_subclass_name(ev);
1609 (void) mutex_lock(SH_LOCK(shp));
1611 send_event:
1613 want_resend = 0;
1614 if (!SH_BOUND(shp)) {
1615 (void) mutex_unlock(SH_LOCK(shp));
1616 errno = EINVAL;
1617 return (-1);
1620 /* Find all subscribers for this event class/subclass */
1621 sc_lst = cache_find_subclass(
1622 cache_find_class(shp, EC_ALL), EC_SUB_ALL);
1623 all_class_subscribers = sc_lst->sl_num;
1625 sc_lst = cache_find_subclass(
1626 cache_find_class(shp, event_class), EC_SUB_ALL);
1627 if (sc_lst)
1628 all_subclass_subscribers = sc_lst->sl_num;
1629 else
1630 all_subclass_subscribers = NULL;
1632 sc_lst = cache_find_subclass(
1633 cache_find_class(shp, event_class), event_subclass);
1634 if (sc_lst)
1635 subclass_subscribers = sc_lst->sl_num;
1636 else
1637 subclass_subscribers = NULL;
1639 /* Send event buffer to all valid subscribers */
1640 for (i = 1; i <= MAX_SUBSCRIBERS; ++i) {
1641 if ((all_class_subscribers[i] |
1642 (all_subclass_subscribers && all_subclass_subscribers[i]) |
1643 (subclass_subscribers && subclass_subscribers[i])) == 0)
1644 continue;
1646 sub = SH_SUBSCRIBER(shp, i);
1647 assert(sub != NULL);
1649 /* Check for active subscriber */
1650 if (!(sub->sd_flag & ACTIVE)) {
1651 dprint("sysevent_send_event: subscriber %d inactive\n",
1653 continue;
1656 /* Process only resend requests */
1657 if (resend_cnt > 0 && !(sub->sd_flag & SEND_AGAIN)) {
1658 continue;
1661 if ((sub_fd = open(sub->sd_door_name, O_RDONLY)) == -1) {
1662 dprint("sysevent_send_event: Failed to open "
1663 "%s: %s\n", sub->sd_door_name, strerror(errno));
1664 continue;
1666 result = 0;
1667 error = clnt_deliver_event(sub_fd, ev,
1668 sysevent_get_size(ev), &result, sizeof (result));
1670 (void) close(sub_fd);
1672 /* Successful door call */
1673 if (error == 0) {
1674 switch (result) {
1675 /* Subscriber requested EAGAIN */
1676 case EAGAIN:
1677 if (resend_cnt > SE_MAX_RETRY_LIMIT) {
1678 deliver_error = 1;
1679 } else {
1680 want_resend = 1;
1681 dprint("sysevent_send_event: resend "
1682 "requested for %d\n", i);
1683 sub->sd_flag |= SEND_AGAIN;
1685 break;
1686 /* Bad sysevent handle for subscriber */
1687 case EBADF:
1688 case EINVAL:
1689 dprint("sysevent_send_event: Bad sysevent "
1690 "handle for %s", sub->sd_door_name);
1691 sub->sd_flag = 0;
1692 deliver_error = 1;
1693 break;
1694 /* Successful delivery */
1695 default:
1696 sub->sd_flag &= ~SEND_AGAIN;
1697 ++subscribers_sent;
1699 } else {
1700 dprint("sysevent_send_event: Failed door call "
1701 "to %s: %s: %d\n", sub->sd_door_name,
1702 strerror(errno), result);
1703 sub->sd_flag = 0;
1704 deliver_error = 1;
1708 if (want_resend) {
1709 resend_cnt++;
1710 goto send_event;
1713 if (deliver_error) {
1714 sysevent_cleanup_subscribers(shp);
1715 (void) mutex_unlock(SH_LOCK(shp));
1716 errno = EFAULT;
1717 return (-1);
1720 (void) mutex_unlock(SH_LOCK(shp));
1722 if (subscribers_sent == 0) {
1723 dprint("sysevent_send_event: No subscribers for %s:%s\n",
1724 event_class, event_subclass);
1725 errno = ENOENT;
1726 return (-1);
1729 return (0);
1733 * Common routine to establish an event channel through which an event
1734 * publisher or subscriber may post or receive events.
1736 static sysevent_handle_t *
1737 sysevent_open_channel_common(const char *channel_path)
1739 uint32_t sub_id = 0;
1740 char *begin_path;
1741 struct stat chan_stat;
1742 sysevent_handle_t *shp;
1745 if (channel_path == NULL || strlen(channel_path) + 1 > MAXPATHLEN) {
1746 errno = EINVAL;
1747 return (NULL);
1750 if (mkdir(channel_path, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH) < 0) {
1751 if (errno != EEXIST) {
1752 errno = EACCES;
1753 return (NULL);
1757 /* Check channel file permissions */
1758 if (stat(channel_path, &chan_stat) != 0) {
1759 dprint("sysevent_open_channel: Invalid permissions for channel "
1760 "%s\n", channel_path);
1761 errno = EACCES;
1762 return (NULL);
1763 } else if (chan_stat.st_uid != getuid() ||
1764 !S_ISDIR(chan_stat.st_mode)) {
1765 dprint("sysevent_open_channel: Invalid "
1766 "permissions for channel %s\n: %d:%d:%d", channel_path,
1767 (int)chan_stat.st_uid, (int)chan_stat.st_gid,
1768 (int)chan_stat.st_mode);
1770 errno = EACCES;
1771 return (NULL);
1774 shp = calloc(1, sizeof (sysevent_impl_hdl_t));
1775 if (shp == NULL) {
1776 errno = ENOMEM;
1777 return (NULL);
1780 SH_CHANNEL_NAME(shp) = NULL;
1781 SH_CHANNEL_PATH(shp) = strdup(channel_path);
1782 if (SH_CHANNEL_PATH(shp) == NULL) {
1783 free(shp);
1784 errno = ENOMEM;
1785 return (NULL);
1788 /* Extract the channel name */
1789 begin_path = SH_CHANNEL_PATH(shp);
1790 while (*begin_path != '\0' &&
1791 (begin_path = strpbrk(begin_path, "/")) != NULL) {
1792 ++begin_path;
1793 SH_CHANNEL_NAME(shp) = begin_path;
1796 if (update_kernel_registration(shp, 0,
1797 SE_OPEN_REGISTRATION, &sub_id, 0, NULL) != 0) {
1798 dprint("sysevent_open_channel: Failed for channel %s\n",
1799 SH_CHANNEL_NAME(shp));
1800 free(SH_CHANNEL_PATH(shp));
1801 free(shp);
1802 errno = EFAULT;
1803 return (NULL);
1806 (void) mutex_init(SH_LOCK(shp), USYNC_THREAD, NULL);
1808 return (shp);
1812 * Establish a sysevent channel for publication and subscription
1814 sysevent_handle_t *
1815 sysevent_open_channel(const char *channel)
1817 int var_run_mounted = 0;
1818 char full_channel[MAXPATHLEN + 1];
1819 FILE *fp;
1820 struct stat chan_stat;
1821 struct extmnttab m;
1823 if (channel == NULL) {
1824 errno = EINVAL;
1825 return (NULL);
1829 * Check that /var/run is mounted as tmpfs before allowing a channel
1830 * to be opened.
1832 if ((fp = fopen(MNTTAB, "rF")) == NULL) {
1833 errno = EACCES;
1834 return (NULL);
1837 resetmnttab(fp);
1839 while (getextmntent(fp, &m, sizeof (struct extmnttab)) == 0) {
1840 if (strcmp(m.mnt_mountp, "/var/run") == 0 &&
1841 strcmp(m.mnt_fstype, "tmpfs") == 0) {
1842 var_run_mounted = 1;
1843 break;
1846 (void) fclose(fp);
1848 if (!var_run_mounted) {
1849 errno = EACCES;
1850 return (NULL);
1853 if (stat(CHAN_PATH, &chan_stat) < 0) {
1854 if (mkdir(CHAN_PATH,
1855 S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH) < 0) {
1856 dprint("sysevent_open_channel: Unable "
1857 "to create channel directory %s:%s\n", CHAN_PATH,
1858 strerror(errno));
1859 if (errno != EEXIST) {
1860 errno = EACCES;
1861 return (NULL);
1866 if (snprintf(full_channel, MAXPATHLEN, "%s/%s", CHAN_PATH, channel) >=
1867 MAXPATHLEN) {
1868 errno = EINVAL;
1869 return (NULL);
1872 return (sysevent_open_channel_common(full_channel));
1876 * Establish a sysevent channel for publication and subscription
1877 * Full path to the channel determined by the caller
1879 sysevent_handle_t *
1880 sysevent_open_channel_alt(const char *channel_path)
1882 return (sysevent_open_channel_common(channel_path));
1886 * sysevent_close_channel - Clean up resources associated with a previously
1887 * opened sysevent channel
1889 void
1890 sysevent_close_channel(sysevent_handle_t *shp)
1892 int error = errno;
1893 uint32_t sub_id = 0;
1895 if (shp == NULL) {
1896 return;
1899 (void) mutex_lock(SH_LOCK(shp));
1900 if (SH_BOUND(shp)) {
1901 (void) mutex_unlock(SH_LOCK(shp));
1902 if (SH_TYPE(shp) == PUBLISHER)
1903 sysevent_unbind_publisher(shp);
1904 else if (SH_TYPE(shp) == SUBSCRIBER)
1905 sysevent_unbind_subscriber(shp);
1906 (void) mutex_lock(SH_LOCK(shp));
1909 (void) update_kernel_registration(shp, 0,
1910 SE_CLOSE_REGISTRATION, &sub_id, 0, NULL);
1911 (void) mutex_unlock(SH_LOCK(shp));
1913 free(SH_CHANNEL_PATH(shp));
1914 free(shp);
1915 errno = error;
1919 * sysevent_bind_publisher - Bind an event publisher to an event channel
1922 sysevent_bind_publisher(sysevent_handle_t *shp)
1924 int error = 0;
1925 int fd = -1;
1926 char door_name[MAXPATHLEN];
1927 uint32_t pub_id;
1928 struct stat reg_stat;
1929 publisher_priv_t *pub;
1931 if (shp == NULL) {
1932 errno = EINVAL;
1933 return (-1);
1936 (void) mutex_lock(SH_LOCK(shp));
1937 if (SH_BOUND(shp)) {
1938 (void) mutex_unlock(SH_LOCK(shp));
1939 errno = EINVAL;
1940 return (-1);
1943 if ((pub = (publisher_priv_t *)calloc(1, sizeof (publisher_priv_t))) ==
1944 NULL) {
1945 (void) mutex_unlock(SH_LOCK(shp));
1946 errno = ENOMEM;
1947 return (-1);
1949 SH_PRIV_DATA(shp) = (void *)pub;
1951 if (snprintf(door_name, MAXPATHLEN, "%s/%s",
1952 SH_CHANNEL_PATH(shp), REG_DOOR) >= MAXPATHLEN) {
1953 free(pub);
1954 (void) mutex_unlock(SH_LOCK(shp));
1955 errno = ENOMEM;
1956 return (-1);
1958 if ((SH_DOOR_NAME(shp) = strdup(door_name)) == NULL) {
1959 free(pub);
1960 (void) mutex_unlock(SH_LOCK(shp));
1961 errno = ENOMEM;
1962 return (-1);
1965 /* Only one publisher allowed per channel */
1966 if (stat(SH_DOOR_NAME(shp), &reg_stat) != 0) {
1967 if (errno != ENOENT) {
1968 error = EINVAL;
1969 goto fail;
1974 * Remove door file for robustness.
1976 if (unlink(SH_DOOR_NAME(shp)) != 0)
1977 dprint("sysevent_bind_publisher: Unlink of %s failed.\n",
1978 SH_DOOR_NAME(shp));
1980 /* Open channel registration door */
1981 fd = open(SH_DOOR_NAME(shp), O_CREAT|O_RDWR,
1982 S_IREAD|S_IWRITE);
1983 if (fd == -1) {
1984 error = EINVAL;
1985 goto fail;
1989 * Create the registration service for this publisher.
1991 if ((SH_DOOR_DESC(shp) = door_create(cache_update_service,
1992 (void *)shp, DOOR_REFUSE_DESC | DOOR_NO_CANCEL)) == -1) {
1993 dprint("sysevent_bind_publisher: door create failed: "
1994 "%s\n", strerror(errno));
1995 error = EFAULT;
1996 goto fail;
1999 (void) fdetach(SH_DOOR_NAME(shp));
2000 if (fattach(SH_DOOR_DESC(shp), SH_DOOR_NAME(shp)) != 0) {
2001 dprint("sysevent_bind_publisher: unable to "
2002 "bind event channel: fattach: %s\n",
2003 SH_DOOR_NAME(shp));
2004 error = EACCES;
2005 goto fail;
2008 /* Bind this publisher in the kernel registration database */
2009 if (update_kernel_registration(shp, PUBLISHER,
2010 SE_BIND_REGISTRATION, &pub_id, 0, NULL) != 0) {
2011 error = errno;
2012 goto fail;
2015 SH_ID(shp) = pub_id;
2016 SH_BOUND(shp) = 1;
2017 SH_TYPE(shp) = PUBLISHER;
2020 /* Create the subscription registration cache */
2021 if (create_cached_registration(shp, SH_CLASS_HASH(shp)) != 0) {
2022 (void) update_kernel_registration(shp,
2023 PUBLISHER, SE_UNBIND_REGISTRATION, &pub_id, 0, NULL);
2024 error = EFAULT;
2025 goto fail;
2027 (void) close(fd);
2029 (void) mutex_unlock(SH_LOCK(shp));
2031 return (0);
2033 fail:
2034 SH_BOUND(shp) = 0;
2035 (void) door_revoke(SH_DOOR_DESC(shp));
2036 (void) fdetach(SH_DOOR_NAME(shp));
2037 free(SH_DOOR_NAME(shp));
2038 free(pub);
2039 (void) close(fd);
2040 (void) mutex_unlock(SH_LOCK(shp));
2041 errno = error;
2042 return (-1);
2045 static pthread_once_t xdoor_thrattr_once = PTHREAD_ONCE_INIT;
2046 static pthread_attr_t xdoor_thrattr;
2048 static void
2049 xdoor_thrattr_init(void)
2051 (void) pthread_attr_init(&xdoor_thrattr);
2052 (void) pthread_attr_setdetachstate(&xdoor_thrattr,
2053 PTHREAD_CREATE_DETACHED);
2054 (void) pthread_attr_setscope(&xdoor_thrattr, PTHREAD_SCOPE_SYSTEM);
2057 static int
2058 xdoor_server_create(door_info_t *dip, void *(*startf)(void *),
2059 void *startfarg, void *cookie)
2061 struct sysevent_subattr_impl *xsa = cookie;
2062 pthread_attr_t *thrattr;
2063 sigset_t oset;
2064 int err;
2066 if (xsa->xs_thrcreate) {
2067 return (xsa->xs_thrcreate(dip, startf, startfarg,
2068 xsa->xs_thrcreate_cookie));
2071 if (xsa->xs_thrattr == NULL) {
2072 (void) pthread_once(&xdoor_thrattr_once, xdoor_thrattr_init);
2073 thrattr = &xdoor_thrattr;
2074 } else {
2075 thrattr = xsa->xs_thrattr;
2078 (void) pthread_sigmask(SIG_SETMASK, &xsa->xs_sigmask, &oset);
2079 err = pthread_create(NULL, thrattr, startf, startfarg);
2080 (void) pthread_sigmask(SIG_SETMASK, &oset, NULL);
2082 return (err == 0 ? 1 : -1);
2085 static void
2086 xdoor_server_setup(void *cookie)
2088 struct sysevent_subattr_impl *xsa = cookie;
2090 if (xsa->xs_thrsetup) {
2091 xsa->xs_thrsetup(xsa->xs_thrsetup_cookie);
2092 } else {
2093 (void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
2094 (void) pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
2098 static int
2099 sysevent_bind_subscriber_cmn(sysevent_handle_t *shp,
2100 void (*event_handler)(sysevent_t *ev),
2101 sysevent_subattr_t *subattr)
2103 int fd = -1;
2104 int error = 0;
2105 uint32_t sub_id = 0;
2106 char door_name[MAXPATHLEN];
2107 subscriber_priv_t *sub_info;
2108 int created;
2109 struct sysevent_subattr_impl *xsa =
2110 (struct sysevent_subattr_impl *)subattr;
2112 if (shp == NULL || event_handler == NULL) {
2113 errno = EINVAL;
2114 return (-1);
2117 (void) mutex_lock(SH_LOCK(shp));
2118 if (SH_BOUND(shp)) {
2119 errno = EINVAL;
2120 (void) mutex_unlock(SH_LOCK(shp));
2121 return (-1);
2124 if ((sub_info = (subscriber_priv_t *)calloc(1,
2125 sizeof (subscriber_priv_t))) == NULL) {
2126 errno = ENOMEM;
2127 (void) mutex_unlock(SH_LOCK(shp));
2128 return (-1);
2131 if (snprintf(door_name, MAXPATHLEN, "%s/%s",
2132 SH_CHANNEL_PATH(shp), REG_DOOR) >= MAXPATHLEN) {
2133 free(sub_info);
2134 errno = EINVAL;
2135 (void) mutex_unlock(SH_LOCK(shp));
2136 return (-1);
2139 if ((sub_info->sp_door_name = strdup(door_name)) == NULL) {
2140 free(sub_info);
2141 errno = ENOMEM;
2142 (void) mutex_unlock(SH_LOCK(shp));
2143 return (-1);
2145 (void) cond_init(&sub_info->sp_cv, USYNC_THREAD, NULL);
2146 (void) mutex_init(&sub_info->sp_qlock, USYNC_THREAD, NULL);
2147 sub_info->sp_func = event_handler;
2149 /* Update the in-kernel registration */
2150 if (update_kernel_registration(shp, SUBSCRIBER,
2151 SE_BIND_REGISTRATION, &sub_id, 0, NULL) != 0) {
2152 error = errno;
2153 goto fail;
2155 SH_ID(shp) = sub_id;
2157 if (snprintf(door_name, MAXPATHLEN, "%s/%d",
2158 SH_CHANNEL_PATH(shp), sub_id) >= MAXPATHLEN) {
2159 error = EINVAL;
2160 goto fail;
2162 if ((SH_DOOR_NAME(shp) = strdup(door_name)) == NULL) {
2163 error = ENOMEM;
2164 goto fail;
2168 * Remove door file for robustness.
2170 if (unlink(SH_DOOR_NAME(shp)) != 0)
2171 dprint("sysevent_bind_subscriber: Unlink of %s failed.\n",
2172 SH_DOOR_NAME(shp));
2174 fd = open(SH_DOOR_NAME(shp), O_CREAT|O_RDWR, S_IREAD|S_IWRITE);
2175 if (fd == -1) {
2176 error = EFAULT;
2177 goto fail;
2181 * Create the sysevent door service for this client.
2182 * syseventd will use this door service to propagate
2183 * events to the client.
2185 if (subattr == NULL) {
2186 SH_DOOR_DESC(shp) = door_create(event_deliver_service,
2187 (void *)shp, DOOR_REFUSE_DESC | DOOR_NO_CANCEL);
2188 } else {
2189 SH_DOOR_DESC(shp) = door_xcreate(event_deliver_service,
2190 (void *)shp,
2191 DOOR_REFUSE_DESC | DOOR_NO_CANCEL | DOOR_NO_DEPLETION_CB,
2192 xdoor_server_create, xdoor_server_setup,
2193 (void *)subattr, 1);
2196 if (SH_DOOR_DESC(shp) == -1) {
2197 dprint("sysevent_bind_subscriber: door create failed: "
2198 "%s\n", strerror(errno));
2199 error = EFAULT;
2200 goto fail;
2203 (void) fdetach(SH_DOOR_NAME(shp));
2204 if (fattach(SH_DOOR_DESC(shp), SH_DOOR_NAME(shp)) != 0) {
2205 error = EFAULT;
2206 goto fail;
2208 (void) close(fd);
2210 if (update_publisher_cache(sub_info, SE_BIND_REGISTRATION,
2211 sub_id, 0, NULL) != 0) {
2212 error = errno;
2213 (void) update_kernel_registration(shp, SUBSCRIBER,
2214 SE_UNBIND_REGISTRATION, &sub_id, 0, NULL);
2215 goto fail;
2218 SH_BOUND(shp) = 1;
2219 SH_TYPE(shp) = SUBSCRIBER;
2220 SH_PRIV_DATA(shp) = (void *)sub_info;
2222 /* Create an event handler thread */
2223 if (xsa == NULL || xsa->xs_thrcreate == NULL) {
2224 created = thr_create(NULL, NULL,
2225 (void *(*)(void *))subscriber_event_handler,
2226 shp, THR_BOUND, &sub_info->sp_handler_tid) == 0;
2227 } else {
2229 * A terrible hack. We will use the extended private
2230 * door thread creation function the caller passed in to
2231 * create the event handler thread. That function will
2232 * be called with our chosen thread start function and arg
2233 * instead of the usual libc-provided ones, but that's ok
2234 * as it is required to use them verbatim anyway. We will
2235 * pass a NULL door_info_t pointer to the function - so
2236 * callers depending on this hack had better be prepared
2237 * for that. All this allow the caller to rubberstamp
2238 * the created thread as it wishes. But we don't get
2239 * the created threadid with this, so we modify the
2240 * thread start function to stash it.
2243 created = xsa->xs_thrcreate(NULL,
2244 (void *(*)(void *))subscriber_event_handler,
2245 shp, xsa->xs_thrcreate_cookie) == 1;
2248 if (!created) {
2249 error = EFAULT;
2250 goto fail;
2253 (void) mutex_unlock(SH_LOCK(shp));
2255 return (0);
2257 fail:
2258 (void) close(fd);
2259 (void) door_revoke(SH_DOOR_DESC(shp));
2260 (void) fdetach(SH_DOOR_NAME(shp));
2261 (void) cond_destroy(&sub_info->sp_cv);
2262 (void) mutex_destroy(&sub_info->sp_qlock);
2263 free(sub_info->sp_door_name);
2264 free(sub_info);
2265 if (SH_ID(shp)) {
2266 (void) update_kernel_registration(shp, SUBSCRIBER,
2267 SE_UNBIND_REGISTRATION, &sub_id, 0, NULL);
2268 SH_ID(shp) = 0;
2270 if (SH_BOUND(shp)) {
2271 (void) update_publisher_cache(sub_info, SE_UNBIND_REGISTRATION,
2272 sub_id, 0, NULL);
2273 free(SH_DOOR_NAME(shp));
2274 SH_BOUND(shp) = 0;
2276 (void) mutex_unlock(SH_LOCK(shp));
2278 errno = error;
2280 return (-1);
2284 * sysevent_bind_subscriber - Bind an event receiver to an event channel
2287 sysevent_bind_subscriber(sysevent_handle_t *shp,
2288 void (*event_handler)(sysevent_t *ev))
2290 return (sysevent_bind_subscriber_cmn(shp, event_handler, NULL));
2294 * sysevent_bind_xsubscriber - Bind a subscriber using door_xcreate with
2295 * attributes specified.
2298 sysevent_bind_xsubscriber(sysevent_handle_t *shp,
2299 void (*event_handler)(sysevent_t *ev), sysevent_subattr_t *subattr)
2301 return (sysevent_bind_subscriber_cmn(shp, event_handler, subattr));
2305 * sysevent_register_event - register an event class and associated subclasses
2306 * for an event subscriber
2309 sysevent_register_event(sysevent_handle_t *shp,
2310 const char *ev_class, const char **ev_subclass,
2311 int subclass_num)
2313 int error;
2314 char *event_class = (char *)ev_class;
2315 char **event_subclass_list = (char **)ev_subclass;
2316 char *nvlbuf = NULL;
2317 size_t datalen;
2318 nvlist_t *nvl;
2320 (void) mutex_lock(SH_LOCK(shp));
2321 if (event_class == NULL || event_subclass_list == NULL ||
2322 event_subclass_list[0] == NULL || SH_BOUND(shp) != 1 ||
2323 subclass_num <= 0) {
2324 (void) mutex_unlock(SH_LOCK(shp));
2325 errno = EINVAL;
2326 return (-1);
2329 if (nvlist_alloc(&nvl, NV_UNIQUE_NAME_TYPE, 0) != 0) {
2330 (void) mutex_unlock(SH_LOCK(shp));
2331 return (-1);
2333 if (nvlist_add_string_array(nvl, event_class, event_subclass_list,
2334 subclass_num) != 0) {
2335 nvlist_free(nvl);
2336 (void) mutex_unlock(SH_LOCK(shp));
2337 return (-1);
2339 if (nvlist_pack(nvl, &nvlbuf, &datalen, NV_ENCODE_NATIVE, 0) != 0) {
2340 nvlist_free(nvl);
2341 (void) mutex_unlock(SH_LOCK(shp));
2342 return (-1);
2344 nvlist_free(nvl);
2346 /* Store new subscriber in in-kernel registration */
2347 if (update_kernel_registration(shp, SUBSCRIBER,
2348 SE_REGISTER, &SH_ID(shp), datalen, (uchar_t *)nvlbuf)
2349 != 0) {
2350 error = errno;
2351 free(nvlbuf);
2352 (void) mutex_unlock(SH_LOCK(shp));
2353 errno = error;
2354 return (-1);
2356 /* Update the publisher's cached registration */
2357 if (update_publisher_cache(
2358 (subscriber_priv_t *)SH_PRIV_DATA(shp), SE_REGISTER,
2359 SH_ID(shp), datalen, (uchar_t *)nvlbuf) != 0) {
2360 error = errno;
2361 free(nvlbuf);
2362 (void) mutex_unlock(SH_LOCK(shp));
2363 errno = error;
2364 return (-1);
2367 free(nvlbuf);
2369 (void) mutex_unlock(SH_LOCK(shp));
2371 return (0);
2375 * sysevent_unregister_event - Unregister an event class and associated
2376 * subclasses for an event subscriber
2378 void
2379 sysevent_unregister_event(sysevent_handle_t *shp, const char *class)
2381 size_t class_sz;
2383 (void) mutex_lock(SH_LOCK(shp));
2385 if (!SH_BOUND(shp)) {
2386 (void) mutex_unlock(SH_LOCK(shp));
2387 return;
2390 /* Remove subscriber from in-kernel registration */
2391 class_sz = strlen(class) + 1;
2392 (void) update_kernel_registration(shp, SUBSCRIBER,
2393 SE_UNREGISTER, &SH_ID(shp), class_sz, (uchar_t *)class);
2394 /* Update the publisher's cached registration */
2395 (void) update_publisher_cache(
2396 (subscriber_priv_t *)SH_PRIV_DATA(shp), SE_UNREGISTER,
2397 SH_ID(shp), class_sz, (uchar_t *)class);
2399 (void) mutex_unlock(SH_LOCK(shp));
2402 static int
2403 cleanup_id(sysevent_handle_t *shp, uint32_t id, int type)
2405 dprint("cleanup_id: Cleaning up %s/%d\n", SH_CHANNEL_NAME(shp), id);
2407 /* Remove registration from the kernel */
2408 if (update_kernel_registration(shp, type, SE_CLEANUP, &id,
2409 0, NULL) != 0) {
2410 dprint("cleanup_id: Unable to clean "
2411 "up %s/%d\n", SH_CHANNEL_NAME(shp), id);
2412 return (-1);
2415 return (0);
2419 * sysevent_cleanup_subscribers: Allows the caller to cleanup resources
2420 * allocated to unresponsive subscribers.
2422 void
2423 sysevent_cleanup_subscribers(sysevent_handle_t *shp)
2425 uint32_t ping, result;
2426 int i, error, sub_fd;
2427 subscriber_data_t *sub;
2429 if (!SH_BOUND(shp)) {
2430 return;
2433 for (i = 1; i <= MAX_SUBSCRIBERS; ++i) {
2435 sub = SH_SUBSCRIBER(shp, i);
2436 if (sub == NULL) {
2437 continue;
2440 if ((sub_fd = open(sub->sd_door_name, O_RDONLY)) == -1) {
2441 continue;
2443 /* Check for valid and responsive subscriber */
2444 error = clnt_deliver_event(sub_fd, &ping,
2445 sizeof (uint32_t), &result, sizeof (result));
2446 (void) close(sub_fd);
2448 /* Only cleanup on EBADF (Invalid door descriptor) */
2449 if (error != EBADF)
2450 continue;
2452 if (cleanup_id(shp, i, SUBSCRIBER) != 0)
2453 continue;
2455 cache_remove_class(shp, EC_ALL, i);
2457 free(sub->sd_door_name);
2458 free(sub);
2459 SH_SUBSCRIBER(shp, i) = NULL;
2465 * sysevent_cleanup_publishers: Allows stale publisher handles to be deallocated
2466 * as needed.
2468 void
2469 sysevent_cleanup_publishers(sysevent_handle_t *shp)
2471 (void) cleanup_id(shp, 1, PUBLISHER);
2475 * sysevent_unbind_subscriber: Unbind the subscriber from the sysevent channel.
2477 void
2478 sysevent_unbind_subscriber(sysevent_handle_t *shp)
2480 subscriber_priv_t *sub_info;
2482 if (shp == NULL)
2483 return;
2485 (void) mutex_lock(SH_LOCK(shp));
2486 if (SH_BOUND(shp) == 0) {
2487 (void) mutex_unlock(SH_LOCK(shp));
2488 return;
2491 /* Update the in-kernel registration */
2492 (void) update_kernel_registration(shp, SUBSCRIBER,
2493 SE_UNBIND_REGISTRATION, &SH_ID(shp), 0, NULL);
2495 /* Update the sysevent channel publisher */
2496 sub_info = (subscriber_priv_t *)SH_PRIV_DATA(shp);
2497 (void) update_publisher_cache(sub_info, SE_UNBIND_REGISTRATION,
2498 SH_ID(shp), 0, NULL);
2500 /* Close down event delivery facilities */
2501 (void) door_revoke(SH_DOOR_DESC(shp));
2502 (void) fdetach(SH_DOOR_NAME(shp));
2505 * Release resources and wait for pending event delivery to
2506 * complete.
2508 (void) mutex_lock(&sub_info->sp_qlock);
2509 SH_BOUND(shp) = 0;
2510 /* Signal event handler and drain the subscriber's event queue */
2511 (void) cond_signal(&sub_info->sp_cv);
2512 (void) mutex_unlock(&sub_info->sp_qlock);
2513 if (sub_info->sp_handler_tid != NULL)
2514 (void) thr_join(sub_info->sp_handler_tid, NULL, NULL);
2516 (void) cond_destroy(&sub_info->sp_cv);
2517 (void) mutex_destroy(&sub_info->sp_qlock);
2518 free(sub_info->sp_door_name);
2519 free(sub_info);
2520 free(SH_DOOR_NAME(shp));
2521 (void) mutex_unlock(SH_LOCK(shp));
2525 * sysevent_unbind_publisher: Unbind publisher from the sysevent channel.
2527 void
2528 sysevent_unbind_publisher(sysevent_handle_t *shp)
2530 if (shp == NULL)
2531 return;
2533 (void) mutex_lock(SH_LOCK(shp));
2534 if (SH_BOUND(shp) == 0) {
2535 (void) mutex_unlock(SH_LOCK(shp));
2536 return;
2539 /* Close down the registration facilities */
2540 (void) door_revoke(SH_DOOR_DESC(shp));
2541 (void) fdetach(SH_DOOR_NAME(shp));
2543 /* Update the in-kernel registration */
2544 (void) update_kernel_registration(shp, PUBLISHER,
2545 SE_UNBIND_REGISTRATION, &SH_ID(shp), 0, NULL);
2546 SH_BOUND(shp) = 0;
2548 /* Free resources associated with bind */
2549 free_cached_registration(shp);
2550 dealloc_subscribers(shp);
2552 free(SH_PRIV_DATA(shp));
2553 free(SH_DOOR_NAME(shp));
2554 SH_ID(shp) = 0;
2555 (void) mutex_unlock(SH_LOCK(shp));
2559 * Evolving APIs to subscribe to syseventd(1M) system events.
2562 static sysevent_handle_t *
2563 sysevent_bind_handle_cmn(void (*event_handler)(sysevent_t *ev),
2564 sysevent_subattr_t *subattr)
2566 sysevent_handle_t *shp;
2568 if (getuid() != 0) {
2569 errno = EACCES;
2570 return (NULL);
2573 if (event_handler == NULL) {
2574 errno = EINVAL;
2575 return (NULL);
2578 if ((shp = sysevent_open_channel(SYSEVENTD_CHAN)) == NULL) {
2579 return (NULL);
2582 if (sysevent_bind_xsubscriber(shp, event_handler, subattr) != 0) {
2584 * Ask syseventd to clean-up any stale subcribers and try to
2585 * to bind again
2587 if (errno == EBUSY) {
2588 int pub_fd;
2589 char door_name[MAXPATHLEN];
2590 uint32_t result;
2591 struct reg_args rargs;
2593 if (snprintf(door_name, MAXPATHLEN, "%s/%s",
2594 SH_CHANNEL_PATH(shp), REG_DOOR) >= MAXPATHLEN) {
2595 sysevent_close_channel(shp);
2596 errno = EINVAL;
2597 return (NULL);
2600 rargs.ra_op = SE_CLEANUP;
2601 pub_fd = open(door_name, O_RDONLY);
2602 (void) clnt_deliver_event(pub_fd, (void *)&rargs,
2603 sizeof (struct reg_args), &result, sizeof (result));
2604 (void) close(pub_fd);
2606 /* Try to bind again */
2607 if (sysevent_bind_xsubscriber(shp, event_handler,
2608 subattr) != 0) {
2609 sysevent_close_channel(shp);
2610 return (NULL);
2612 } else {
2613 sysevent_close_channel(shp);
2614 return (NULL);
2618 return (shp);
2622 * sysevent_bind_handle - Bind application event handler for syseventd
2623 * subscription.
2625 sysevent_handle_t *
2626 sysevent_bind_handle(void (*event_handler)(sysevent_t *ev))
2628 return (sysevent_bind_handle_cmn(event_handler, NULL));
2632 * sysevent_bind_xhandle - Bind application event handler for syseventd
2633 * subscription, using door_xcreate and attributes as specified.
2635 sysevent_handle_t *
2636 sysevent_bind_xhandle(void (*event_handler)(sysevent_t *ev),
2637 sysevent_subattr_t *subattr)
2639 return (sysevent_bind_handle_cmn(event_handler, subattr));
2643 * sysevent_unbind_handle - Unbind caller from syseventd subscriptions
2645 void
2646 sysevent_unbind_handle(sysevent_handle_t *shp)
2648 sysevent_unbind_subscriber(shp);
2649 sysevent_close_channel(shp);
2653 * sysevent_subscribe_event - Subscribe to system event notification from
2654 * syseventd(1M) for the class and subclasses specified.
2657 sysevent_subscribe_event(sysevent_handle_t *shp, const char *event_class,
2658 const char **event_subclass_list, int num_subclasses)
2660 return (sysevent_register_event(shp, event_class,
2661 event_subclass_list, num_subclasses));
2664 void
2665 sysevent_unsubscribe_event(sysevent_handle_t *shp, const char *event_class)
2667 sysevent_unregister_event(shp, event_class);