6659 nvlist_free(NULL) is a no-op
[illumos-gate.git] / usr / src / lib / libsysevent / libsysevent.c
blob72ec9b008b285257f3b569953243878c890f2965
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
19 * CDDL HEADER END
23 * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
26 #include <stdio.h>
27 #include <fcntl.h>
28 #include <errno.h>
29 #include <door.h>
30 #include <unistd.h>
31 #include <stddef.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <strings.h>
35 #include <synch.h>
36 #include <pthread.h>
37 #include <signal.h>
38 #include <thread.h>
39 #include <libnvpair.h>
40 #include <assert.h>
41 #include <sys/stat.h>
42 #include <sys/types.h>
43 #include <sys/modctl.h>
44 #include <sys/mnttab.h>
45 #include <sys/sysevent.h>
46 #include <sys/sysevent_impl.h>
48 #include "libsysevent.h"
49 #include "libsysevent_impl.h"
52 * libsysevent - The system event framework library
54 * This library provides routines to help with marshalling
55 * and unmarshalling of data contained in a sysevent event
56 * buffer.
59 #define SE_ENCODE_METHOD NV_ENCODE_NATIVE
61 #define dprint if (libsysevent_debug) (void) printf
62 static int libsysevent_debug = 0;
64 static sysevent_t *se_unpack(sysevent_t *);
65 static int cleanup_id(sysevent_handle_t *shp, uint32_t id, int type);
68 * The following routines allow system event publication to the sysevent
69 * framework.
73 * sysevent_alloc - allocate a sysevent buffer
75 static sysevent_t *
76 sysevent_alloc(char *class, int class_sz, char *subclass, int subclass_sz,
77 char *pub, int pub_sz, nvlist_t *attr_list)
79 int payload_sz;
80 int aligned_class_sz, aligned_subclass_sz, aligned_pub_sz;
81 size_t nvlist_sz = 0;
82 char *attr;
83 uint64_t attr_offset;
84 sysevent_t *ev;
86 if (attr_list != NULL) {
87 if (nvlist_size(attr_list, &nvlist_sz, SE_ENCODE_METHOD)
88 != 0) {
89 return (NULL);
94 * Calculate and reserve space for the class, subclass and
95 * publisher strings in the event buffer
98 /* String sizes must be 64-bit aligned in the event buffer */
99 aligned_class_sz = SE_ALIGN(class_sz);
100 aligned_subclass_sz = SE_ALIGN(subclass_sz);
101 aligned_pub_sz = SE_ALIGN(pub_sz);
103 payload_sz = (aligned_class_sz - sizeof (uint64_t)) +
104 (aligned_subclass_sz - sizeof (uint64_t)) +
105 (aligned_pub_sz - sizeof (uint64_t)) - sizeof (uint64_t) +
106 nvlist_sz;
109 * Allocate event buffer plus additional payload overhead.
111 ev = calloc(1, sizeof (sysevent_impl_t) + payload_sz);
112 if (ev == NULL) {
113 return (NULL);
116 /* Initialize the event buffer data */
117 SE_VERSION(ev) = SYS_EVENT_VERSION;
118 (void) bcopy(class, SE_CLASS_NAME(ev), class_sz);
120 SE_SUBCLASS_OFF(ev) = SE_ALIGN(offsetof(sysevent_impl_t, se_class_name))
121 + aligned_class_sz;
122 (void) bcopy(subclass, SE_SUBCLASS_NAME(ev), subclass_sz);
124 SE_PUB_OFF(ev) = SE_SUBCLASS_OFF(ev) + aligned_subclass_sz;
125 (void) bcopy(pub, SE_PUB_NAME(ev), pub_sz);
127 SE_PAYLOAD_SZ(ev) = payload_sz;
128 SE_ATTR_PTR(ev) = (uint64_t)0;
130 /* Check for attribute list */
131 if (attr_list == NULL) {
132 return (ev);
135 /* Copy attribute data to contiguous memory */
136 SE_FLAG(ev) = SE_PACKED_BUF;
137 attr_offset = SE_ATTR_OFF(ev);
138 attr = (char *)((caddr_t)ev + attr_offset);
139 if (nvlist_pack(attr_list, &attr, &nvlist_sz, SE_ENCODE_METHOD,
140 0) != 0) {
141 free(ev);
142 return (NULL);
145 return (ev);
149 * sysevent_post_event - generate a system event via the sysevent framework
152 sysevent_post_event(char *class, char *subclass, char *vendor, char *pub_name,
153 nvlist_t *attr_list, sysevent_id_t *eid)
155 int error;
156 sysevent_t *ev;
158 ev = sysevent_alloc_event(class, subclass, vendor, pub_name, attr_list);
159 if (ev == NULL) {
160 return (-1);
163 error = modctl(MODEVENTS, (uintptr_t)MODEVENTS_POST_EVENT,
164 (uintptr_t)ev, (uintptr_t)SE_SIZE(ev), (uintptr_t)eid, 0);
166 sysevent_free(ev);
168 if (error) {
169 errno = EIO;
170 return (-1);
173 return (0);
177 * The following routines are used to free or duplicate a
178 * sysevent event buffer.
182 * sysevent_dup - Allocate and copy an event buffer
183 * Copies both packed and unpacked to unpacked sysevent.
185 sysevent_t *
186 sysevent_dup(sysevent_t *ev)
188 nvlist_t *nvl, *cnvl = NULL;
189 uint64_t attr_offset;
190 sysevent_t *copy;
192 if (SE_FLAG(ev) == SE_PACKED_BUF)
193 return (se_unpack(ev));
195 /* Copy event header information */
196 attr_offset = SE_ATTR_OFF(ev);
197 copy = calloc(1, attr_offset);
198 if (copy == NULL)
199 return (NULL);
200 bcopy(ev, copy, attr_offset);
202 nvl = (nvlist_t *)(uintptr_t)SE_ATTR_PTR(ev);
203 if (nvl && nvlist_dup(nvl, &cnvl, 0) != 0) {
204 free(copy);
205 return (NULL);
208 SE_ATTR_PTR(copy) = (uintptr_t)cnvl;
209 SE_FLAG(copy) = 0; /* unpacked */
210 return (copy);
214 * sysevent_free - Free memory allocated for an event buffer
216 void
217 sysevent_free(sysevent_t *ev)
219 nvlist_t *attr_list = (nvlist_t *)(uintptr_t)SE_ATTR_PTR(ev);
221 nvlist_free(attr_list);
222 free(ev);
226 * The following routines are used to extract attribute data from a sysevent
227 * handle.
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)
237 int error;
238 caddr_t attr;
239 size_t attr_len;
240 uint64_t attr_offset;
241 nvlist_t *nvl;
243 *nvlist = NULL;
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);
248 if (nvl == NULL) {
249 return (0);
251 if ((error = nvlist_dup(nvl, nvlist, 0)) != 0) {
252 if (error == ENOMEM) {
253 errno = error;
254 } else {
255 errno = EINVAL;
257 return (-1);
259 return (0);
262 attr_offset = SE_ATTR_OFF(ev);
263 if (SE_SIZE(ev) == attr_offset) {
264 return (0);
267 /* unpack nvlist */
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) {
272 errno = error;
273 } else {
274 errno = EINVAL;
276 return (-1);
279 return (0);
283 * sysevent_attr_name - Get name of attribute
285 char *
286 sysevent_attr_name(sysevent_attr_t *attr)
288 if (attr == NULL) {
289 errno = EINVAL;
290 return (NULL);
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;
303 if (nvp == NULL)
304 return (EINVAL);
306 /* Convert DATA_TYPE_* to SE_DATA_TYPE_* */
307 switch (nvpair_type(nvp)) {
308 case DATA_TYPE_BYTE:
309 se_value->value_type = SE_DATA_TYPE_BYTE;
310 (void) nvpair_value_byte(nvp, &se_value->value.sv_byte);
311 break;
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);
315 break;
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);
319 break;
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);
323 break;
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);
327 break;
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);
331 break;
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);
335 break;
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);
339 break;
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);
345 break;
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);
349 break;
350 default:
351 return (ENOTSUP);
353 return (0);
357 * sysevent_attr_next - Get next attribute in event attribute list
359 sysevent_attr_t *
360 sysevent_attr_next(sysevent_t *ev, sysevent_attr_t *attr)
362 nvlist_t *nvl;
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) {
369 return (NULL);
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)
383 nvpair_t *nvp;
384 nvlist_t *nvl;
386 assert(SE_FLAG(ev) != SE_PACKED_BUF);
388 if (SE_ATTR_PTR(ev) == (uint64_t)0) {
389 return (ENOENT);
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);
399 while (nvp) {
400 if ((strcmp(name, nvpair_name(nvp)) == 0) &&
401 (sysevent_attr_value(nvp, se_value) == 0) &&
402 (se_value->value_type == datatype))
403 return (0);
404 nvp = nvlist_next_nvpair(nvl, nvp);
406 return (ENOENT);
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
432 char *
433 sysevent_get_class_name(sysevent_t *ev)
435 return (SE_CLASS_NAME(ev));
438 typedef enum {
439 PUB_VEND,
440 PUB_KEYWD,
441 PUB_NAME,
442 PUB_PID
443 } se_pub_id_t;
446 * sysevent_get_pub - Get publisher name string
448 char *
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.
459 static char *
460 parse_pub_id(sysevent_t *ev, se_pub_id_t token)
462 int i;
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);
468 if (str == NULL) {
469 free(pub_id);
470 return (NULL);
474 pub_element = strdup(str);
475 free(pub_id);
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.
485 static const char *
486 pub_idx(const char *pstr, int token)
488 int i;
490 for (i = 1; i <= token; i++) {
491 if ((pstr = index(pstr, ':')) == NULL)
492 return (NULL);
493 pstr++;
496 /* String might be empty */
497 if (pstr) {
498 if (*pstr == '\0' || *pstr == ':')
499 return (NULL);
501 return (pstr);
504 char *
505 sysevent_get_vendor_name(sysevent_t *ev)
507 return (parse_pub_id(ev, PUB_VEND));
510 char *
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.
520 void
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)
530 return;
532 if ((part_str = pub_idx(pub_str, PUB_PID)) == NULL)
533 return;
535 *pid = (pid_t)atoi(part_str);
539 * sysevent_get_subclass_name - Get subclass name string
541 char *
542 sysevent_get_subclass_name(sysevent_t *ev)
544 return (SE_SUBCLASS_NAME(ev));
548 * sysevent_get_seq - Get event sequence id
550 uint64_t
551 sysevent_get_seq(sysevent_t *ev)
553 return (SE_SEQ(ev));
557 * sysevent_get_time - Get event timestamp
559 void
560 sysevent_get_time(sysevent_t *ev, hrtime_t *etime)
562 *etime = SE_TIME(ev);
566 * sysevent_get_size - Get event buffer size
568 size_t
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
583 * channel.
585 sysevent_t *
586 sysevent_alloc_event(char *class, char *subclass, char *vendor, char *pub_name,
587 nvlist_t *attr_list)
589 int class_sz, subclass_sz, pub_sz;
590 char *pub_id;
591 sysevent_t *ev;
593 if ((class == NULL) || (subclass == NULL) || (vendor == NULL) ||
594 (pub_name == NULL)) {
595 errno = EINVAL;
596 return (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)) {
603 errno = EINVAL;
604 return (NULL);
608 * Calculate the publisher size plus string seperators and maximum
609 * pid characters
611 pub_sz = strlen(vendor) + sizeof (SE_USR_PUB) + strlen(pub_name) + 14;
612 if (pub_sz > MAX_PUB_LEN) {
613 errno = EINVAL;
614 return (NULL);
616 pub_id = malloc(pub_sz);
617 if (pub_id == NULL) {
618 errno = ENOMEM;
619 return (NULL);
621 if (snprintf(pub_id, pub_sz, "%s:%s%s:%d", vendor, SE_USR_PUB,
622 pub_name, (int)getpid()) >= pub_sz) {
623 free(pub_id);
624 errno = EINVAL;
625 return (NULL);
627 pub_sz = strlen(pub_id) + 1;
629 ev = sysevent_alloc(class, class_sz, subclass, subclass_sz,
630 pub_id, pub_sz, attr_list);
631 free(pub_id);
632 if (ev == NULL) {
633 errno = ENOMEM;
634 return (NULL);
637 return (ev);
641 * se_unpack - unpack nvlist to a searchable list.
642 * If already unpacked, will do a dup.
644 static sysevent_t *
645 se_unpack(sysevent_t *ev)
647 caddr_t attr;
648 size_t attr_len;
649 nvlist_t *attrp = NULL;
650 uint64_t attr_offset;
651 sysevent_t *copy;
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);
658 if (copy == NULL)
659 return (NULL);
660 bcopy(ev, copy, attr_offset);
661 SE_FLAG(copy) = 0; /* unpacked */
663 /* unpack nvlist */
664 attr = (caddr_t)ev + attr_offset;
665 attr_len = SE_SIZE(ev) - attr_offset;
666 if (attr_len == 0) {
667 return (copy);
669 if (nvlist_unpack(attr, attr_len, &attrp, 0) != 0) {
670 free(copy);
671 return (NULL);
674 SE_ATTR_PTR(copy) = (uintptr_t)attrp;
675 return (copy);
679 * se_print - Prints elements in an event buffer
681 void
682 se_print(FILE *fp, sysevent_t *ev)
684 char *vendor, *pub;
685 pid_t pid;
686 hrtime_t hrt;
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);
696 free(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);
701 free(pub);
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
737 * delivery.
739 * Most of these routines are used by syseventd(1M), 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.
758 static void
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 == NULL)
768 sub_info->sp_handler_tid = thr_self();
770 (void) mutex_lock(&sub_info->sp_qlock);
771 for (;;) {
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;
776 while (evqp) {
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;
781 free(evqp->sq_ev);
782 free(evqp);
783 evqp = sub_info->sp_evq_head;
785 if (!SH_BOUND(shp)) {
786 (void) mutex_unlock(&sub_info->sp_qlock);
787 return;
791 /* NOTREACHED */
795 * Data structure used to communicate event subscription cache updates
796 * to publishers via a registration door
798 struct reg_args {
799 uint32_t ra_sub_id;
800 uint32_t ra_op;
801 uint64_t ra_buf_ptr;
806 * event_deliver_service - generic event delivery service routine. This routine
807 * is called in response to a door call to post an event.
810 /*ARGSUSED*/
811 static void
812 event_deliver_service(void *cookie, char *args, size_t alen,
813 door_desc_t *ddp, uint_t ndid)
815 int ret = 0;
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)) {
821 ret = EINVAL;
822 goto return_from_door;
825 /* Publisher checking on subscriber */
826 if (alen == sizeof (uint32_t)) {
827 ret = 0;
828 goto return_from_door;
831 shp = (sysevent_handle_t *)cookie;
832 if (shp == NULL) {
833 ret = EBADF;
834 goto return_from_door;
838 * Mustn't block if we are trying to update the registration with
839 * the publisher
841 if (mutex_trylock(SH_LOCK(shp)) != 0) {
842 ret = EAGAIN;
843 goto return_from_door;
846 if (!SH_BOUND(shp)) {
847 ret = EBADF;
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) {
854 ret = EBADF;
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) {
862 ret = EAGAIN;
863 (void) mutex_unlock(SH_LOCK(shp));
864 goto return_from_door;
868 * Allocate and copy the event buffer into the subscriber's
869 * address space
871 new_eq->sq_ev = calloc(1, alen);
872 if (new_eq->sq_ev == NULL) {
873 free(new_eq);
874 ret = EAGAIN;
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;
883 } else {
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));
892 return_from_door:
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
917 * delivery door
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.
922 static int
923 clnt_deliver_event(int service_door, void *data, size_t datalen,
924 void *result, size_t rlen)
926 int error = 0;
927 door_arg_t door_arg;
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;
937 * Make door call
939 while ((error = door_call(service_door, &door_arg)) != 0) {
940 if (errno == EAGAIN || errno == EINTR) {
941 continue;
942 } else {
943 error = errno;
944 break;
948 return (error);
951 static int
952 update_publisher_cache(subscriber_priv_t *sub_info, int update_op,
953 uint32_t sub_id, size_t datasz, uchar_t *data)
955 int pub_fd;
956 uint32_t result = 0;
957 struct reg_args *rargs;
959 rargs = (struct reg_args *)calloc(1, sizeof (struct reg_args) +
960 datasz);
961 if (rargs == NULL) {
962 errno = ENOMEM;
963 return (-1);
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);
975 free(rargs);
976 if (result != 0) {
977 errno = result;
978 return (-1);
981 return (0);
986 * update_kernel_registration - update the in-kernel registration for the
987 * given channel.
989 static int
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)
993 int error;
994 char *channel_name = SH_CHANNEL_NAME(shp);
995 se_pubsub_t udata;
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))
1005 != 0) {
1006 return (error);
1009 *sub_id = udata.ps_id;
1011 return (error);
1015 * get_kernel_registration - get the current subscriber registration for
1016 * the given channel
1018 static nvlist_t *
1019 get_kernel_registration(char *channel_name, uint32_t class_id)
1021 char *nvlbuf;
1022 nvlist_t *nvl;
1023 se_pubsub_t udata;
1025 nvlbuf = calloc(1, MAX_SUBSCRIPTION_SZ);
1026 if (nvlbuf == NULL) {
1027 return (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)
1038 != 0) {
1040 /* Need a bigger buffer to hold channel registration */
1041 if (errno == EAGAIN) {
1042 free(nvlbuf);
1043 nvlbuf = calloc(1, udata.ps_buflen);
1044 if (nvlbuf == NULL)
1045 return (NULL);
1047 /* Try again */
1048 if (modctl(MODEVENTS,
1049 (uintptr_t)MODEVENTS_REGISTER_EVENT,
1050 (uintptr_t)channel_name, (uintptr_t)nvlbuf,
1051 (uintptr_t)&udata, 0) != 0) {
1052 free(nvlbuf);
1053 return (NULL);
1055 } else {
1056 free(nvlbuf);
1057 return (NULL);
1061 if (nvlist_unpack(nvlbuf, udata.ps_buflen, &nvl, 0) != 0) {
1062 free(nvlbuf);
1063 return (NULL);
1065 free(nvlbuf);
1067 return (nvl);
1071 * The following routines provide a mechanism for publishers to maintain
1072 * subscriber information.
1075 static void
1076 dealloc_subscribers(sysevent_handle_t *shp)
1078 int i;
1079 subscriber_data_t *sub;
1081 for (i = 1; i <= MAX_SUBSCRIBERS; ++i) {
1082 sub = SH_SUBSCRIBER(shp, i);
1083 if (sub != NULL) {
1084 free(sub->sd_door_name);
1085 free(sub);
1087 SH_SUBSCRIBER(shp, i) = NULL;
1091 /*ARGSUSED*/
1092 static int
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) {
1099 return (0);
1102 /* Allocate and initialize the subscriber data */
1103 sub = (subscriber_data_t *)calloc(1,
1104 sizeof (subscriber_data_t));
1105 if (sub == NULL) {
1106 return (-1);
1108 if (snprintf(door_name, MAXPATHLEN, "%s/%d",
1109 SH_CHANNEL_PATH(shp), sub_id) >= MAXPATHLEN) {
1110 free(sub);
1111 return (-1);
1114 sub->sd_flag = ACTIVE;
1115 sub->sd_door_name = strdup(door_name);
1116 if (sub->sd_door_name == NULL) {
1117 free(sub);
1118 return (-1);
1121 SH_SUBSCRIBER(shp, sub_id) = sub;
1122 return (0);
1127 * The following routines are used to update and maintain the registration cache
1128 * for a particular sysevent channel.
1131 static uint32_t
1132 hash_func(const char *s)
1134 uint32_t result = 0;
1135 uint_t g;
1137 while (*s != '\0') {
1138 result <<= 4;
1139 result += (uint32_t)*s++;
1140 g = result & 0xf0000000;
1141 if (g != 0) {
1142 result ^= g >> 24;
1143 result ^= g;
1147 return (result);
1150 subclass_lst_t *
1151 cache_find_subclass(class_lst_t *c_list, char *subclass)
1153 subclass_lst_t *sc_list;
1155 if (c_list == NULL)
1156 return (NULL);
1158 sc_list = c_list->cl_subclass_list;
1160 while (sc_list != NULL) {
1161 if (strcmp(sc_list->sl_name, subclass) == 0) {
1162 return (sc_list);
1164 sc_list = sc_list->sl_next;
1167 return (NULL);
1171 static class_lst_t *
1172 cache_find_class(sysevent_handle_t *shp, char *class)
1174 int index;
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) {
1186 break;
1188 c_list = c_list->cl_next;
1191 return (c_list);
1194 static int
1195 cache_insert_subclass(class_lst_t *c_list, char **subclass_names,
1196 int subclass_num, uint32_t sub_id)
1198 int i;
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]))
1203 != NULL) {
1204 sc_list->sl_num[sub_id] = 1;
1205 } else {
1206 sc_list = (subclass_lst_t *)calloc(1,
1207 sizeof (subclass_lst_t));
1208 if (sc_list == NULL)
1209 return (-1);
1211 sc_list->sl_name = strdup(subclass_names[i]);
1212 if (sc_list->sl_name == NULL) {
1213 free(sc_list);
1214 return (-1);
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;
1223 return (0);
1226 static int
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);
1237 return (0);
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) {
1245 return (1);
1247 c_list->cl_name = strdup(class);
1248 if (c_list->cl_name == NULL) {
1249 free(c_list);
1250 return (1);
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);
1257 free(c_list);
1258 return (1);
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);
1264 free(c_list);
1265 return (1);
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,
1274 sub_id) != 0)
1275 return (1);
1277 return (0);
1280 static void
1281 cache_remove_all_class(sysevent_handle_t *shp, uint32_t sub_id)
1283 int i;
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;
1300 static void
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);
1308 return;
1311 if ((c_list = cache_find_class(shp, class)) == NULL) {
1312 return;
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;
1322 static void
1323 free_cached_registration(sysevent_handle_t *shp)
1325 int i;
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;
1336 free(sc_list);
1337 sc_list = next_sc;
1339 free(clist->cl_name);
1340 next_clist = clist->cl_next;
1341 free(clist);
1342 clist = next_clist;
1344 SH_CLASS_HASH(shp)[i] = NULL;
1348 static int
1349 create_cached_registration(sysevent_handle_t *shp,
1350 class_lst_t **class_hash)
1352 int i, j, new_class;
1353 char *class_name;
1354 uint_t num_elem;
1355 uchar_t *subscribers;
1356 nvlist_t *nvl;
1357 nvpair_t *nvpair;
1358 class_lst_t *clist;
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))
1364 == NULL) {
1365 if (errno == ENOENT) {
1366 class_hash[i] = NULL;
1367 continue;
1368 } else {
1369 goto create_failed;
1374 nvpair = NULL;
1375 if ((nvpair = nvlist_next_nvpair(nvl, nvpair)) == NULL) {
1376 goto create_failed;
1379 new_class = 1;
1380 while (new_class) {
1381 /* Extract the class name from the nvpair */
1382 if (nvpair_value_string(nvpair, &class_name) != 0) {
1383 goto create_failed;
1385 clist = (class_lst_t *)
1386 calloc(1, sizeof (class_lst_t));
1387 if (clist == NULL) {
1388 goto create_failed;
1391 clist->cl_name = strdup(class_name);
1392 if (clist->cl_name == NULL) {
1393 free(clist);
1394 goto create_failed;
1398 * Extract the subclass name and registration
1399 * from the nvpair
1401 if ((nvpair = nvlist_next_nvpair(nvl, nvpair))
1402 == NULL) {
1403 free(clist->cl_name);
1404 free(clist);
1405 goto create_failed;
1408 clist->cl_next = class_hash[i];
1409 class_hash[i] = clist;
1411 for (;;) {
1413 sc_list = (subclass_lst_t *)calloc(1,
1414 sizeof (subclass_lst_t));
1415 if (sc_list == NULL) {
1416 goto create_failed;
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) {
1424 goto create_failed;
1427 if (nvpair_value_byte_array(nvpair,
1428 &subscribers, &num_elem) != 0) {
1429 goto create_failed;
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)
1436 continue;
1438 if (alloc_subscriber(shp, j, 1) != 0) {
1439 goto create_failed;
1444 * Check next nvpair - either subclass or
1445 * class
1447 if ((nvpair = nvlist_next_nvpair(nvl, nvpair))
1448 == NULL) {
1449 new_class = 0;
1450 break;
1451 } else if (strcmp(nvpair_name(nvpair),
1452 CLASS_NAME) == 0) {
1453 break;
1457 nvlist_free(nvl);
1459 return (0);
1461 create_failed:
1462 dealloc_subscribers(shp);
1463 free_cached_registration(shp);
1464 nvlist_free(nvl);
1465 return (-1);
1470 * cache_update_service - generic event publisher service routine. This routine
1471 * is called in response to a registration cache update.
1474 /*ARGSUSED*/
1475 static void
1476 cache_update_service(void *cookie, char *args, size_t alen,
1477 door_desc_t *ddp, uint_t ndid)
1479 int ret = 0;
1480 uint_t num_elem;
1481 char *class, **event_list;
1482 size_t datalen;
1483 uint32_t sub_id;
1484 nvlist_t *nvl;
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) {
1491 ret = EINVAL;
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) {
1505 case SE_UNREGISTER:
1506 class = (char *)&rargs->ra_buf_ptr;
1507 cache_remove_class(shp, (char *)class,
1508 sub_id);
1509 break;
1510 case SE_UNBIND_REGISTRATION:
1512 sub = SH_SUBSCRIBER(shp, sub_id);
1513 if (sub == NULL)
1514 break;
1516 free(sub->sd_door_name);
1517 free(sub);
1518 cache_remove_class(shp, EC_ALL, sub_id);
1519 SH_SUBSCRIBER(shp, sub_id) = NULL;
1521 break;
1522 case SE_BIND_REGISTRATION:
1524 /* New subscriber */
1525 if (alloc_subscriber(shp, sub_id, 0) != 0) {
1526 ret = ENOMEM;
1527 break;
1529 break;
1530 case SE_REGISTER:
1532 if (SH_SUBSCRIBER(shp, sub_id) == NULL) {
1533 ret = EINVAL;
1534 break;
1536 /* Get new registration data */
1537 if (nvlist_unpack((char *)&rargs->ra_buf_ptr, datalen,
1538 &nvl, 0) != 0) {
1539 ret = EFAULT;
1540 break;
1542 if ((nvpair = nvlist_next_nvpair(nvl, nvpair)) == NULL) {
1543 nvlist_free(nvl);
1544 ret = EFAULT;
1545 break;
1547 if (nvpair_value_string_array(nvpair, &event_list, &num_elem)
1548 != 0) {
1549 nvlist_free(nvl);
1550 ret = EFAULT;
1551 break;
1553 class = nvpair_name(nvpair);
1555 ret = cache_insert_class(shp, class,
1556 event_list, num_elem, sub_id);
1557 if (ret != 0) {
1558 cache_remove_class(shp, class, sub_id);
1559 nvlist_free(nvl);
1560 ret = EFAULT;
1561 break;
1564 nvlist_free(nvl);
1566 break;
1567 case SE_CLEANUP:
1568 /* Cleanup stale subscribers */
1569 sysevent_cleanup_subscribers(shp);
1570 break;
1571 default:
1572 ret = EINVAL;
1575 (void) mutex_unlock(SH_LOCK(shp));
1577 return_from_door:
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));
1609 send_event:
1611 want_resend = 0;
1612 if (!SH_BOUND(shp)) {
1613 (void) mutex_unlock(SH_LOCK(shp));
1614 errno = EINVAL;
1615 return (-1);
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);
1625 if (sc_lst)
1626 all_subclass_subscribers = sc_lst->sl_num;
1627 else
1628 all_subclass_subscribers = NULL;
1630 sc_lst = cache_find_subclass(
1631 cache_find_class(shp, event_class), event_subclass);
1632 if (sc_lst)
1633 subclass_subscribers = sc_lst->sl_num;
1634 else
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)
1642 continue;
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",
1651 continue;
1654 /* Process only resend requests */
1655 if (resend_cnt > 0 && !(sub->sd_flag & SEND_AGAIN)) {
1656 continue;
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));
1662 continue;
1664 result = 0;
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 */
1671 if (error == 0) {
1672 switch (result) {
1673 /* Subscriber requested EAGAIN */
1674 case EAGAIN:
1675 if (resend_cnt > SE_MAX_RETRY_LIMIT) {
1676 deliver_error = 1;
1677 } else {
1678 want_resend = 1;
1679 dprint("sysevent_send_event: resend "
1680 "requested for %d\n", i);
1681 sub->sd_flag |= SEND_AGAIN;
1683 break;
1684 /* Bad sysevent handle for subscriber */
1685 case EBADF:
1686 case EINVAL:
1687 dprint("sysevent_send_event: Bad sysevent "
1688 "handle for %s", sub->sd_door_name);
1689 sub->sd_flag = 0;
1690 deliver_error = 1;
1691 break;
1692 /* Successful delivery */
1693 default:
1694 sub->sd_flag &= ~SEND_AGAIN;
1695 ++subscribers_sent;
1697 } else {
1698 dprint("sysevent_send_event: Failed door call "
1699 "to %s: %s: %d\n", sub->sd_door_name,
1700 strerror(errno), result);
1701 sub->sd_flag = 0;
1702 deliver_error = 1;
1706 if (want_resend) {
1707 resend_cnt++;
1708 goto send_event;
1711 if (deliver_error) {
1712 sysevent_cleanup_subscribers(shp);
1713 (void) mutex_unlock(SH_LOCK(shp));
1714 errno = EFAULT;
1715 return (-1);
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);
1723 errno = ENOENT;
1724 return (-1);
1727 return (0);
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;
1738 char *begin_path;
1739 struct stat chan_stat;
1740 sysevent_handle_t *shp;
1743 if (channel_path == NULL || strlen(channel_path) + 1 > MAXPATHLEN) {
1744 errno = EINVAL;
1745 return (NULL);
1748 if (mkdir(channel_path, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH) < 0) {
1749 if (errno != EEXIST) {
1750 errno = EACCES;
1751 return (NULL);
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);
1759 errno = EACCES;
1760 return (NULL);
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);
1768 errno = EACCES;
1769 return (NULL);
1772 shp = calloc(1, sizeof (sysevent_impl_hdl_t));
1773 if (shp == NULL) {
1774 errno = ENOMEM;
1775 return (NULL);
1778 SH_CHANNEL_NAME(shp) = NULL;
1779 SH_CHANNEL_PATH(shp) = strdup(channel_path);
1780 if (SH_CHANNEL_PATH(shp) == NULL) {
1781 free(shp);
1782 errno = ENOMEM;
1783 return (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) {
1790 ++begin_path;
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));
1799 free(shp);
1800 errno = EFAULT;
1801 return (NULL);
1804 (void) mutex_init(SH_LOCK(shp), USYNC_THREAD, NULL);
1806 return (shp);
1810 * Establish a sysevent channel for publication and subscription
1812 sysevent_handle_t *
1813 sysevent_open_channel(const char *channel)
1815 int var_run_mounted = 0;
1816 char full_channel[MAXPATHLEN + 1];
1817 FILE *fp;
1818 struct stat chan_stat;
1819 struct extmnttab m;
1821 if (channel == NULL) {
1822 errno = EINVAL;
1823 return (NULL);
1827 * Check that /var/run is mounted as tmpfs before allowing a channel
1828 * to be opened.
1830 if ((fp = fopen(MNTTAB, "rF")) == NULL) {
1831 errno = EACCES;
1832 return (NULL);
1835 resetmnttab(fp);
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;
1841 break;
1844 (void) fclose(fp);
1846 if (!var_run_mounted) {
1847 errno = EACCES;
1848 return (NULL);
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,
1856 strerror(errno));
1857 if (errno != EEXIST) {
1858 errno = EACCES;
1859 return (NULL);
1864 if (snprintf(full_channel, MAXPATHLEN, "%s/%s", CHAN_PATH, channel) >=
1865 MAXPATHLEN) {
1866 errno = EINVAL;
1867 return (NULL);
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
1877 sysevent_handle_t *
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
1887 void
1888 sysevent_close_channel(sysevent_handle_t *shp)
1890 int error = errno;
1891 uint32_t sub_id = 0;
1893 if (shp == NULL) {
1894 return;
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));
1912 free(shp);
1913 errno = error;
1917 * sysevent_bind_publisher - Bind an event publisher to an event channel
1920 sysevent_bind_publisher(sysevent_handle_t *shp)
1922 int error = 0;
1923 int fd = -1;
1924 char door_name[MAXPATHLEN];
1925 uint32_t pub_id;
1926 struct stat reg_stat;
1927 publisher_priv_t *pub;
1929 if (shp == NULL) {
1930 errno = EINVAL;
1931 return (-1);
1934 (void) mutex_lock(SH_LOCK(shp));
1935 if (SH_BOUND(shp)) {
1936 (void) mutex_unlock(SH_LOCK(shp));
1937 errno = EINVAL;
1938 return (-1);
1941 if ((pub = (publisher_priv_t *)calloc(1, sizeof (publisher_priv_t))) ==
1942 NULL) {
1943 (void) mutex_unlock(SH_LOCK(shp));
1944 errno = ENOMEM;
1945 return (-1);
1947 SH_PRIV_DATA(shp) = (void *)pub;
1949 if (snprintf(door_name, MAXPATHLEN, "%s/%s",
1950 SH_CHANNEL_PATH(shp), REG_DOOR) >= MAXPATHLEN) {
1951 free(pub);
1952 (void) mutex_unlock(SH_LOCK(shp));
1953 errno = ENOMEM;
1954 return (-1);
1956 if ((SH_DOOR_NAME(shp) = strdup(door_name)) == NULL) {
1957 free(pub);
1958 (void) mutex_unlock(SH_LOCK(shp));
1959 errno = ENOMEM;
1960 return (-1);
1963 /* Only one publisher allowed per channel */
1964 if (stat(SH_DOOR_NAME(shp), &reg_stat) != 0) {
1965 if (errno != ENOENT) {
1966 error = EINVAL;
1967 goto fail;
1972 * Remove door file for robustness.
1974 if (unlink(SH_DOOR_NAME(shp)) != 0)
1975 dprint("sysevent_bind_publisher: Unlink of %s failed.\n",
1976 SH_DOOR_NAME(shp));
1978 /* Open channel registration door */
1979 fd = open(SH_DOOR_NAME(shp), O_CREAT|O_RDWR,
1980 S_IREAD|S_IWRITE);
1981 if (fd == -1) {
1982 error = EINVAL;
1983 goto fail;
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));
1993 error = EFAULT;
1994 goto fail;
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",
2001 SH_DOOR_NAME(shp));
2002 error = EACCES;
2003 goto fail;
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) {
2009 error = errno;
2010 goto fail;
2013 SH_ID(shp) = pub_id;
2014 SH_BOUND(shp) = 1;
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);
2022 error = EFAULT;
2023 goto fail;
2025 (void) close(fd);
2027 (void) mutex_unlock(SH_LOCK(shp));
2029 return (0);
2031 fail:
2032 SH_BOUND(shp) = 0;
2033 (void) door_revoke(SH_DOOR_DESC(shp));
2034 (void) fdetach(SH_DOOR_NAME(shp));
2035 free(SH_DOOR_NAME(shp));
2036 free(pub);
2037 (void) close(fd);
2038 (void) mutex_unlock(SH_LOCK(shp));
2039 errno = error;
2040 return (-1);
2043 static pthread_once_t xdoor_thrattr_once = PTHREAD_ONCE_INIT;
2044 static pthread_attr_t xdoor_thrattr;
2046 static void
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);
2055 static int
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;
2061 sigset_t oset;
2062 int err;
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;
2072 } else {
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);
2083 static void
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);
2090 } else {
2091 (void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
2092 (void) pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
2096 static int
2097 sysevent_bind_subscriber_cmn(sysevent_handle_t *shp,
2098 void (*event_handler)(sysevent_t *ev),
2099 sysevent_subattr_t *subattr)
2101 int fd = -1;
2102 int error = 0;
2103 uint32_t sub_id = 0;
2104 char door_name[MAXPATHLEN];
2105 subscriber_priv_t *sub_info;
2106 int created;
2107 struct sysevent_subattr_impl *xsa =
2108 (struct sysevent_subattr_impl *)subattr;
2110 if (shp == NULL || event_handler == NULL) {
2111 errno = EINVAL;
2112 return (-1);
2115 (void) mutex_lock(SH_LOCK(shp));
2116 if (SH_BOUND(shp)) {
2117 errno = EINVAL;
2118 (void) mutex_unlock(SH_LOCK(shp));
2119 return (-1);
2122 if ((sub_info = (subscriber_priv_t *)calloc(1,
2123 sizeof (subscriber_priv_t))) == NULL) {
2124 errno = ENOMEM;
2125 (void) mutex_unlock(SH_LOCK(shp));
2126 return (-1);
2129 if (snprintf(door_name, MAXPATHLEN, "%s/%s",
2130 SH_CHANNEL_PATH(shp), REG_DOOR) >= MAXPATHLEN) {
2131 free(sub_info);
2132 errno = EINVAL;
2133 (void) mutex_unlock(SH_LOCK(shp));
2134 return (-1);
2137 if ((sub_info->sp_door_name = strdup(door_name)) == NULL) {
2138 free(sub_info);
2139 errno = ENOMEM;
2140 (void) mutex_unlock(SH_LOCK(shp));
2141 return (-1);
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) {
2150 error = errno;
2151 goto fail;
2153 SH_ID(shp) = sub_id;
2155 if (snprintf(door_name, MAXPATHLEN, "%s/%d",
2156 SH_CHANNEL_PATH(shp), sub_id) >= MAXPATHLEN) {
2157 error = EINVAL;
2158 goto fail;
2160 if ((SH_DOOR_NAME(shp) = strdup(door_name)) == NULL) {
2161 error = ENOMEM;
2162 goto fail;
2166 * Remove door file for robustness.
2168 if (unlink(SH_DOOR_NAME(shp)) != 0)
2169 dprint("sysevent_bind_subscriber: Unlink of %s failed.\n",
2170 SH_DOOR_NAME(shp));
2172 fd = open(SH_DOOR_NAME(shp), O_CREAT|O_RDWR, S_IREAD|S_IWRITE);
2173 if (fd == -1) {
2174 error = EFAULT;
2175 goto fail;
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);
2186 } else {
2187 SH_DOOR_DESC(shp) = door_xcreate(event_deliver_service,
2188 (void *)shp,
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));
2197 error = EFAULT;
2198 goto fail;
2201 (void) fdetach(SH_DOOR_NAME(shp));
2202 if (fattach(SH_DOOR_DESC(shp), SH_DOOR_NAME(shp)) != 0) {
2203 error = EFAULT;
2204 goto fail;
2206 (void) close(fd);
2208 if (update_publisher_cache(sub_info, SE_BIND_REGISTRATION,
2209 sub_id, 0, NULL) != 0) {
2210 error = errno;
2211 (void) update_kernel_registration(shp, SUBSCRIBER,
2212 SE_UNBIND_REGISTRATION, &sub_id, 0, NULL);
2213 goto fail;
2216 SH_BOUND(shp) = 1;
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, NULL,
2223 (void *(*)(void *))subscriber_event_handler,
2224 shp, THR_BOUND, &sub_info->sp_handler_tid) == 0;
2225 } else {
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;
2246 if (!created) {
2247 error = EFAULT;
2248 goto fail;
2251 (void) mutex_unlock(SH_LOCK(shp));
2253 return (0);
2255 fail:
2256 (void) close(fd);
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);
2262 free(sub_info);
2263 if (SH_ID(shp)) {
2264 (void) update_kernel_registration(shp, SUBSCRIBER,
2265 SE_UNBIND_REGISTRATION, &sub_id, 0, NULL);
2266 SH_ID(shp) = 0;
2268 if (SH_BOUND(shp)) {
2269 (void) update_publisher_cache(sub_info, SE_UNBIND_REGISTRATION,
2270 sub_id, 0, NULL);
2271 free(SH_DOOR_NAME(shp));
2272 SH_BOUND(shp) = 0;
2274 (void) mutex_unlock(SH_LOCK(shp));
2276 errno = error;
2278 return (-1);
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,
2309 int subclass_num)
2311 int error;
2312 char *event_class = (char *)ev_class;
2313 char **event_subclass_list = (char **)ev_subclass;
2314 char *nvlbuf = NULL;
2315 size_t datalen;
2316 nvlist_t *nvl;
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));
2323 errno = EINVAL;
2324 return (-1);
2327 if (nvlist_alloc(&nvl, NV_UNIQUE_NAME_TYPE, 0) != 0) {
2328 (void) mutex_unlock(SH_LOCK(shp));
2329 return (-1);
2331 if (nvlist_add_string_array(nvl, event_class, event_subclass_list,
2332 subclass_num) != 0) {
2333 nvlist_free(nvl);
2334 (void) mutex_unlock(SH_LOCK(shp));
2335 return (-1);
2337 if (nvlist_pack(nvl, &nvlbuf, &datalen, NV_ENCODE_NATIVE, 0) != 0) {
2338 nvlist_free(nvl);
2339 (void) mutex_unlock(SH_LOCK(shp));
2340 return (-1);
2342 nvlist_free(nvl);
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)
2347 != 0) {
2348 error = errno;
2349 free(nvlbuf);
2350 (void) mutex_unlock(SH_LOCK(shp));
2351 errno = error;
2352 return (-1);
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) {
2358 error = errno;
2359 free(nvlbuf);
2360 (void) mutex_unlock(SH_LOCK(shp));
2361 errno = error;
2362 return (-1);
2365 free(nvlbuf);
2367 (void) mutex_unlock(SH_LOCK(shp));
2369 return (0);
2373 * sysevent_unregister_event - Unregister an event class and associated
2374 * subclasses for an event subscriber
2376 void
2377 sysevent_unregister_event(sysevent_handle_t *shp, const char *class)
2379 size_t class_sz;
2381 (void) mutex_lock(SH_LOCK(shp));
2383 if (!SH_BOUND(shp)) {
2384 (void) mutex_unlock(SH_LOCK(shp));
2385 return;
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));
2400 static int
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,
2407 0, NULL) != 0) {
2408 dprint("cleanup_id: Unable to clean "
2409 "up %s/%d\n", SH_CHANNEL_NAME(shp), id);
2410 return (-1);
2413 return (0);
2417 * sysevent_cleanup_subscribers: Allows the caller to cleanup resources
2418 * allocated to unresponsive subscribers.
2420 void
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)) {
2428 return;
2431 for (i = 1; i <= MAX_SUBSCRIBERS; ++i) {
2433 sub = SH_SUBSCRIBER(shp, i);
2434 if (sub == NULL) {
2435 continue;
2438 if ((sub_fd = open(sub->sd_door_name, O_RDONLY)) == -1) {
2439 continue;
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) */
2447 if (error != EBADF)
2448 continue;
2450 if (cleanup_id(shp, i, SUBSCRIBER) != 0)
2451 continue;
2453 cache_remove_class(shp, EC_ALL, i);
2455 free(sub->sd_door_name);
2456 free(sub);
2457 SH_SUBSCRIBER(shp, i) = NULL;
2463 * sysevent_cleanup_publishers: Allows stale publisher handles to be deallocated
2464 * as needed.
2466 void
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.
2475 void
2476 sysevent_unbind_subscriber(sysevent_handle_t *shp)
2478 subscriber_priv_t *sub_info;
2480 if (shp == NULL)
2481 return;
2483 (void) mutex_lock(SH_LOCK(shp));
2484 if (SH_BOUND(shp) == 0) {
2485 (void) mutex_unlock(SH_LOCK(shp));
2486 return;
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
2504 * complete.
2506 (void) mutex_lock(&sub_info->sp_qlock);
2507 SH_BOUND(shp) = 0;
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 != NULL)
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);
2517 free(sub_info);
2518 free(SH_DOOR_NAME(shp));
2519 (void) mutex_unlock(SH_LOCK(shp));
2523 * sysevent_unbind_publisher: Unbind publisher from the sysevent channel.
2525 void
2526 sysevent_unbind_publisher(sysevent_handle_t *shp)
2528 if (shp == NULL)
2529 return;
2531 (void) mutex_lock(SH_LOCK(shp));
2532 if (SH_BOUND(shp) == 0) {
2533 (void) mutex_unlock(SH_LOCK(shp));
2534 return;
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);
2544 SH_BOUND(shp) = 0;
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));
2552 SH_ID(shp) = 0;
2553 (void) mutex_unlock(SH_LOCK(shp));
2557 * Evolving APIs to subscribe to syseventd(1M) 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) {
2567 errno = EACCES;
2568 return (NULL);
2571 if (event_handler == NULL) {
2572 errno = EINVAL;
2573 return (NULL);
2576 if ((shp = sysevent_open_channel(SYSEVENTD_CHAN)) == NULL) {
2577 return (NULL);
2580 if (sysevent_bind_xsubscriber(shp, event_handler, subattr) != 0) {
2582 * Ask syseventd to clean-up any stale subcribers and try to
2583 * to bind again
2585 if (errno == EBUSY) {
2586 int pub_fd;
2587 char door_name[MAXPATHLEN];
2588 uint32_t result;
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);
2594 errno = EINVAL;
2595 return (NULL);
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,
2606 subattr) != 0) {
2607 sysevent_close_channel(shp);
2608 return (NULL);
2610 } else {
2611 sysevent_close_channel(shp);
2612 return (NULL);
2616 return (shp);
2620 * sysevent_bind_handle - Bind application event handler for syseventd
2621 * subscription.
2623 sysevent_handle_t *
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.
2633 sysevent_handle_t *
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
2643 void
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(1M) 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));
2662 void
2663 sysevent_unsubscribe_event(sysevent_handle_t *shp, const char *event_class)
2665 sysevent_unregister_event(shp, event_class);