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]
23 * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
39 #include <libnvpair.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
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
73 * sysevent_alloc - allocate a sysevent buffer
76 sysevent_alloc(char *class, int class_sz
, char *subclass
, int subclass_sz
,
77 char *pub
, int pub_sz
, nvlist_t
*attr_list
)
80 int aligned_class_sz
, aligned_subclass_sz
, aligned_pub_sz
;
86 if (attr_list
!= NULL
) {
87 if (nvlist_size(attr_list
, &nvlist_sz
, SE_ENCODE_METHOD
)
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) +
109 * Allocate event buffer plus additional payload overhead.
111 ev
= calloc(1, sizeof (sysevent_impl_t
) + payload_sz
);
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
))
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
) {
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
,
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
)
158 ev
= sysevent_alloc_event(class, subclass
, vendor
, pub_name
, attr_list
);
163 error
= modctl(MODEVENTS
, (uintptr_t)MODEVENTS_POST_EVENT
,
164 (uintptr_t)ev
, (uintptr_t)SE_SIZE(ev
), (uintptr_t)eid
, 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.
186 sysevent_dup(sysevent_t
*ev
)
188 nvlist_t
*nvl
, *cnvl
= NULL
;
189 uint64_t attr_offset
;
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
);
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) {
208 SE_ATTR_PTR(copy
) = (uintptr_t)cnvl
;
209 SE_FLAG(copy
) = 0; /* unpacked */
214 * sysevent_free - Free memory allocated for an event buffer
217 sysevent_free(sysevent_t
*ev
)
219 nvlist_t
*attr_list
= (nvlist_t
*)(uintptr_t)SE_ATTR_PTR(ev
);
222 nvlist_free(attr_list
);
227 * The following routines are used to extract attribute data from a sysevent
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
)
241 uint64_t attr_offset
;
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
);
252 if ((error
= nvlist_dup(nvl
, nvlist
, 0)) != 0) {
253 if (error
== ENOMEM
) {
263 attr_offset
= SE_ATTR_OFF(ev
);
264 if (SE_SIZE(ev
) == attr_offset
) {
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
) {
284 * sysevent_attr_name - Get name of attribute
287 sysevent_attr_name(sysevent_attr_t
*attr
)
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
;
307 /* Convert DATA_TYPE_* to SE_DATA_TYPE_* */
308 switch (nvpair_type(nvp
)) {
310 se_value
->value_type
= SE_DATA_TYPE_BYTE
;
311 (void) nvpair_value_byte(nvp
, &se_value
->value
.sv_byte
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
358 * sysevent_attr_next - Get next attribute in event attribute list
361 sysevent_attr_next(sysevent_t
*ev
, sysevent_attr_t
*attr
)
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) {
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
)
387 assert(SE_FLAG(ev
) != SE_PACKED_BUF
);
389 if (SE_ATTR_PTR(ev
) == (uint64_t)0) {
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
);
401 if ((strcmp(name
, nvpair_name(nvp
)) == 0) &&
402 (sysevent_attr_value(nvp
, se_value
) == 0) &&
403 (se_value
->value_type
== datatype
))
405 nvp
= nvlist_next_nvpair(nvl
, nvp
);
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
434 sysevent_get_class_name(sysevent_t
*ev
)
436 return (SE_CLASS_NAME(ev
));
447 * sysevent_get_pub - Get publisher name string
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.
461 parse_pub_id(sysevent_t
*ev
, se_pub_id_t token
)
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
);
475 pub_element
= strdup(str
);
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.
487 pub_idx(const char *pstr
, int token
)
491 for (i
= 1; i
<= token
; i
++) {
492 if ((pstr
= index(pstr
, ':')) == NULL
)
497 /* String might be empty */
499 if (*pstr
== '\0' || *pstr
== ':')
506 sysevent_get_vendor_name(sysevent_t
*ev
)
508 return (parse_pub_id(ev
, PUB_VEND
));
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.
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
)
533 if ((part_str
= pub_idx(pub_str
, PUB_PID
)) == NULL
)
536 *pid
= (pid_t
)atoi(part_str
);
540 * sysevent_get_subclass_name - Get subclass name string
543 sysevent_get_subclass_name(sysevent_t
*ev
)
545 return (SE_SUBCLASS_NAME(ev
));
549 * sysevent_get_seq - Get event sequence id
552 sysevent_get_seq(sysevent_t
*ev
)
558 * sysevent_get_time - Get event timestamp
561 sysevent_get_time(sysevent_t
*ev
, hrtime_t
*etime
)
563 *etime
= SE_TIME(ev
);
567 * sysevent_get_size - Get event buffer size
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
587 sysevent_alloc_event(char *class, char *subclass
, char *vendor
, char *pub_name
,
590 int class_sz
, subclass_sz
, pub_sz
;
594 if ((class == NULL
) || (subclass
== NULL
) || (vendor
== NULL
) ||
595 (pub_name
== 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
)) {
609 * Calculate the publisher size plus string seperators and maximum
612 pub_sz
= strlen(vendor
) + sizeof (SE_USR_PUB
) + strlen(pub_name
) + 14;
613 if (pub_sz
> MAX_PUB_LEN
) {
617 pub_id
= malloc(pub_sz
);
618 if (pub_id
== NULL
) {
622 if (snprintf(pub_id
, pub_sz
, "%s:%s%s:%d", vendor
, SE_USR_PUB
,
623 pub_name
, (int)getpid()) >= pub_sz
) {
628 pub_sz
= strlen(pub_id
) + 1;
630 ev
= sysevent_alloc(class, class_sz
, subclass
, subclass_sz
,
631 pub_id
, pub_sz
, attr_list
);
642 * se_unpack - unpack nvlist to a searchable list.
643 * If already unpacked, will do a dup.
646 se_unpack(sysevent_t
*ev
)
650 nvlist_t
*attrp
= NULL
;
651 uint64_t attr_offset
;
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
);
661 bcopy(ev
, copy
, attr_offset
);
662 SE_FLAG(copy
) = 0; /* unpacked */
665 attr
= (caddr_t
)ev
+ attr_offset
;
666 attr_len
= SE_SIZE(ev
) - attr_offset
;
670 if (nvlist_unpack(attr
, attr_len
, &attrp
, 0) != 0) {
675 SE_ATTR_PTR(copy
) = (uintptr_t)attrp
;
680 * se_print - Prints elements in an event buffer
683 se_print(FILE *fp
, sysevent_t
*ev
)
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
);
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
);
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
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.
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
);
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
;
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
;
784 evqp
= sub_info
->sp_evq_head
;
786 if (!SH_BOUND(shp
)) {
787 (void) mutex_unlock(&sub_info
->sp_qlock
);
796 * Data structure used to communicate event subscription cache updates
797 * to publishers via a registration door
807 * event_deliver_service - generic event delivery service routine. This routine
808 * is called in response to a door call to post an event.
813 event_deliver_service(void *cookie
, char *args
, size_t alen
,
814 door_desc_t
*ddp
, uint_t ndid
)
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)) {
823 goto return_from_door
;
826 /* Publisher checking on subscriber */
827 if (alen
== sizeof (uint32_t)) {
829 goto return_from_door
;
832 shp
= (sysevent_handle_t
*)cookie
;
835 goto return_from_door
;
839 * Mustn't block if we are trying to update the registration with
842 if (mutex_trylock(SH_LOCK(shp
)) != 0) {
844 goto return_from_door
;
847 if (!SH_BOUND(shp
)) {
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
) {
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
) {
864 (void) mutex_unlock(SH_LOCK(shp
));
865 goto return_from_door
;
869 * Allocate and copy the event buffer into the subscriber's
872 new_eq
->sq_ev
= calloc(1, alen
);
873 if (new_eq
->sq_ev
== NULL
) {
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
;
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
));
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
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.
924 clnt_deliver_event(int service_door
, void *data
, size_t datalen
,
925 void *result
, size_t rlen
)
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;
940 while ((error
= door_call(service_door
, &door_arg
)) != 0) {
941 if (errno
== EAGAIN
|| errno
== EINTR
) {
953 update_publisher_cache(subscriber_priv_t
*sub_info
, int update_op
,
954 uint32_t sub_id
, size_t datasz
, uchar_t
*data
)
958 struct reg_args
*rargs
;
960 rargs
= (struct reg_args
*)calloc(1, sizeof (struct reg_args
) +
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
);
987 * update_kernel_registration - update the in-kernel registration for the
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
)
995 char *channel_name
= SH_CHANNEL_NAME(shp
);
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))
1010 *sub_id
= udata
.ps_id
;
1016 * get_kernel_registration - get the current subscriber registration for
1020 get_kernel_registration(char *channel_name
, uint32_t class_id
)
1026 nvlbuf
= calloc(1, MAX_SUBSCRIPTION_SZ
);
1027 if (nvlbuf
== 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)
1041 /* Need a bigger buffer to hold channel registration */
1042 if (errno
== EAGAIN
) {
1044 nvlbuf
= calloc(1, udata
.ps_buflen
);
1049 if (modctl(MODEVENTS
,
1050 (uintptr_t)MODEVENTS_REGISTER_EVENT
,
1051 (uintptr_t)channel_name
, (uintptr_t)nvlbuf
,
1052 (uintptr_t)&udata
, 0) != 0) {
1062 if (nvlist_unpack(nvlbuf
, udata
.ps_buflen
, &nvl
, 0) != 0) {
1072 * The following routines provide a mechanism for publishers to maintain
1073 * subscriber information.
1077 dealloc_subscribers(sysevent_handle_t
*shp
)
1080 subscriber_data_t
*sub
;
1082 for (i
= 1; i
<= MAX_SUBSCRIBERS
; ++i
) {
1083 sub
= SH_SUBSCRIBER(shp
, i
);
1085 free(sub
->sd_door_name
);
1088 SH_SUBSCRIBER(shp
, i
) = NULL
;
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
) {
1103 /* Allocate and initialize the subscriber data */
1104 sub
= (subscriber_data_t
*)calloc(1,
1105 sizeof (subscriber_data_t
));
1109 if (snprintf(door_name
, MAXPATHLEN
, "%s/%d",
1110 SH_CHANNEL_PATH(shp
), sub_id
) >= MAXPATHLEN
) {
1115 sub
->sd_flag
= ACTIVE
;
1116 sub
->sd_door_name
= strdup(door_name
);
1117 if (sub
->sd_door_name
== NULL
) {
1122 SH_SUBSCRIBER(shp
, sub_id
) = sub
;
1128 * The following routines are used to update and maintain the registration cache
1129 * for a particular sysevent channel.
1133 hash_func(const char *s
)
1135 uint32_t result
= 0;
1138 while (*s
!= '\0') {
1140 result
+= (uint32_t)*s
++;
1141 g
= result
& 0xf0000000;
1152 cache_find_subclass(class_lst_t
*c_list
, char *subclass
)
1154 subclass_lst_t
*sc_list
;
1159 sc_list
= c_list
->cl_subclass_list
;
1161 while (sc_list
!= NULL
) {
1162 if (strcmp(sc_list
->sl_name
, subclass
) == 0) {
1165 sc_list
= sc_list
->sl_next
;
1172 static class_lst_t
*
1173 cache_find_class(sysevent_handle_t
*shp
, char *class)
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) {
1189 c_list
= c_list
->cl_next
;
1196 cache_insert_subclass(class_lst_t
*c_list
, char **subclass_names
,
1197 int subclass_num
, uint32_t sub_id
)
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
]))
1205 sc_list
->sl_num
[sub_id
] = 1;
1207 sc_list
= (subclass_lst_t
*)calloc(1,
1208 sizeof (subclass_lst_t
));
1209 if (sc_list
== NULL
)
1212 sc_list
->sl_name
= strdup(subclass_names
[i
]);
1213 if (sc_list
->sl_name
== NULL
) {
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
;
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
);
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
) {
1248 c_list
->cl_name
= strdup(class);
1249 if (c_list
->cl_name
== NULL
) {
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
);
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
);
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
,
1282 cache_remove_all_class(sysevent_handle_t
*shp
, uint32_t sub_id
)
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
;
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
);
1312 if ((c_list
= cache_find_class(shp
, class)) == NULL
) {
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
;
1324 free_cached_registration(sysevent_handle_t
*shp
)
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
;
1340 free(clist
->cl_name
);
1341 next_clist
= clist
->cl_next
;
1345 SH_CLASS_HASH(shp
)[i
] = NULL
;
1350 create_cached_registration(sysevent_handle_t
*shp
,
1351 class_lst_t
**class_hash
)
1353 int i
, j
, new_class
;
1356 uchar_t
*subscribers
;
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
))
1366 if (errno
== ENOENT
) {
1367 class_hash
[i
] = NULL
;
1376 if ((nvpair
= nvlist_next_nvpair(nvl
, nvpair
)) == NULL
) {
1382 /* Extract the class name from the nvpair */
1383 if (nvpair_value_string(nvpair
, &class_name
) != 0) {
1386 clist
= (class_lst_t
*)
1387 calloc(1, sizeof (class_lst_t
));
1388 if (clist
== NULL
) {
1392 clist
->cl_name
= strdup(class_name
);
1393 if (clist
->cl_name
== NULL
) {
1399 * Extract the subclass name and registration
1402 if ((nvpair
= nvlist_next_nvpair(nvl
, nvpair
))
1404 free(clist
->cl_name
);
1409 clist
->cl_next
= class_hash
[i
];
1410 class_hash
[i
] = clist
;
1414 sc_list
= (subclass_lst_t
*)calloc(1,
1415 sizeof (subclass_lst_t
));
1416 if (sc_list
== NULL
) {
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
) {
1428 if (nvpair_value_byte_array(nvpair
,
1429 &subscribers
, &num_elem
) != 0) {
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)
1439 if (alloc_subscriber(shp
, j
, 1) != 0) {
1445 * Check next nvpair - either subclass or
1448 if ((nvpair
= nvlist_next_nvpair(nvl
, nvpair
))
1452 } else if (strcmp(nvpair_name(nvpair
),
1463 dealloc_subscribers(shp
);
1464 free_cached_registration(shp
);
1472 * cache_update_service - generic event publisher service routine. This routine
1473 * is called in response to a registration cache update.
1478 cache_update_service(void *cookie
, char *args
, size_t alen
,
1479 door_desc_t
*ddp
, uint_t ndid
)
1483 char *class, **event_list
;
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
) {
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
) {
1508 class = (char *)&rargs
->ra_buf_ptr
;
1509 cache_remove_class(shp
, (char *)class,
1512 case SE_UNBIND_REGISTRATION
:
1514 sub
= SH_SUBSCRIBER(shp
, sub_id
);
1518 free(sub
->sd_door_name
);
1520 cache_remove_class(shp
, EC_ALL
, sub_id
);
1521 SH_SUBSCRIBER(shp
, sub_id
) = NULL
;
1524 case SE_BIND_REGISTRATION
:
1526 /* New subscriber */
1527 if (alloc_subscriber(shp
, sub_id
, 0) != 0) {
1534 if (SH_SUBSCRIBER(shp
, sub_id
) == NULL
) {
1538 /* Get new registration data */
1539 if (nvlist_unpack((char *)&rargs
->ra_buf_ptr
, datalen
,
1544 if ((nvpair
= nvlist_next_nvpair(nvl
, nvpair
)) == NULL
) {
1549 if (nvpair_value_string_array(nvpair
, &event_list
, &num_elem
)
1555 class = nvpair_name(nvpair
);
1557 ret
= cache_insert_class(shp
, class,
1558 event_list
, num_elem
, sub_id
);
1560 cache_remove_class(shp
, class, sub_id
);
1570 /* Cleanup stale subscribers */
1571 sysevent_cleanup_subscribers(shp
);
1577 (void) mutex_unlock(SH_LOCK(shp
));
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
));
1614 if (!SH_BOUND(shp
)) {
1615 (void) mutex_unlock(SH_LOCK(shp
));
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
);
1628 all_subclass_subscribers
= sc_lst
->sl_num
;
1630 all_subclass_subscribers
= NULL
;
1632 sc_lst
= cache_find_subclass(
1633 cache_find_class(shp
, event_class
), event_subclass
);
1635 subclass_subscribers
= sc_lst
->sl_num
;
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)
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",
1656 /* Process only resend requests */
1657 if (resend_cnt
> 0 && !(sub
->sd_flag
& SEND_AGAIN
)) {
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
));
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 */
1675 /* Subscriber requested EAGAIN */
1677 if (resend_cnt
> SE_MAX_RETRY_LIMIT
) {
1681 dprint("sysevent_send_event: resend "
1682 "requested for %d\n", i
);
1683 sub
->sd_flag
|= SEND_AGAIN
;
1686 /* Bad sysevent handle for subscriber */
1689 dprint("sysevent_send_event: Bad sysevent "
1690 "handle for %s", sub
->sd_door_name
);
1694 /* Successful delivery */
1696 sub
->sd_flag
&= ~SEND_AGAIN
;
1700 dprint("sysevent_send_event: Failed door call "
1701 "to %s: %s: %d\n", sub
->sd_door_name
,
1702 strerror(errno
), result
);
1713 if (deliver_error
) {
1714 sysevent_cleanup_subscribers(shp
);
1715 (void) mutex_unlock(SH_LOCK(shp
));
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
);
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;
1741 struct stat chan_stat
;
1742 sysevent_handle_t
*shp
;
1745 if (channel_path
== NULL
|| strlen(channel_path
) + 1 > MAXPATHLEN
) {
1750 if (mkdir(channel_path
, S_IRWXU
|S_IRGRP
|S_IXGRP
|S_IROTH
|S_IXOTH
) < 0) {
1751 if (errno
!= EEXIST
) {
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
);
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
);
1774 shp
= calloc(1, sizeof (sysevent_impl_hdl_t
));
1780 SH_CHANNEL_NAME(shp
) = NULL
;
1781 SH_CHANNEL_PATH(shp
) = strdup(channel_path
);
1782 if (SH_CHANNEL_PATH(shp
) == 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
) {
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
));
1806 (void) mutex_init(SH_LOCK(shp
), USYNC_THREAD
, NULL
);
1812 * Establish a sysevent channel for publication and subscription
1815 sysevent_open_channel(const char *channel
)
1817 int var_run_mounted
= 0;
1818 char full_channel
[MAXPATHLEN
+ 1];
1820 struct stat chan_stat
;
1823 if (channel
== NULL
) {
1829 * Check that /var/run is mounted as tmpfs before allowing a channel
1832 if ((fp
= fopen(MNTTAB
, "rF")) == NULL
) {
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;
1848 if (!var_run_mounted
) {
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
,
1859 if (errno
!= EEXIST
) {
1866 if (snprintf(full_channel
, MAXPATHLEN
, "%s/%s", CHAN_PATH
, channel
) >=
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
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
1890 sysevent_close_channel(sysevent_handle_t
*shp
)
1893 uint32_t sub_id
= 0;
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
));
1919 * sysevent_bind_publisher - Bind an event publisher to an event channel
1922 sysevent_bind_publisher(sysevent_handle_t
*shp
)
1926 char door_name
[MAXPATHLEN
];
1928 struct stat reg_stat
;
1929 publisher_priv_t
*pub
;
1936 (void) mutex_lock(SH_LOCK(shp
));
1937 if (SH_BOUND(shp
)) {
1938 (void) mutex_unlock(SH_LOCK(shp
));
1943 if ((pub
= (publisher_priv_t
*)calloc(1, sizeof (publisher_priv_t
))) ==
1945 (void) mutex_unlock(SH_LOCK(shp
));
1949 SH_PRIV_DATA(shp
) = (void *)pub
;
1951 if (snprintf(door_name
, MAXPATHLEN
, "%s/%s",
1952 SH_CHANNEL_PATH(shp
), REG_DOOR
) >= MAXPATHLEN
) {
1954 (void) mutex_unlock(SH_LOCK(shp
));
1958 if ((SH_DOOR_NAME(shp
) = strdup(door_name
)) == NULL
) {
1960 (void) mutex_unlock(SH_LOCK(shp
));
1965 /* Only one publisher allowed per channel */
1966 if (stat(SH_DOOR_NAME(shp
), ®_stat
) != 0) {
1967 if (errno
!= ENOENT
) {
1974 * Remove door file for robustness.
1976 if (unlink(SH_DOOR_NAME(shp
)) != 0)
1977 dprint("sysevent_bind_publisher: Unlink of %s failed.\n",
1980 /* Open channel registration door */
1981 fd
= open(SH_DOOR_NAME(shp
), O_CREAT
|O_RDWR
,
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
));
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",
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) {
2015 SH_ID(shp
) = pub_id
;
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
);
2029 (void) mutex_unlock(SH_LOCK(shp
));
2035 (void) door_revoke(SH_DOOR_DESC(shp
));
2036 (void) fdetach(SH_DOOR_NAME(shp
));
2037 free(SH_DOOR_NAME(shp
));
2040 (void) mutex_unlock(SH_LOCK(shp
));
2045 static pthread_once_t xdoor_thrattr_once
= PTHREAD_ONCE_INIT
;
2046 static pthread_attr_t xdoor_thrattr
;
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
);
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
;
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
;
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);
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
);
2093 (void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE
, NULL
);
2094 (void) pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED
, NULL
);
2099 sysevent_bind_subscriber_cmn(sysevent_handle_t
*shp
,
2100 void (*event_handler
)(sysevent_t
*ev
),
2101 sysevent_subattr_t
*subattr
)
2105 uint32_t sub_id
= 0;
2106 char door_name
[MAXPATHLEN
];
2107 subscriber_priv_t
*sub_info
;
2109 struct sysevent_subattr_impl
*xsa
=
2110 (struct sysevent_subattr_impl
*)subattr
;
2112 if (shp
== NULL
|| event_handler
== NULL
) {
2117 (void) mutex_lock(SH_LOCK(shp
));
2118 if (SH_BOUND(shp
)) {
2120 (void) mutex_unlock(SH_LOCK(shp
));
2124 if ((sub_info
= (subscriber_priv_t
*)calloc(1,
2125 sizeof (subscriber_priv_t
))) == NULL
) {
2127 (void) mutex_unlock(SH_LOCK(shp
));
2131 if (snprintf(door_name
, MAXPATHLEN
, "%s/%s",
2132 SH_CHANNEL_PATH(shp
), REG_DOOR
) >= MAXPATHLEN
) {
2135 (void) mutex_unlock(SH_LOCK(shp
));
2139 if ((sub_info
->sp_door_name
= strdup(door_name
)) == NULL
) {
2142 (void) mutex_unlock(SH_LOCK(shp
));
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) {
2155 SH_ID(shp
) = sub_id
;
2157 if (snprintf(door_name
, MAXPATHLEN
, "%s/%d",
2158 SH_CHANNEL_PATH(shp
), sub_id
) >= MAXPATHLEN
) {
2162 if ((SH_DOOR_NAME(shp
) = strdup(door_name
)) == NULL
) {
2168 * Remove door file for robustness.
2170 if (unlink(SH_DOOR_NAME(shp
)) != 0)
2171 dprint("sysevent_bind_subscriber: Unlink of %s failed.\n",
2174 fd
= open(SH_DOOR_NAME(shp
), O_CREAT
|O_RDWR
, S_IREAD
|S_IWRITE
);
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
);
2189 SH_DOOR_DESC(shp
) = door_xcreate(event_deliver_service
,
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
));
2203 (void) fdetach(SH_DOOR_NAME(shp
));
2204 if (fattach(SH_DOOR_DESC(shp
), SH_DOOR_NAME(shp
)) != 0) {
2210 if (update_publisher_cache(sub_info
, SE_BIND_REGISTRATION
,
2211 sub_id
, 0, NULL
) != 0) {
2213 (void) update_kernel_registration(shp
, SUBSCRIBER
,
2214 SE_UNBIND_REGISTRATION
, &sub_id
, 0, NULL
);
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;
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;
2253 (void) mutex_unlock(SH_LOCK(shp
));
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
);
2266 (void) update_kernel_registration(shp
, SUBSCRIBER
,
2267 SE_UNBIND_REGISTRATION
, &sub_id
, 0, NULL
);
2270 if (SH_BOUND(shp
)) {
2271 (void) update_publisher_cache(sub_info
, SE_UNBIND_REGISTRATION
,
2273 free(SH_DOOR_NAME(shp
));
2276 (void) mutex_unlock(SH_LOCK(shp
));
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
,
2314 char *event_class
= (char *)ev_class
;
2315 char **event_subclass_list
= (char **)ev_subclass
;
2316 char *nvlbuf
= NULL
;
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
));
2329 if (nvlist_alloc(&nvl
, NV_UNIQUE_NAME_TYPE
, 0) != 0) {
2330 (void) mutex_unlock(SH_LOCK(shp
));
2333 if (nvlist_add_string_array(nvl
, event_class
, event_subclass_list
,
2334 subclass_num
) != 0) {
2336 (void) mutex_unlock(SH_LOCK(shp
));
2339 if (nvlist_pack(nvl
, &nvlbuf
, &datalen
, NV_ENCODE_NATIVE
, 0) != 0) {
2341 (void) mutex_unlock(SH_LOCK(shp
));
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
)
2352 (void) mutex_unlock(SH_LOCK(shp
));
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) {
2362 (void) mutex_unlock(SH_LOCK(shp
));
2369 (void) mutex_unlock(SH_LOCK(shp
));
2375 * sysevent_unregister_event - Unregister an event class and associated
2376 * subclasses for an event subscriber
2379 sysevent_unregister_event(sysevent_handle_t
*shp
, const char *class)
2383 (void) mutex_lock(SH_LOCK(shp
));
2385 if (!SH_BOUND(shp
)) {
2386 (void) mutex_unlock(SH_LOCK(shp
));
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
));
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
,
2410 dprint("cleanup_id: Unable to clean "
2411 "up %s/%d\n", SH_CHANNEL_NAME(shp
), id
);
2419 * sysevent_cleanup_subscribers: Allows the caller to cleanup resources
2420 * allocated to unresponsive subscribers.
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
)) {
2433 for (i
= 1; i
<= MAX_SUBSCRIBERS
; ++i
) {
2435 sub
= SH_SUBSCRIBER(shp
, i
);
2440 if ((sub_fd
= open(sub
->sd_door_name
, O_RDONLY
)) == -1) {
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) */
2452 if (cleanup_id(shp
, i
, SUBSCRIBER
) != 0)
2455 cache_remove_class(shp
, EC_ALL
, i
);
2457 free(sub
->sd_door_name
);
2459 SH_SUBSCRIBER(shp
, i
) = NULL
;
2465 * sysevent_cleanup_publishers: Allows stale publisher handles to be deallocated
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.
2478 sysevent_unbind_subscriber(sysevent_handle_t
*shp
)
2480 subscriber_priv_t
*sub_info
;
2485 (void) mutex_lock(SH_LOCK(shp
));
2486 if (SH_BOUND(shp
) == 0) {
2487 (void) mutex_unlock(SH_LOCK(shp
));
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
2508 (void) mutex_lock(&sub_info
->sp_qlock
);
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
);
2520 free(SH_DOOR_NAME(shp
));
2521 (void) mutex_unlock(SH_LOCK(shp
));
2525 * sysevent_unbind_publisher: Unbind publisher from the sysevent channel.
2528 sysevent_unbind_publisher(sysevent_handle_t
*shp
)
2533 (void) mutex_lock(SH_LOCK(shp
));
2534 if (SH_BOUND(shp
) == 0) {
2535 (void) mutex_unlock(SH_LOCK(shp
));
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
);
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
));
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) {
2573 if (event_handler
== NULL
) {
2578 if ((shp
= sysevent_open_channel(SYSEVENTD_CHAN
)) == NULL
) {
2582 if (sysevent_bind_xsubscriber(shp
, event_handler
, subattr
) != 0) {
2584 * Ask syseventd to clean-up any stale subcribers and try to
2587 if (errno
== EBUSY
) {
2589 char door_name
[MAXPATHLEN
];
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
);
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
,
2609 sysevent_close_channel(shp
);
2613 sysevent_close_channel(shp
);
2622 * sysevent_bind_handle - Bind application event handler for syseventd
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.
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
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
));
2665 sysevent_unsubscribe_event(sysevent_handle_t
*shp
, const char *event_class
)
2667 sysevent_unregister_event(shp
, event_class
);