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
);
221 nvlist_free(attr_list
);
226 * The following routines are used to extract attribute data from a sysevent
231 * sysevent_get_attr_list - allocate and return an attribute associated with
232 * the given sysevent buffer.
235 sysevent_get_attr_list(sysevent_t
*ev
, nvlist_t
**nvlist
)
240 uint64_t attr_offset
;
245 /* Duplicate attribute for an unpacked sysevent buffer */
246 if (SE_FLAG(ev
) != SE_PACKED_BUF
) {
247 nvl
= (nvlist_t
*)(uintptr_t)SE_ATTR_PTR(ev
);
251 if ((error
= nvlist_dup(nvl
, nvlist
, 0)) != 0) {
252 if (error
== ENOMEM
) {
262 attr_offset
= SE_ATTR_OFF(ev
);
263 if (SE_SIZE(ev
) == attr_offset
) {
268 attr
= (caddr_t
)ev
+ attr_offset
;
269 attr_len
= SE_SIZE(ev
) - attr_offset
;
270 if ((error
= nvlist_unpack(attr
, attr_len
, nvlist
, 0)) != 0) {
271 if (error
== ENOMEM
) {
283 * sysevent_attr_name - Get name of attribute
286 sysevent_attr_name(sysevent_attr_t
*attr
)
292 return (nvpair_name((nvpair_t
*)attr
));
296 * sysevent_attr_value - Get attribute value data and type
299 sysevent_attr_value(sysevent_attr_t
*attr
, sysevent_value_t
*se_value
)
301 nvpair_t
*nvp
= attr
;
306 /* Convert DATA_TYPE_* to SE_DATA_TYPE_* */
307 switch (nvpair_type(nvp
)) {
309 se_value
->value_type
= SE_DATA_TYPE_BYTE
;
310 (void) nvpair_value_byte(nvp
, &se_value
->value
.sv_byte
);
312 case DATA_TYPE_INT16
:
313 se_value
->value_type
= SE_DATA_TYPE_INT16
;
314 (void) nvpair_value_int16(nvp
, &se_value
->value
.sv_int16
);
316 case DATA_TYPE_UINT16
:
317 se_value
->value_type
= SE_DATA_TYPE_UINT16
;
318 (void) nvpair_value_uint16(nvp
, &se_value
->value
.sv_uint16
);
320 case DATA_TYPE_INT32
:
321 se_value
->value_type
= SE_DATA_TYPE_INT32
;
322 (void) nvpair_value_int32(nvp
, &se_value
->value
.sv_int32
);
324 case DATA_TYPE_UINT32
:
325 se_value
->value_type
= SE_DATA_TYPE_UINT32
;
326 (void) nvpair_value_uint32(nvp
, &se_value
->value
.sv_uint32
);
328 case DATA_TYPE_INT64
:
329 se_value
->value_type
= SE_DATA_TYPE_INT64
;
330 (void) nvpair_value_int64(nvp
, &se_value
->value
.sv_int64
);
332 case DATA_TYPE_UINT64
:
333 se_value
->value_type
= SE_DATA_TYPE_UINT64
;
334 (void) nvpair_value_uint64(nvp
, &se_value
->value
.sv_uint64
);
336 case DATA_TYPE_STRING
:
337 se_value
->value_type
= SE_DATA_TYPE_STRING
;
338 (void) nvpair_value_string(nvp
, &se_value
->value
.sv_string
);
340 case DATA_TYPE_BYTE_ARRAY
:
341 se_value
->value_type
= SE_DATA_TYPE_BYTES
;
342 (void) nvpair_value_byte_array(nvp
,
343 &se_value
->value
.sv_bytes
.data
,
344 (uint_t
*)&se_value
->value
.sv_bytes
.size
);
346 case DATA_TYPE_HRTIME
:
347 se_value
->value_type
= SE_DATA_TYPE_TIME
;
348 (void) nvpair_value_hrtime(nvp
, &se_value
->value
.sv_time
);
357 * sysevent_attr_next - Get next attribute in event attribute list
360 sysevent_attr_next(sysevent_t
*ev
, sysevent_attr_t
*attr
)
363 nvpair_t
*nvp
= attr
;
365 /* all user visible sysevent_t's are unpacked */
366 assert(SE_FLAG(ev
) != SE_PACKED_BUF
);
368 if (SE_ATTR_PTR(ev
) == (uint64_t)0) {
372 nvl
= (nvlist_t
*)(uintptr_t)SE_ATTR_PTR(ev
);
373 return (nvlist_next_nvpair(nvl
, nvp
));
377 * sysevent_lookup_attr - Lookup attribute by name and datatype.
380 sysevent_lookup_attr(sysevent_t
*ev
, char *name
, int datatype
,
381 sysevent_value_t
*se_value
)
386 assert(SE_FLAG(ev
) != SE_PACKED_BUF
);
388 if (SE_ATTR_PTR(ev
) == (uint64_t)0) {
393 * sysevent matches on both name and datatype
394 * nvlist_look mataches name only. So we walk
395 * nvlist manually here.
397 nvl
= (nvlist_t
*)(uintptr_t)SE_ATTR_PTR(ev
);
398 nvp
= nvlist_next_nvpair(nvl
, NULL
);
400 if ((strcmp(name
, nvpair_name(nvp
)) == 0) &&
401 (sysevent_attr_value(nvp
, se_value
) == 0) &&
402 (se_value
->value_type
== datatype
))
404 nvp
= nvlist_next_nvpair(nvl
, nvp
);
409 /* Routines to extract event header information */
412 * sysevent_get_class - Get class id
415 sysevent_get_class(sysevent_t
*ev
)
417 return (SE_CLASS(ev
));
421 * sysevent_get_subclass - Get subclass id
424 sysevent_get_subclass(sysevent_t
*ev
)
426 return (SE_SUBCLASS(ev
));
430 * sysevent_get_class_name - Get class name string
433 sysevent_get_class_name(sysevent_t
*ev
)
435 return (SE_CLASS_NAME(ev
));
446 * sysevent_get_pub - Get publisher name string
449 sysevent_get_pub(sysevent_t
*ev
)
451 return (SE_PUB_NAME(ev
));
455 * Get the requested string pointed by the token.
457 * Return NULL if not found or for insufficient memory.
460 parse_pub_id(sysevent_t
*ev
, se_pub_id_t token
)
463 char *pub_id
, *pub_element
, *str
, *next
;
465 next
= pub_id
= strdup(sysevent_get_pub(ev
));
466 for (i
= 0; i
<= token
; ++i
) {
467 str
= strtok_r(next
, ":", &next
);
474 pub_element
= strdup(str
);
476 return (pub_element
);
480 * Return a pointer to the string following the token
482 * Note: This is a dedicated function for parsing
483 * publisher strings and not for general purpose.
486 pub_idx(const char *pstr
, int token
)
490 for (i
= 1; i
<= token
; i
++) {
491 if ((pstr
= index(pstr
, ':')) == NULL
)
496 /* String might be empty */
498 if (*pstr
== '\0' || *pstr
== ':')
505 sysevent_get_vendor_name(sysevent_t
*ev
)
507 return (parse_pub_id(ev
, PUB_VEND
));
511 sysevent_get_pub_name(sysevent_t
*ev
)
513 return (parse_pub_id(ev
, PUB_NAME
));
517 * Provide the pid encoded in the publisher string
518 * w/o allocating any resouces.
521 sysevent_get_pid(sysevent_t
*ev
, pid_t
*pid
)
523 const char *part_str
;
524 const char *pub_str
= sysevent_get_pub(ev
);
526 *pid
= (pid_t
)SE_KERN_PID
;
528 part_str
= pub_idx(pub_str
, PUB_KEYWD
);
529 if (part_str
!= NULL
&& strstr(part_str
, SE_KERN_PUB
) != NULL
)
532 if ((part_str
= pub_idx(pub_str
, PUB_PID
)) == NULL
)
535 *pid
= (pid_t
)atoi(part_str
);
539 * sysevent_get_subclass_name - Get subclass name string
542 sysevent_get_subclass_name(sysevent_t
*ev
)
544 return (SE_SUBCLASS_NAME(ev
));
548 * sysevent_get_seq - Get event sequence id
551 sysevent_get_seq(sysevent_t
*ev
)
557 * sysevent_get_time - Get event timestamp
560 sysevent_get_time(sysevent_t
*ev
, hrtime_t
*etime
)
562 *etime
= SE_TIME(ev
);
566 * sysevent_get_size - Get event buffer size
569 sysevent_get_size(sysevent_t
*ev
)
571 return ((size_t)SE_SIZE(ev
));
575 * The following routines are used by devfsadm_mod.c to propagate event
576 * buffers to devfsadmd. These routines will serve as the basis for
577 * event channel publication and subscription.
581 * sysevent_alloc_event -
582 * allocate a sysevent buffer for sending through an established event
586 sysevent_alloc_event(char *class, char *subclass
, char *vendor
, char *pub_name
,
589 int class_sz
, subclass_sz
, pub_sz
;
593 if ((class == NULL
) || (subclass
== NULL
) || (vendor
== NULL
) ||
594 (pub_name
== NULL
)) {
599 class_sz
= strlen(class) + 1;
600 subclass_sz
= strlen(subclass
) + 1;
601 if ((class_sz
> MAX_CLASS_LEN
) ||
602 (subclass_sz
> MAX_SUBCLASS_LEN
)) {
608 * Calculate the publisher size plus string seperators and maximum
611 pub_sz
= strlen(vendor
) + sizeof (SE_USR_PUB
) + strlen(pub_name
) + 14;
612 if (pub_sz
> MAX_PUB_LEN
) {
616 pub_id
= malloc(pub_sz
);
617 if (pub_id
== NULL
) {
621 if (snprintf(pub_id
, pub_sz
, "%s:%s%s:%d", vendor
, SE_USR_PUB
,
622 pub_name
, (int)getpid()) >= pub_sz
) {
627 pub_sz
= strlen(pub_id
) + 1;
629 ev
= sysevent_alloc(class, class_sz
, subclass
, subclass_sz
,
630 pub_id
, pub_sz
, attr_list
);
641 * se_unpack - unpack nvlist to a searchable list.
642 * If already unpacked, will do a dup.
645 se_unpack(sysevent_t
*ev
)
649 nvlist_t
*attrp
= NULL
;
650 uint64_t attr_offset
;
653 assert(SE_FLAG(ev
) == SE_PACKED_BUF
);
655 /* Copy event header information */
656 attr_offset
= SE_ATTR_OFF(ev
);
657 copy
= calloc(1, attr_offset
);
660 bcopy(ev
, copy
, attr_offset
);
661 SE_FLAG(copy
) = 0; /* unpacked */
664 attr
= (caddr_t
)ev
+ attr_offset
;
665 attr_len
= SE_SIZE(ev
) - attr_offset
;
669 if (nvlist_unpack(attr
, attr_len
, &attrp
, 0) != 0) {
674 SE_ATTR_PTR(copy
) = (uintptr_t)attrp
;
679 * se_print - Prints elements in an event buffer
682 se_print(FILE *fp
, sysevent_t
*ev
)
687 nvlist_t
*attr_list
= NULL
;
689 (void) sysevent_get_time(ev
, &hrt
);
690 (void) fprintf(fp
, "received sysevent id = 0X%llx:%llx\n",
691 hrt
, (longlong_t
)sysevent_get_seq(ev
));
692 (void) fprintf(fp
, "\tclass = %s\n", sysevent_get_class_name(ev
));
693 (void) fprintf(fp
, "\tsubclass = %s\n", sysevent_get_subclass_name(ev
));
694 if ((vendor
= sysevent_get_vendor_name(ev
)) != NULL
) {
695 (void) fprintf(fp
, "\tvendor = %s\n", vendor
);
698 if ((pub
= sysevent_get_pub_name(ev
)) != NULL
) {
699 sysevent_get_pid(ev
, &pid
);
700 (void) fprintf(fp
, "\tpublisher = %s:%d\n", pub
, (int)pid
);
704 if (sysevent_get_attr_list(ev
, &attr_list
) == 0 && attr_list
!= NULL
) {
705 nvlist_print(fp
, attr_list
);
706 nvlist_free(attr_list
);
711 * The following routines are provided to support establishment and use
712 * of sysevent channels. A sysevent channel is established between
713 * publishers and subscribers of sysevents for an agreed upon channel name.
714 * These routines currently support sysevent channels between user-level
715 * applications running on the same system.
717 * Sysevent channels may be created by a single publisher or subscriber process.
718 * Once established, up to MAX_SUBSRCIBERS subscribers may subscribe interest in
719 * receiving sysevent notifications on the named channel. At present, only
720 * one publisher is allowed per sysevent channel.
722 * The registration information for each channel is kept in the kernel. A
723 * kernel-based registration was chosen for persistence and reliability reasons.
724 * If either a publisher or a subscriber exits for any reason, the channel
725 * properties are maintained until all publishers and subscribers have exited.
726 * Additionally, an in-kernel registration allows the API to be extended to
727 * include kernel subscribers as well as userland subscribers in the future.
729 * To insure fast lookup of subscriptions, a cached copy of the registration
730 * is kept and maintained for the publisher process. Updates are made
731 * everytime a change is made in the kernel. Changes to the registration are
732 * expected to be infrequent.
734 * Channel communication between publisher and subscriber processes is
735 * implemented primarily via doors. Each publisher creates a door for
736 * registration notifications and each subscriber creates a door for event
739 * Most of these routines are used by syseventd(8), the sysevent publisher
740 * for the syseventd channel. Processes wishing to receive sysevent
741 * notifications from syseventd may use a set of public
742 * APIs designed to subscribe to syseventd sysevents. The subscription
743 * APIs are implemented in accordance with PSARC/2001/076.
748 * Door handlers for the channel subscribers
752 * subscriber_event_handler - generic event handling wrapper for subscribers
753 * This handler is used to process incoming sysevent
754 * notifications from channel publishers.
755 * It is created as a seperate thread in each subscriber
756 * process per subscription.
759 subscriber_event_handler(sysevent_handle_t
*shp
)
761 subscriber_priv_t
*sub_info
;
762 sysevent_queue_t
*evqp
;
764 sub_info
= (subscriber_priv_t
*)SH_PRIV_DATA(shp
);
766 /* See hack alert in sysevent_bind_subscriber_cmn */
767 if (sub_info
->sp_handler_tid
== 0)
768 sub_info
->sp_handler_tid
= thr_self();
770 (void) mutex_lock(&sub_info
->sp_qlock
);
772 while (sub_info
->sp_evq_head
== NULL
&& SH_BOUND(shp
)) {
773 (void) cond_wait(&sub_info
->sp_cv
, &sub_info
->sp_qlock
);
775 evqp
= sub_info
->sp_evq_head
;
777 (void) mutex_unlock(&sub_info
->sp_qlock
);
778 (void) sub_info
->sp_func(evqp
->sq_ev
);
779 (void) mutex_lock(&sub_info
->sp_qlock
);
780 sub_info
->sp_evq_head
= sub_info
->sp_evq_head
->sq_next
;
783 evqp
= sub_info
->sp_evq_head
;
785 if (!SH_BOUND(shp
)) {
786 (void) mutex_unlock(&sub_info
->sp_qlock
);
795 * Data structure used to communicate event subscription cache updates
796 * to publishers via a registration door
806 * event_deliver_service - generic event delivery service routine. This routine
807 * is called in response to a door call to post an event.
812 event_deliver_service(void *cookie
, char *args
, size_t alen
,
813 door_desc_t
*ddp
, uint_t ndid
)
816 subscriber_priv_t
*sub_info
;
817 sysevent_handle_t
*shp
;
818 sysevent_queue_t
*new_eq
;
820 if (args
== NULL
|| alen
< sizeof (uint32_t)) {
822 goto return_from_door
;
825 /* Publisher checking on subscriber */
826 if (alen
== sizeof (uint32_t)) {
828 goto return_from_door
;
831 shp
= (sysevent_handle_t
*)cookie
;
834 goto return_from_door
;
838 * Mustn't block if we are trying to update the registration with
841 if (mutex_trylock(SH_LOCK(shp
)) != 0) {
843 goto return_from_door
;
846 if (!SH_BOUND(shp
)) {
848 (void) mutex_unlock(SH_LOCK(shp
));
849 goto return_from_door
;
852 sub_info
= (subscriber_priv_t
*)SH_PRIV_DATA(shp
);
853 if (sub_info
== NULL
) {
855 (void) mutex_unlock(SH_LOCK(shp
));
856 goto return_from_door
;
859 new_eq
= (sysevent_queue_t
*)calloc(1,
860 sizeof (sysevent_queue_t
));
861 if (new_eq
== NULL
) {
863 (void) mutex_unlock(SH_LOCK(shp
));
864 goto return_from_door
;
868 * Allocate and copy the event buffer into the subscriber's
871 new_eq
->sq_ev
= calloc(1, alen
);
872 if (new_eq
->sq_ev
== NULL
) {
875 (void) mutex_unlock(SH_LOCK(shp
));
876 goto return_from_door
;
878 (void) bcopy(args
, new_eq
->sq_ev
, alen
);
880 (void) mutex_lock(&sub_info
->sp_qlock
);
881 if (sub_info
->sp_evq_head
== NULL
) {
882 sub_info
->sp_evq_head
= new_eq
;
884 sub_info
->sp_evq_tail
->sq_next
= new_eq
;
886 sub_info
->sp_evq_tail
= new_eq
;
888 (void) cond_signal(&sub_info
->sp_cv
);
889 (void) mutex_unlock(&sub_info
->sp_qlock
);
890 (void) mutex_unlock(SH_LOCK(shp
));
893 (void) door_return((void *)&ret
, sizeof (ret
), NULL
, 0);
894 (void) door_return(NULL
, 0, NULL
, 0);
898 * Sysevent subscription information is maintained in the kernel. Updates
899 * to the in-kernel registration database is expected to be infrequent and
900 * offers consistency for publishers and subscribers that may come and go
901 * for a given channel.
903 * To expedite registration lookups by publishers, a cached copy of the
904 * kernel registration database is kept per-channel. Caches are invalidated
905 * and refreshed upon state changes to the in-kernel registration database.
907 * To prevent stale subscriber data, publishers may remove subsriber
908 * registrations from the in-kernel registration database in the event
909 * that a particular subscribing process is unresponsive.
911 * The following routines provide a mechanism to update publisher and subscriber
912 * information for a specified channel.
916 * clnt_deliver_event - Deliver an event through the consumer's event
919 * Returns -1 if message not delivered. With errno set to cause of error.
920 * Returns 0 for success with the results returned in posting buffer.
923 clnt_deliver_event(int service_door
, void *data
, size_t datalen
,
924 void *result
, size_t rlen
)
929 door_arg
.rbuf
= result
;
930 door_arg
.rsize
= rlen
;
931 door_arg
.data_ptr
= data
;
932 door_arg
.data_size
= datalen
;
933 door_arg
.desc_ptr
= NULL
;
934 door_arg
.desc_num
= 0;
939 while ((error
= door_call(service_door
, &door_arg
)) != 0) {
940 if (errno
== EAGAIN
|| errno
== EINTR
) {
952 update_publisher_cache(subscriber_priv_t
*sub_info
, int update_op
,
953 uint32_t sub_id
, size_t datasz
, uchar_t
*data
)
957 struct reg_args
*rargs
;
959 rargs
= (struct reg_args
*)calloc(1, sizeof (struct reg_args
) +
966 rargs
->ra_sub_id
= sub_id
;
967 rargs
->ra_op
= update_op
;
968 bcopy(data
, (char *)&rargs
->ra_buf_ptr
, datasz
);
970 pub_fd
= open(sub_info
->sp_door_name
, O_RDONLY
);
971 (void) clnt_deliver_event(pub_fd
, (void *)rargs
,
972 sizeof (struct reg_args
) + datasz
, &result
, sizeof (result
));
973 (void) close(pub_fd
);
986 * update_kernel_registration - update the in-kernel registration for the
990 update_kernel_registration(sysevent_handle_t
*shp
, int update_type
,
991 int update_op
, uint32_t *sub_id
, size_t datasz
, uchar_t
*data
)
994 char *channel_name
= SH_CHANNEL_NAME(shp
);
997 udata
.ps_channel_name_len
= strlen(channel_name
) + 1;
998 udata
.ps_op
= update_op
;
999 udata
.ps_type
= update_type
;
1000 udata
.ps_buflen
= datasz
;
1001 udata
.ps_id
= *sub_id
;
1003 if ((error
= modctl(MODEVENTS
, (uintptr_t)MODEVENTS_REGISTER_EVENT
,
1004 (uintptr_t)channel_name
, (uintptr_t)data
, (uintptr_t)&udata
, 0))
1009 *sub_id
= udata
.ps_id
;
1015 * get_kernel_registration - get the current subscriber registration for
1019 get_kernel_registration(char *channel_name
, uint32_t class_id
)
1025 nvlbuf
= calloc(1, MAX_SUBSCRIPTION_SZ
);
1026 if (nvlbuf
== NULL
) {
1030 udata
.ps_buflen
= MAX_SUBSCRIPTION_SZ
;
1031 udata
.ps_channel_name_len
= strlen(channel_name
) + 1;
1032 udata
.ps_id
= class_id
;
1033 udata
.ps_op
= SE_GET_REGISTRATION
;
1034 udata
.ps_type
= PUBLISHER
;
1036 if (modctl(MODEVENTS
, (uintptr_t)MODEVENTS_REGISTER_EVENT
,
1037 (uintptr_t)channel_name
, (uintptr_t)nvlbuf
, (uintptr_t)&udata
, 0)
1040 /* Need a bigger buffer to hold channel registration */
1041 if (errno
== EAGAIN
) {
1043 nvlbuf
= calloc(1, udata
.ps_buflen
);
1048 if (modctl(MODEVENTS
,
1049 (uintptr_t)MODEVENTS_REGISTER_EVENT
,
1050 (uintptr_t)channel_name
, (uintptr_t)nvlbuf
,
1051 (uintptr_t)&udata
, 0) != 0) {
1061 if (nvlist_unpack(nvlbuf
, udata
.ps_buflen
, &nvl
, 0) != 0) {
1071 * The following routines provide a mechanism for publishers to maintain
1072 * subscriber information.
1076 dealloc_subscribers(sysevent_handle_t
*shp
)
1079 subscriber_data_t
*sub
;
1081 for (i
= 1; i
<= MAX_SUBSCRIBERS
; ++i
) {
1082 sub
= SH_SUBSCRIBER(shp
, i
);
1084 free(sub
->sd_door_name
);
1087 SH_SUBSCRIBER(shp
, i
) = NULL
;
1093 alloc_subscriber(sysevent_handle_t
*shp
, uint32_t sub_id
, int oflag
)
1095 subscriber_data_t
*sub
;
1096 char door_name
[MAXPATHLEN
];
1098 if (SH_SUBSCRIBER(shp
, sub_id
) != NULL
) {
1102 /* Allocate and initialize the subscriber data */
1103 sub
= (subscriber_data_t
*)calloc(1,
1104 sizeof (subscriber_data_t
));
1108 if (snprintf(door_name
, MAXPATHLEN
, "%s/%d",
1109 SH_CHANNEL_PATH(shp
), sub_id
) >= MAXPATHLEN
) {
1114 sub
->sd_flag
= ACTIVE
;
1115 sub
->sd_door_name
= strdup(door_name
);
1116 if (sub
->sd_door_name
== NULL
) {
1121 SH_SUBSCRIBER(shp
, sub_id
) = sub
;
1127 * The following routines are used to update and maintain the registration cache
1128 * for a particular sysevent channel.
1132 hash_func(const char *s
)
1134 uint32_t result
= 0;
1137 while (*s
!= '\0') {
1139 result
+= (uint32_t)*s
++;
1140 g
= result
& 0xf0000000;
1151 cache_find_subclass(class_lst_t
*c_list
, char *subclass
)
1153 subclass_lst_t
*sc_list
;
1158 sc_list
= c_list
->cl_subclass_list
;
1160 while (sc_list
!= NULL
) {
1161 if (strcmp(sc_list
->sl_name
, subclass
) == 0) {
1164 sc_list
= sc_list
->sl_next
;
1171 static class_lst_t
*
1172 cache_find_class(sysevent_handle_t
*shp
, char *class)
1175 class_lst_t
*c_list
;
1176 class_lst_t
**class_hash
= SH_CLASS_HASH(shp
);
1178 if (strcmp(class, EC_ALL
) == 0) {
1179 return (class_hash
[0]);
1182 index
= CLASS_HASH(class);
1183 c_list
= class_hash
[index
];
1184 while (c_list
!= NULL
) {
1185 if (strcmp(class, c_list
->cl_name
) == 0) {
1188 c_list
= c_list
->cl_next
;
1195 cache_insert_subclass(class_lst_t
*c_list
, char **subclass_names
,
1196 int subclass_num
, uint32_t sub_id
)
1199 subclass_lst_t
*sc_list
;
1201 for (i
= 0; i
< subclass_num
; ++i
) {
1202 if ((sc_list
= cache_find_subclass(c_list
, subclass_names
[i
]))
1204 sc_list
->sl_num
[sub_id
] = 1;
1206 sc_list
= (subclass_lst_t
*)calloc(1,
1207 sizeof (subclass_lst_t
));
1208 if (sc_list
== NULL
)
1211 sc_list
->sl_name
= strdup(subclass_names
[i
]);
1212 if (sc_list
->sl_name
== NULL
) {
1217 sc_list
->sl_num
[sub_id
] = 1;
1218 sc_list
->sl_next
= c_list
->cl_subclass_list
;
1219 c_list
->cl_subclass_list
= sc_list
;
1227 cache_insert_class(sysevent_handle_t
*shp
, char *class,
1228 char **subclass_names
, int subclass_num
, uint32_t sub_id
)
1230 class_lst_t
*c_list
;
1232 if (strcmp(class, EC_ALL
) == 0) {
1233 char *subclass_all
= EC_SUB_ALL
;
1235 (void) cache_insert_subclass(SH_CLASS_HASH(shp
)[0],
1236 (char **)&subclass_all
, 1, sub_id
);
1240 /* New class, add to the registration cache */
1241 if ((c_list
= cache_find_class(shp
, class)) == NULL
) {
1243 c_list
= (class_lst_t
*)calloc(1, sizeof (class_lst_t
));
1244 if (c_list
== NULL
) {
1247 c_list
->cl_name
= strdup(class);
1248 if (c_list
->cl_name
== NULL
) {
1253 c_list
->cl_subclass_list
= (subclass_lst_t
*)
1254 calloc(1, sizeof (subclass_lst_t
));
1255 if (c_list
->cl_subclass_list
== NULL
) {
1256 free(c_list
->cl_name
);
1260 c_list
->cl_subclass_list
->sl_name
= strdup(EC_SUB_ALL
);
1261 if (c_list
->cl_subclass_list
->sl_name
== NULL
) {
1262 free(c_list
->cl_subclass_list
);
1263 free(c_list
->cl_name
);
1267 c_list
->cl_next
= SH_CLASS_HASH(shp
)[CLASS_HASH(class)];
1268 SH_CLASS_HASH(shp
)[CLASS_HASH(class)] = c_list
;
1272 /* Update the subclass list */
1273 if (cache_insert_subclass(c_list
, subclass_names
, subclass_num
,
1281 cache_remove_all_class(sysevent_handle_t
*shp
, uint32_t sub_id
)
1284 class_lst_t
*c_list
;
1285 subclass_lst_t
*sc_list
;
1287 for (i
= 0; i
< CLASS_HASH_SZ
+ 1; ++i
) {
1288 c_list
= SH_CLASS_HASH(shp
)[i
];
1289 while (c_list
!= NULL
) {
1290 sc_list
= c_list
->cl_subclass_list
;
1291 while (sc_list
!= NULL
) {
1292 sc_list
->sl_num
[sub_id
] = 0;
1293 sc_list
= sc_list
->sl_next
;
1295 c_list
= c_list
->cl_next
;
1301 cache_remove_class(sysevent_handle_t
*shp
, char *class, uint32_t sub_id
)
1303 class_lst_t
*c_list
;
1304 subclass_lst_t
*sc_list
;
1306 if (strcmp(class, EC_ALL
) == 0) {
1307 cache_remove_all_class(shp
, sub_id
);
1311 if ((c_list
= cache_find_class(shp
, class)) == NULL
) {
1315 sc_list
= c_list
->cl_subclass_list
;
1316 while (sc_list
!= NULL
) {
1317 sc_list
->sl_num
[sub_id
] = 0;
1318 sc_list
= sc_list
->sl_next
;
1323 free_cached_registration(sysevent_handle_t
*shp
)
1326 class_lst_t
*clist
, *next_clist
;
1327 subclass_lst_t
*sc_list
, *next_sc
;
1329 for (i
= 0; i
< CLASS_HASH_SZ
+ 1; i
++) {
1330 clist
= SH_CLASS_HASH(shp
)[i
];
1331 while (clist
!= NULL
) {
1332 sc_list
= clist
->cl_subclass_list
;
1333 while (sc_list
!= NULL
) {
1334 free(sc_list
->sl_name
);
1335 next_sc
= sc_list
->sl_next
;
1339 free(clist
->cl_name
);
1340 next_clist
= clist
->cl_next
;
1344 SH_CLASS_HASH(shp
)[i
] = NULL
;
1349 create_cached_registration(sysevent_handle_t
*shp
,
1350 class_lst_t
**class_hash
)
1352 int i
, j
, new_class
;
1355 uchar_t
*subscribers
;
1359 subclass_lst_t
*sc_list
;
1361 for (i
= 0; i
< CLASS_HASH_SZ
+ 1; ++i
) {
1363 if ((nvl
= get_kernel_registration(SH_CHANNEL_NAME(shp
), i
))
1365 if (errno
== ENOENT
) {
1366 class_hash
[i
] = NULL
;
1375 if ((nvpair
= nvlist_next_nvpair(nvl
, nvpair
)) == NULL
) {
1381 /* Extract the class name from the nvpair */
1382 if (nvpair_value_string(nvpair
, &class_name
) != 0) {
1385 clist
= (class_lst_t
*)
1386 calloc(1, sizeof (class_lst_t
));
1387 if (clist
== NULL
) {
1391 clist
->cl_name
= strdup(class_name
);
1392 if (clist
->cl_name
== NULL
) {
1398 * Extract the subclass name and registration
1401 if ((nvpair
= nvlist_next_nvpair(nvl
, nvpair
))
1403 free(clist
->cl_name
);
1408 clist
->cl_next
= class_hash
[i
];
1409 class_hash
[i
] = clist
;
1413 sc_list
= (subclass_lst_t
*)calloc(1,
1414 sizeof (subclass_lst_t
));
1415 if (sc_list
== NULL
) {
1419 sc_list
->sl_next
= clist
->cl_subclass_list
;
1420 clist
->cl_subclass_list
= sc_list
;
1422 sc_list
->sl_name
= strdup(nvpair_name(nvpair
));
1423 if (sc_list
->sl_name
== NULL
) {
1427 if (nvpair_value_byte_array(nvpair
,
1428 &subscribers
, &num_elem
) != 0) {
1431 bcopy(subscribers
, (uchar_t
*)sc_list
->sl_num
,
1432 MAX_SUBSCRIBERS
+ 1);
1434 for (j
= 1; j
<= MAX_SUBSCRIBERS
; ++j
) {
1435 if (sc_list
->sl_num
[j
] == 0)
1438 if (alloc_subscriber(shp
, j
, 1) != 0) {
1444 * Check next nvpair - either subclass or
1447 if ((nvpair
= nvlist_next_nvpair(nvl
, nvpair
))
1451 } else if (strcmp(nvpair_name(nvpair
),
1462 dealloc_subscribers(shp
);
1463 free_cached_registration(shp
);
1470 * cache_update_service - generic event publisher service routine. This routine
1471 * is called in response to a registration cache update.
1476 cache_update_service(void *cookie
, char *args
, size_t alen
,
1477 door_desc_t
*ddp
, uint_t ndid
)
1481 char *class, **event_list
;
1485 nvpair_t
*nvpair
= NULL
;
1486 struct reg_args
*rargs
;
1487 sysevent_handle_t
*shp
;
1488 subscriber_data_t
*sub
;
1490 if (alen
< sizeof (struct reg_args
) || cookie
== NULL
) {
1492 goto return_from_door
;
1495 /* LINTED: E_BAD_PTR_CAST_ALIGN */
1496 rargs
= (struct reg_args
*)args
;
1497 shp
= (sysevent_handle_t
*)cookie
;
1499 datalen
= alen
- sizeof (struct reg_args
);
1500 sub_id
= rargs
->ra_sub_id
;
1502 (void) mutex_lock(SH_LOCK(shp
));
1504 switch (rargs
->ra_op
) {
1506 class = (char *)&rargs
->ra_buf_ptr
;
1507 cache_remove_class(shp
, (char *)class,
1510 case SE_UNBIND_REGISTRATION
:
1512 sub
= SH_SUBSCRIBER(shp
, sub_id
);
1516 free(sub
->sd_door_name
);
1518 cache_remove_class(shp
, EC_ALL
, sub_id
);
1519 SH_SUBSCRIBER(shp
, sub_id
) = NULL
;
1522 case SE_BIND_REGISTRATION
:
1524 /* New subscriber */
1525 if (alloc_subscriber(shp
, sub_id
, 0) != 0) {
1532 if (SH_SUBSCRIBER(shp
, sub_id
) == NULL
) {
1536 /* Get new registration data */
1537 if (nvlist_unpack((char *)&rargs
->ra_buf_ptr
, datalen
,
1542 if ((nvpair
= nvlist_next_nvpair(nvl
, nvpair
)) == NULL
) {
1547 if (nvpair_value_string_array(nvpair
, &event_list
, &num_elem
)
1553 class = nvpair_name(nvpair
);
1555 ret
= cache_insert_class(shp
, class,
1556 event_list
, num_elem
, sub_id
);
1558 cache_remove_class(shp
, class, sub_id
);
1568 /* Cleanup stale subscribers */
1569 sysevent_cleanup_subscribers(shp
);
1575 (void) mutex_unlock(SH_LOCK(shp
));
1578 (void) door_return((void *)&ret
, sizeof (ret
), NULL
, 0);
1579 (void) door_return(NULL
, 0, NULL
, 0);
1583 * sysevent_send_event -
1584 * Send an event via the communication channel associated with the sysevent
1585 * handle. Event notifications are broadcast to all subscribers based upon
1586 * the event class and subclass. The handle must have been previously
1587 * allocated and bound by
1588 * sysevent_open_channel() and sysevent_bind_publisher()
1591 sysevent_send_event(sysevent_handle_t
*shp
, sysevent_t
*ev
)
1593 int i
, error
, sub_fd
, result
= 0;
1594 int deliver_error
= 0;
1595 int subscribers_sent
= 0;
1596 int want_resend
, resend_cnt
= 0;
1597 char *event_class
, *event_subclass
;
1598 uchar_t
*all_class_subscribers
, *all_subclass_subscribers
;
1599 uchar_t
*subclass_subscribers
;
1600 subscriber_data_t
*sub
;
1601 subclass_lst_t
*sc_lst
;
1603 /* Check for proper registration */
1604 event_class
= sysevent_get_class_name(ev
);
1605 event_subclass
= sysevent_get_subclass_name(ev
);
1607 (void) mutex_lock(SH_LOCK(shp
));
1612 if (!SH_BOUND(shp
)) {
1613 (void) mutex_unlock(SH_LOCK(shp
));
1618 /* Find all subscribers for this event class/subclass */
1619 sc_lst
= cache_find_subclass(
1620 cache_find_class(shp
, EC_ALL
), EC_SUB_ALL
);
1621 all_class_subscribers
= sc_lst
->sl_num
;
1623 sc_lst
= cache_find_subclass(
1624 cache_find_class(shp
, event_class
), EC_SUB_ALL
);
1626 all_subclass_subscribers
= sc_lst
->sl_num
;
1628 all_subclass_subscribers
= NULL
;
1630 sc_lst
= cache_find_subclass(
1631 cache_find_class(shp
, event_class
), event_subclass
);
1633 subclass_subscribers
= sc_lst
->sl_num
;
1635 subclass_subscribers
= NULL
;
1637 /* Send event buffer to all valid subscribers */
1638 for (i
= 1; i
<= MAX_SUBSCRIBERS
; ++i
) {
1639 if ((all_class_subscribers
[i
] |
1640 (all_subclass_subscribers
&& all_subclass_subscribers
[i
]) |
1641 (subclass_subscribers
&& subclass_subscribers
[i
])) == 0)
1644 sub
= SH_SUBSCRIBER(shp
, i
);
1645 assert(sub
!= NULL
);
1647 /* Check for active subscriber */
1648 if (!(sub
->sd_flag
& ACTIVE
)) {
1649 dprint("sysevent_send_event: subscriber %d inactive\n",
1654 /* Process only resend requests */
1655 if (resend_cnt
> 0 && !(sub
->sd_flag
& SEND_AGAIN
)) {
1659 if ((sub_fd
= open(sub
->sd_door_name
, O_RDONLY
)) == -1) {
1660 dprint("sysevent_send_event: Failed to open "
1661 "%s: %s\n", sub
->sd_door_name
, strerror(errno
));
1665 error
= clnt_deliver_event(sub_fd
, ev
,
1666 sysevent_get_size(ev
), &result
, sizeof (result
));
1668 (void) close(sub_fd
);
1670 /* Successful door call */
1673 /* Subscriber requested EAGAIN */
1675 if (resend_cnt
> SE_MAX_RETRY_LIMIT
) {
1679 dprint("sysevent_send_event: resend "
1680 "requested for %d\n", i
);
1681 sub
->sd_flag
|= SEND_AGAIN
;
1684 /* Bad sysevent handle for subscriber */
1687 dprint("sysevent_send_event: Bad sysevent "
1688 "handle for %s", sub
->sd_door_name
);
1692 /* Successful delivery */
1694 sub
->sd_flag
&= ~SEND_AGAIN
;
1698 dprint("sysevent_send_event: Failed door call "
1699 "to %s: %s: %d\n", sub
->sd_door_name
,
1700 strerror(errno
), result
);
1711 if (deliver_error
) {
1712 sysevent_cleanup_subscribers(shp
);
1713 (void) mutex_unlock(SH_LOCK(shp
));
1718 (void) mutex_unlock(SH_LOCK(shp
));
1720 if (subscribers_sent
== 0) {
1721 dprint("sysevent_send_event: No subscribers for %s:%s\n",
1722 event_class
, event_subclass
);
1731 * Common routine to establish an event channel through which an event
1732 * publisher or subscriber may post or receive events.
1734 static sysevent_handle_t
*
1735 sysevent_open_channel_common(const char *channel_path
)
1737 uint32_t sub_id
= 0;
1739 struct stat chan_stat
;
1740 sysevent_handle_t
*shp
;
1743 if (channel_path
== NULL
|| strlen(channel_path
) + 1 > MAXPATHLEN
) {
1748 if (mkdir(channel_path
, S_IRWXU
|S_IRGRP
|S_IXGRP
|S_IROTH
|S_IXOTH
) < 0) {
1749 if (errno
!= EEXIST
) {
1755 /* Check channel file permissions */
1756 if (stat(channel_path
, &chan_stat
) != 0) {
1757 dprint("sysevent_open_channel: Invalid permissions for channel "
1758 "%s\n", channel_path
);
1761 } else if (chan_stat
.st_uid
!= getuid() ||
1762 !S_ISDIR(chan_stat
.st_mode
)) {
1763 dprint("sysevent_open_channel: Invalid "
1764 "permissions for channel %s\n: %d:%d:%d", channel_path
,
1765 (int)chan_stat
.st_uid
, (int)chan_stat
.st_gid
,
1766 (int)chan_stat
.st_mode
);
1772 shp
= calloc(1, sizeof (sysevent_impl_hdl_t
));
1778 SH_CHANNEL_NAME(shp
) = NULL
;
1779 SH_CHANNEL_PATH(shp
) = strdup(channel_path
);
1780 if (SH_CHANNEL_PATH(shp
) == NULL
) {
1786 /* Extract the channel name */
1787 begin_path
= SH_CHANNEL_PATH(shp
);
1788 while (*begin_path
!= '\0' &&
1789 (begin_path
= strpbrk(begin_path
, "/")) != NULL
) {
1791 SH_CHANNEL_NAME(shp
) = begin_path
;
1794 if (update_kernel_registration(shp
, 0,
1795 SE_OPEN_REGISTRATION
, &sub_id
, 0, NULL
) != 0) {
1796 dprint("sysevent_open_channel: Failed for channel %s\n",
1797 SH_CHANNEL_NAME(shp
));
1798 free(SH_CHANNEL_PATH(shp
));
1804 (void) mutex_init(SH_LOCK(shp
), USYNC_THREAD
, NULL
);
1810 * Establish a sysevent channel for publication and subscription
1813 sysevent_open_channel(const char *channel
)
1815 int var_run_mounted
= 0;
1816 char full_channel
[MAXPATHLEN
+ 1];
1818 struct stat chan_stat
;
1821 if (channel
== NULL
) {
1827 * Check that /var/run is mounted as tmpfs before allowing a channel
1830 if ((fp
= fopen(MNTTAB
, "rF")) == NULL
) {
1837 while (getextmntent(fp
, &m
, sizeof (struct extmnttab
)) == 0) {
1838 if (strcmp(m
.mnt_mountp
, "/var/run") == 0 &&
1839 strcmp(m
.mnt_fstype
, "tmpfs") == 0) {
1840 var_run_mounted
= 1;
1846 if (!var_run_mounted
) {
1851 if (stat(CHAN_PATH
, &chan_stat
) < 0) {
1852 if (mkdir(CHAN_PATH
,
1853 S_IRWXU
|S_IRGRP
|S_IXGRP
|S_IROTH
|S_IXOTH
) < 0) {
1854 dprint("sysevent_open_channel: Unable "
1855 "to create channel directory %s:%s\n", CHAN_PATH
,
1857 if (errno
!= EEXIST
) {
1864 if (snprintf(full_channel
, MAXPATHLEN
, "%s/%s", CHAN_PATH
, channel
) >=
1870 return (sysevent_open_channel_common(full_channel
));
1874 * Establish a sysevent channel for publication and subscription
1875 * Full path to the channel determined by the caller
1878 sysevent_open_channel_alt(const char *channel_path
)
1880 return (sysevent_open_channel_common(channel_path
));
1884 * sysevent_close_channel - Clean up resources associated with a previously
1885 * opened sysevent channel
1888 sysevent_close_channel(sysevent_handle_t
*shp
)
1891 uint32_t sub_id
= 0;
1897 (void) mutex_lock(SH_LOCK(shp
));
1898 if (SH_BOUND(shp
)) {
1899 (void) mutex_unlock(SH_LOCK(shp
));
1900 if (SH_TYPE(shp
) == PUBLISHER
)
1901 sysevent_unbind_publisher(shp
);
1902 else if (SH_TYPE(shp
) == SUBSCRIBER
)
1903 sysevent_unbind_subscriber(shp
);
1904 (void) mutex_lock(SH_LOCK(shp
));
1907 (void) update_kernel_registration(shp
, 0,
1908 SE_CLOSE_REGISTRATION
, &sub_id
, 0, NULL
);
1909 (void) mutex_unlock(SH_LOCK(shp
));
1911 free(SH_CHANNEL_PATH(shp
));
1917 * sysevent_bind_publisher - Bind an event publisher to an event channel
1920 sysevent_bind_publisher(sysevent_handle_t
*shp
)
1924 char door_name
[MAXPATHLEN
];
1926 struct stat reg_stat
;
1927 publisher_priv_t
*pub
;
1934 (void) mutex_lock(SH_LOCK(shp
));
1935 if (SH_BOUND(shp
)) {
1936 (void) mutex_unlock(SH_LOCK(shp
));
1941 if ((pub
= (publisher_priv_t
*)calloc(1, sizeof (publisher_priv_t
))) ==
1943 (void) mutex_unlock(SH_LOCK(shp
));
1947 SH_PRIV_DATA(shp
) = (void *)pub
;
1949 if (snprintf(door_name
, MAXPATHLEN
, "%s/%s",
1950 SH_CHANNEL_PATH(shp
), REG_DOOR
) >= MAXPATHLEN
) {
1952 (void) mutex_unlock(SH_LOCK(shp
));
1956 if ((SH_DOOR_NAME(shp
) = strdup(door_name
)) == NULL
) {
1958 (void) mutex_unlock(SH_LOCK(shp
));
1963 /* Only one publisher allowed per channel */
1964 if (stat(SH_DOOR_NAME(shp
), ®_stat
) != 0) {
1965 if (errno
!= ENOENT
) {
1972 * Remove door file for robustness.
1974 if (unlink(SH_DOOR_NAME(shp
)) != 0)
1975 dprint("sysevent_bind_publisher: Unlink of %s failed.\n",
1978 /* Open channel registration door */
1979 fd
= open(SH_DOOR_NAME(shp
), O_CREAT
|O_RDWR
,
1987 * Create the registration service for this publisher.
1989 if ((SH_DOOR_DESC(shp
) = door_create(cache_update_service
,
1990 (void *)shp
, DOOR_REFUSE_DESC
| DOOR_NO_CANCEL
)) == -1) {
1991 dprint("sysevent_bind_publisher: door create failed: "
1992 "%s\n", strerror(errno
));
1997 (void) fdetach(SH_DOOR_NAME(shp
));
1998 if (fattach(SH_DOOR_DESC(shp
), SH_DOOR_NAME(shp
)) != 0) {
1999 dprint("sysevent_bind_publisher: unable to "
2000 "bind event channel: fattach: %s\n",
2006 /* Bind this publisher in the kernel registration database */
2007 if (update_kernel_registration(shp
, PUBLISHER
,
2008 SE_BIND_REGISTRATION
, &pub_id
, 0, NULL
) != 0) {
2013 SH_ID(shp
) = pub_id
;
2015 SH_TYPE(shp
) = PUBLISHER
;
2018 /* Create the subscription registration cache */
2019 if (create_cached_registration(shp
, SH_CLASS_HASH(shp
)) != 0) {
2020 (void) update_kernel_registration(shp
,
2021 PUBLISHER
, SE_UNBIND_REGISTRATION
, &pub_id
, 0, NULL
);
2027 (void) mutex_unlock(SH_LOCK(shp
));
2033 (void) door_revoke(SH_DOOR_DESC(shp
));
2034 (void) fdetach(SH_DOOR_NAME(shp
));
2035 free(SH_DOOR_NAME(shp
));
2038 (void) mutex_unlock(SH_LOCK(shp
));
2043 static pthread_once_t xdoor_thrattr_once
= PTHREAD_ONCE_INIT
;
2044 static pthread_attr_t xdoor_thrattr
;
2047 xdoor_thrattr_init(void)
2049 (void) pthread_attr_init(&xdoor_thrattr
);
2050 (void) pthread_attr_setdetachstate(&xdoor_thrattr
,
2051 PTHREAD_CREATE_DETACHED
);
2052 (void) pthread_attr_setscope(&xdoor_thrattr
, PTHREAD_SCOPE_SYSTEM
);
2056 xdoor_server_create(door_info_t
*dip
, void *(*startf
)(void *),
2057 void *startfarg
, void *cookie
)
2059 struct sysevent_subattr_impl
*xsa
= cookie
;
2060 pthread_attr_t
*thrattr
;
2064 if (xsa
->xs_thrcreate
) {
2065 return (xsa
->xs_thrcreate(dip
, startf
, startfarg
,
2066 xsa
->xs_thrcreate_cookie
));
2069 if (xsa
->xs_thrattr
== NULL
) {
2070 (void) pthread_once(&xdoor_thrattr_once
, xdoor_thrattr_init
);
2071 thrattr
= &xdoor_thrattr
;
2073 thrattr
= xsa
->xs_thrattr
;
2076 (void) pthread_sigmask(SIG_SETMASK
, &xsa
->xs_sigmask
, &oset
);
2077 err
= pthread_create(NULL
, thrattr
, startf
, startfarg
);
2078 (void) pthread_sigmask(SIG_SETMASK
, &oset
, NULL
);
2080 return (err
== 0 ? 1 : -1);
2084 xdoor_server_setup(void *cookie
)
2086 struct sysevent_subattr_impl
*xsa
= cookie
;
2088 if (xsa
->xs_thrsetup
) {
2089 xsa
->xs_thrsetup(xsa
->xs_thrsetup_cookie
);
2091 (void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE
, NULL
);
2092 (void) pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED
, NULL
);
2097 sysevent_bind_subscriber_cmn(sysevent_handle_t
*shp
,
2098 void (*event_handler
)(sysevent_t
*ev
),
2099 sysevent_subattr_t
*subattr
)
2103 uint32_t sub_id
= 0;
2104 char door_name
[MAXPATHLEN
];
2105 subscriber_priv_t
*sub_info
;
2107 struct sysevent_subattr_impl
*xsa
=
2108 (struct sysevent_subattr_impl
*)subattr
;
2110 if (shp
== NULL
|| event_handler
== NULL
) {
2115 (void) mutex_lock(SH_LOCK(shp
));
2116 if (SH_BOUND(shp
)) {
2118 (void) mutex_unlock(SH_LOCK(shp
));
2122 if ((sub_info
= (subscriber_priv_t
*)calloc(1,
2123 sizeof (subscriber_priv_t
))) == NULL
) {
2125 (void) mutex_unlock(SH_LOCK(shp
));
2129 if (snprintf(door_name
, MAXPATHLEN
, "%s/%s",
2130 SH_CHANNEL_PATH(shp
), REG_DOOR
) >= MAXPATHLEN
) {
2133 (void) mutex_unlock(SH_LOCK(shp
));
2137 if ((sub_info
->sp_door_name
= strdup(door_name
)) == NULL
) {
2140 (void) mutex_unlock(SH_LOCK(shp
));
2143 (void) cond_init(&sub_info
->sp_cv
, USYNC_THREAD
, NULL
);
2144 (void) mutex_init(&sub_info
->sp_qlock
, USYNC_THREAD
, NULL
);
2145 sub_info
->sp_func
= event_handler
;
2147 /* Update the in-kernel registration */
2148 if (update_kernel_registration(shp
, SUBSCRIBER
,
2149 SE_BIND_REGISTRATION
, &sub_id
, 0, NULL
) != 0) {
2153 SH_ID(shp
) = sub_id
;
2155 if (snprintf(door_name
, MAXPATHLEN
, "%s/%d",
2156 SH_CHANNEL_PATH(shp
), sub_id
) >= MAXPATHLEN
) {
2160 if ((SH_DOOR_NAME(shp
) = strdup(door_name
)) == NULL
) {
2166 * Remove door file for robustness.
2168 if (unlink(SH_DOOR_NAME(shp
)) != 0)
2169 dprint("sysevent_bind_subscriber: Unlink of %s failed.\n",
2172 fd
= open(SH_DOOR_NAME(shp
), O_CREAT
|O_RDWR
, S_IREAD
|S_IWRITE
);
2179 * Create the sysevent door service for this client.
2180 * syseventd will use this door service to propagate
2181 * events to the client.
2183 if (subattr
== NULL
) {
2184 SH_DOOR_DESC(shp
) = door_create(event_deliver_service
,
2185 (void *)shp
, DOOR_REFUSE_DESC
| DOOR_NO_CANCEL
);
2187 SH_DOOR_DESC(shp
) = door_xcreate(event_deliver_service
,
2189 DOOR_REFUSE_DESC
| DOOR_NO_CANCEL
| DOOR_NO_DEPLETION_CB
,
2190 xdoor_server_create
, xdoor_server_setup
,
2191 (void *)subattr
, 1);
2194 if (SH_DOOR_DESC(shp
) == -1) {
2195 dprint("sysevent_bind_subscriber: door create failed: "
2196 "%s\n", strerror(errno
));
2201 (void) fdetach(SH_DOOR_NAME(shp
));
2202 if (fattach(SH_DOOR_DESC(shp
), SH_DOOR_NAME(shp
)) != 0) {
2208 if (update_publisher_cache(sub_info
, SE_BIND_REGISTRATION
,
2209 sub_id
, 0, NULL
) != 0) {
2211 (void) update_kernel_registration(shp
, SUBSCRIBER
,
2212 SE_UNBIND_REGISTRATION
, &sub_id
, 0, NULL
);
2217 SH_TYPE(shp
) = SUBSCRIBER
;
2218 SH_PRIV_DATA(shp
) = (void *)sub_info
;
2220 /* Create an event handler thread */
2221 if (xsa
== NULL
|| xsa
->xs_thrcreate
== NULL
) {
2222 created
= thr_create(NULL
, 0,
2223 (void *(*)(void *))subscriber_event_handler
,
2224 shp
, THR_BOUND
, &sub_info
->sp_handler_tid
) == 0;
2227 * A terrible hack. We will use the extended private
2228 * door thread creation function the caller passed in to
2229 * create the event handler thread. That function will
2230 * be called with our chosen thread start function and arg
2231 * instead of the usual libc-provided ones, but that's ok
2232 * as it is required to use them verbatim anyway. We will
2233 * pass a NULL door_info_t pointer to the function - so
2234 * callers depending on this hack had better be prepared
2235 * for that. All this allow the caller to rubberstamp
2236 * the created thread as it wishes. But we don't get
2237 * the created threadid with this, so we modify the
2238 * thread start function to stash it.
2241 created
= xsa
->xs_thrcreate(NULL
,
2242 (void *(*)(void *))subscriber_event_handler
,
2243 shp
, xsa
->xs_thrcreate_cookie
) == 1;
2251 (void) mutex_unlock(SH_LOCK(shp
));
2257 (void) door_revoke(SH_DOOR_DESC(shp
));
2258 (void) fdetach(SH_DOOR_NAME(shp
));
2259 (void) cond_destroy(&sub_info
->sp_cv
);
2260 (void) mutex_destroy(&sub_info
->sp_qlock
);
2261 free(sub_info
->sp_door_name
);
2264 (void) update_kernel_registration(shp
, SUBSCRIBER
,
2265 SE_UNBIND_REGISTRATION
, &sub_id
, 0, NULL
);
2268 if (SH_BOUND(shp
)) {
2269 (void) update_publisher_cache(sub_info
, SE_UNBIND_REGISTRATION
,
2271 free(SH_DOOR_NAME(shp
));
2274 (void) mutex_unlock(SH_LOCK(shp
));
2282 * sysevent_bind_subscriber - Bind an event receiver to an event channel
2285 sysevent_bind_subscriber(sysevent_handle_t
*shp
,
2286 void (*event_handler
)(sysevent_t
*ev
))
2288 return (sysevent_bind_subscriber_cmn(shp
, event_handler
, NULL
));
2292 * sysevent_bind_xsubscriber - Bind a subscriber using door_xcreate with
2293 * attributes specified.
2296 sysevent_bind_xsubscriber(sysevent_handle_t
*shp
,
2297 void (*event_handler
)(sysevent_t
*ev
), sysevent_subattr_t
*subattr
)
2299 return (sysevent_bind_subscriber_cmn(shp
, event_handler
, subattr
));
2303 * sysevent_register_event - register an event class and associated subclasses
2304 * for an event subscriber
2307 sysevent_register_event(sysevent_handle_t
*shp
,
2308 const char *ev_class
, const char **ev_subclass
,
2312 char *event_class
= (char *)ev_class
;
2313 char **event_subclass_list
= (char **)ev_subclass
;
2314 char *nvlbuf
= NULL
;
2318 (void) mutex_lock(SH_LOCK(shp
));
2319 if (event_class
== NULL
|| event_subclass_list
== NULL
||
2320 event_subclass_list
[0] == NULL
|| SH_BOUND(shp
) != 1 ||
2321 subclass_num
<= 0) {
2322 (void) mutex_unlock(SH_LOCK(shp
));
2327 if (nvlist_alloc(&nvl
, NV_UNIQUE_NAME_TYPE
, 0) != 0) {
2328 (void) mutex_unlock(SH_LOCK(shp
));
2331 if (nvlist_add_string_array(nvl
, event_class
, event_subclass_list
,
2332 subclass_num
) != 0) {
2334 (void) mutex_unlock(SH_LOCK(shp
));
2337 if (nvlist_pack(nvl
, &nvlbuf
, &datalen
, NV_ENCODE_NATIVE
, 0) != 0) {
2339 (void) mutex_unlock(SH_LOCK(shp
));
2344 /* Store new subscriber in in-kernel registration */
2345 if (update_kernel_registration(shp
, SUBSCRIBER
,
2346 SE_REGISTER
, &SH_ID(shp
), datalen
, (uchar_t
*)nvlbuf
)
2350 (void) mutex_unlock(SH_LOCK(shp
));
2354 /* Update the publisher's cached registration */
2355 if (update_publisher_cache(
2356 (subscriber_priv_t
*)SH_PRIV_DATA(shp
), SE_REGISTER
,
2357 SH_ID(shp
), datalen
, (uchar_t
*)nvlbuf
) != 0) {
2360 (void) mutex_unlock(SH_LOCK(shp
));
2367 (void) mutex_unlock(SH_LOCK(shp
));
2373 * sysevent_unregister_event - Unregister an event class and associated
2374 * subclasses for an event subscriber
2377 sysevent_unregister_event(sysevent_handle_t
*shp
, const char *class)
2381 (void) mutex_lock(SH_LOCK(shp
));
2383 if (!SH_BOUND(shp
)) {
2384 (void) mutex_unlock(SH_LOCK(shp
));
2388 /* Remove subscriber from in-kernel registration */
2389 class_sz
= strlen(class) + 1;
2390 (void) update_kernel_registration(shp
, SUBSCRIBER
,
2391 SE_UNREGISTER
, &SH_ID(shp
), class_sz
, (uchar_t
*)class);
2392 /* Update the publisher's cached registration */
2393 (void) update_publisher_cache(
2394 (subscriber_priv_t
*)SH_PRIV_DATA(shp
), SE_UNREGISTER
,
2395 SH_ID(shp
), class_sz
, (uchar_t
*)class);
2397 (void) mutex_unlock(SH_LOCK(shp
));
2401 cleanup_id(sysevent_handle_t
*shp
, uint32_t id
, int type
)
2403 dprint("cleanup_id: Cleaning up %s/%d\n", SH_CHANNEL_NAME(shp
), id
);
2405 /* Remove registration from the kernel */
2406 if (update_kernel_registration(shp
, type
, SE_CLEANUP
, &id
,
2408 dprint("cleanup_id: Unable to clean "
2409 "up %s/%d\n", SH_CHANNEL_NAME(shp
), id
);
2417 * sysevent_cleanup_subscribers: Allows the caller to cleanup resources
2418 * allocated to unresponsive subscribers.
2421 sysevent_cleanup_subscribers(sysevent_handle_t
*shp
)
2423 uint32_t ping
, result
;
2424 int i
, error
, sub_fd
;
2425 subscriber_data_t
*sub
;
2427 if (!SH_BOUND(shp
)) {
2431 for (i
= 1; i
<= MAX_SUBSCRIBERS
; ++i
) {
2433 sub
= SH_SUBSCRIBER(shp
, i
);
2438 if ((sub_fd
= open(sub
->sd_door_name
, O_RDONLY
)) == -1) {
2441 /* Check for valid and responsive subscriber */
2442 error
= clnt_deliver_event(sub_fd
, &ping
,
2443 sizeof (uint32_t), &result
, sizeof (result
));
2444 (void) close(sub_fd
);
2446 /* Only cleanup on EBADF (Invalid door descriptor) */
2450 if (cleanup_id(shp
, i
, SUBSCRIBER
) != 0)
2453 cache_remove_class(shp
, EC_ALL
, i
);
2455 free(sub
->sd_door_name
);
2457 SH_SUBSCRIBER(shp
, i
) = NULL
;
2463 * sysevent_cleanup_publishers: Allows stale publisher handles to be deallocated
2467 sysevent_cleanup_publishers(sysevent_handle_t
*shp
)
2469 (void) cleanup_id(shp
, 1, PUBLISHER
);
2473 * sysevent_unbind_subscriber: Unbind the subscriber from the sysevent channel.
2476 sysevent_unbind_subscriber(sysevent_handle_t
*shp
)
2478 subscriber_priv_t
*sub_info
;
2483 (void) mutex_lock(SH_LOCK(shp
));
2484 if (SH_BOUND(shp
) == 0) {
2485 (void) mutex_unlock(SH_LOCK(shp
));
2489 /* Update the in-kernel registration */
2490 (void) update_kernel_registration(shp
, SUBSCRIBER
,
2491 SE_UNBIND_REGISTRATION
, &SH_ID(shp
), 0, NULL
);
2493 /* Update the sysevent channel publisher */
2494 sub_info
= (subscriber_priv_t
*)SH_PRIV_DATA(shp
);
2495 (void) update_publisher_cache(sub_info
, SE_UNBIND_REGISTRATION
,
2496 SH_ID(shp
), 0, NULL
);
2498 /* Close down event delivery facilities */
2499 (void) door_revoke(SH_DOOR_DESC(shp
));
2500 (void) fdetach(SH_DOOR_NAME(shp
));
2503 * Release resources and wait for pending event delivery to
2506 (void) mutex_lock(&sub_info
->sp_qlock
);
2508 /* Signal event handler and drain the subscriber's event queue */
2509 (void) cond_signal(&sub_info
->sp_cv
);
2510 (void) mutex_unlock(&sub_info
->sp_qlock
);
2511 if (sub_info
->sp_handler_tid
!= 0)
2512 (void) thr_join(sub_info
->sp_handler_tid
, NULL
, NULL
);
2514 (void) cond_destroy(&sub_info
->sp_cv
);
2515 (void) mutex_destroy(&sub_info
->sp_qlock
);
2516 free(sub_info
->sp_door_name
);
2518 free(SH_DOOR_NAME(shp
));
2519 (void) mutex_unlock(SH_LOCK(shp
));
2523 * sysevent_unbind_publisher: Unbind publisher from the sysevent channel.
2526 sysevent_unbind_publisher(sysevent_handle_t
*shp
)
2531 (void) mutex_lock(SH_LOCK(shp
));
2532 if (SH_BOUND(shp
) == 0) {
2533 (void) mutex_unlock(SH_LOCK(shp
));
2537 /* Close down the registration facilities */
2538 (void) door_revoke(SH_DOOR_DESC(shp
));
2539 (void) fdetach(SH_DOOR_NAME(shp
));
2541 /* Update the in-kernel registration */
2542 (void) update_kernel_registration(shp
, PUBLISHER
,
2543 SE_UNBIND_REGISTRATION
, &SH_ID(shp
), 0, NULL
);
2546 /* Free resources associated with bind */
2547 free_cached_registration(shp
);
2548 dealloc_subscribers(shp
);
2550 free(SH_PRIV_DATA(shp
));
2551 free(SH_DOOR_NAME(shp
));
2553 (void) mutex_unlock(SH_LOCK(shp
));
2557 * Evolving APIs to subscribe to syseventd(8) system events.
2560 static sysevent_handle_t
*
2561 sysevent_bind_handle_cmn(void (*event_handler
)(sysevent_t
*ev
),
2562 sysevent_subattr_t
*subattr
)
2564 sysevent_handle_t
*shp
;
2566 if (getuid() != 0) {
2571 if (event_handler
== NULL
) {
2576 if ((shp
= sysevent_open_channel(SYSEVENTD_CHAN
)) == NULL
) {
2580 if (sysevent_bind_xsubscriber(shp
, event_handler
, subattr
) != 0) {
2582 * Ask syseventd to clean-up any stale subcribers and try to
2585 if (errno
== EBUSY
) {
2587 char door_name
[MAXPATHLEN
];
2589 struct reg_args rargs
;
2591 if (snprintf(door_name
, MAXPATHLEN
, "%s/%s",
2592 SH_CHANNEL_PATH(shp
), REG_DOOR
) >= MAXPATHLEN
) {
2593 sysevent_close_channel(shp
);
2598 rargs
.ra_op
= SE_CLEANUP
;
2599 pub_fd
= open(door_name
, O_RDONLY
);
2600 (void) clnt_deliver_event(pub_fd
, (void *)&rargs
,
2601 sizeof (struct reg_args
), &result
, sizeof (result
));
2602 (void) close(pub_fd
);
2604 /* Try to bind again */
2605 if (sysevent_bind_xsubscriber(shp
, event_handler
,
2607 sysevent_close_channel(shp
);
2611 sysevent_close_channel(shp
);
2620 * sysevent_bind_handle - Bind application event handler for syseventd
2624 sysevent_bind_handle(void (*event_handler
)(sysevent_t
*ev
))
2626 return (sysevent_bind_handle_cmn(event_handler
, NULL
));
2630 * sysevent_bind_xhandle - Bind application event handler for syseventd
2631 * subscription, using door_xcreate and attributes as specified.
2634 sysevent_bind_xhandle(void (*event_handler
)(sysevent_t
*ev
),
2635 sysevent_subattr_t
*subattr
)
2637 return (sysevent_bind_handle_cmn(event_handler
, subattr
));
2641 * sysevent_unbind_handle - Unbind caller from syseventd subscriptions
2644 sysevent_unbind_handle(sysevent_handle_t
*shp
)
2646 sysevent_unbind_subscriber(shp
);
2647 sysevent_close_channel(shp
);
2651 * sysevent_subscribe_event - Subscribe to system event notification from
2652 * syseventd(8) for the class and subclasses specified.
2655 sysevent_subscribe_event(sysevent_handle_t
*shp
, const char *event_class
,
2656 const char **event_subclass_list
, int num_subclasses
)
2658 return (sysevent_register_event(shp
, event_class
,
2659 event_subclass_list
, num_subclasses
));
2663 sysevent_unsubscribe_event(sysevent_handle_t
*shp
, const char *event_class
)
2665 sysevent_unregister_event(shp
, event_class
);