1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
24 */
25
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>
47
48 #include "libsysevent.h"
49 #include "libsysevent_impl.h"
50
51 /*
52 * libsysevent - The system event framework library
53 *
54 * This library provides routines to help with marshalling
55 * and unmarshalling of data contained in a sysevent event
56 * buffer.
57 */
58
59 #define SE_ENCODE_METHOD NV_ENCODE_NATIVE
60
61 #define dprint if (libsysevent_debug) (void) printf
62 static int libsysevent_debug = 0;
63
64 static sysevent_t *se_unpack(sysevent_t *);
65 static int cleanup_id(sysevent_handle_t *shp, uint32_t id, int type);
66
67 /*
68 * The following routines allow system event publication to the sysevent
69 * framework.
70 */
71
72 /*
73 * sysevent_alloc - allocate a sysevent buffer
74 */
75 static sysevent_t *
sysevent_alloc(char * class,int class_sz,char * subclass,int subclass_sz,char * pub,int pub_sz,nvlist_t * attr_list)76 sysevent_alloc(char *class, int class_sz, char *subclass, int subclass_sz,
77 char *pub, int pub_sz, nvlist_t *attr_list)
78 {
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;
85
86 if (attr_list != NULL) {
87 if (nvlist_size(attr_list, &nvlist_sz, SE_ENCODE_METHOD)
88 != 0) {
89 return (NULL);
90 }
91 }
92
93 /*
94 * Calculate and reserve space for the class, subclass and
95 * publisher strings in the event buffer
96 */
97
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);
102
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;
107
108 /*
109 * Allocate event buffer plus additional payload overhead.
110 */
111 ev = calloc(1, sizeof (sysevent_impl_t) + payload_sz);
112 if (ev == NULL) {
113 return (NULL);
114 }
115
116 /* Initialize the event buffer data */
117 SE_VERSION(ev) = SYS_EVENT_VERSION;
118 (void) bcopy(class, SE_CLASS_NAME(ev), class_sz);
119
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);
123
124 SE_PUB_OFF(ev) = SE_SUBCLASS_OFF(ev) + aligned_subclass_sz;
125 (void) bcopy(pub, SE_PUB_NAME(ev), pub_sz);
126
127 SE_PAYLOAD_SZ(ev) = payload_sz;
128 SE_ATTR_PTR(ev) = (uint64_t)0;
129
130 /* Check for attribute list */
131 if (attr_list == NULL) {
132 return (ev);
133 }
134
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);
143 }
144
145 return (ev);
146 }
147
148 /*
149 * sysevent_post_event - generate a system event via the sysevent framework
150 */
151 int
sysevent_post_event(char * class,char * subclass,char * vendor,char * pub_name,nvlist_t * attr_list,sysevent_id_t * eid)152 sysevent_post_event(char *class, char *subclass, char *vendor, char *pub_name,
153 nvlist_t *attr_list, sysevent_id_t *eid)
154 {
155 int error;
156 sysevent_t *ev;
157
158 ev = sysevent_alloc_event(class, subclass, vendor, pub_name, attr_list);
159 if (ev == NULL) {
160 return (-1);
161 }
162
163 error = modctl(MODEVENTS, (uintptr_t)MODEVENTS_POST_EVENT,
164 (uintptr_t)ev, (uintptr_t)SE_SIZE(ev), (uintptr_t)eid, 0);
165
166 sysevent_free(ev);
167
168 if (error) {
169 errno = EIO;
170 return (-1);
171 }
172
173 return (0);
174 }
175
176 /*
177 * The following routines are used to free or duplicate a
178 * sysevent event buffer.
179 */
180
181 /*
182 * sysevent_dup - Allocate and copy an event buffer
183 * Copies both packed and unpacked to unpacked sysevent.
184 */
185 sysevent_t *
sysevent_dup(sysevent_t * ev)186 sysevent_dup(sysevent_t *ev)
187 {
188 nvlist_t *nvl, *cnvl = NULL;
189 uint64_t attr_offset;
190 sysevent_t *copy;
191
192 if (SE_FLAG(ev) == SE_PACKED_BUF)
193 return (se_unpack(ev));
194
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);
201
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);
206 }
207
208 SE_ATTR_PTR(copy) = (uintptr_t)cnvl;
209 SE_FLAG(copy) = 0; /* unpacked */
210 return (copy);
211 }
212
213 /*
214 * sysevent_free - Free memory allocated for an event buffer
215 */
216 void
sysevent_free(sysevent_t * ev)217 sysevent_free(sysevent_t *ev)
218 {
219 nvlist_t *attr_list = (nvlist_t *)(uintptr_t)SE_ATTR_PTR(ev);
220
221 nvlist_free(attr_list);
222 free(ev);
223 }
224
225 /*
226 * The following routines are used to extract attribute data from a sysevent
227 * handle.
228 */
229
230 /*
231 * sysevent_get_attr_list - allocate and return an attribute associated with
232 * the given sysevent buffer.
233 */
234 int
sysevent_get_attr_list(sysevent_t * ev,nvlist_t ** nvlist)235 sysevent_get_attr_list(sysevent_t *ev, nvlist_t **nvlist)
236 {
237 int error;
238 caddr_t attr;
239 size_t attr_len;
240 uint64_t attr_offset;
241 nvlist_t *nvl;
242
243 *nvlist = NULL;
244
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);
250 }
251 if ((error = nvlist_dup(nvl, nvlist, 0)) != 0) {
252 if (error == ENOMEM) {
253 errno = error;
254 } else {
255 errno = EINVAL;
256 }
257 return (-1);
258 }
259 return (0);
260 }
261
262 attr_offset = SE_ATTR_OFF(ev);
263 if (SE_SIZE(ev) == attr_offset) {
264 return (0);
265 }
266
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;
275 }
276 return (-1);
277 }
278
279 return (0);
280 }
281
282 /*
283 * sysevent_attr_name - Get name of attribute
284 */
285 char *
sysevent_attr_name(sysevent_attr_t * attr)286 sysevent_attr_name(sysevent_attr_t *attr)
287 {
288 if (attr == NULL) {
289 errno = EINVAL;
290 return (NULL);
291 }
292 return (nvpair_name((nvpair_t *)attr));
293 }
294
295 /*
296 * sysevent_attr_value - Get attribute value data and type
297 */
298 int
sysevent_attr_value(sysevent_attr_t * attr,sysevent_value_t * se_value)299 sysevent_attr_value(sysevent_attr_t *attr, sysevent_value_t *se_value)
300 {
301 nvpair_t *nvp = attr;
302
303 if (nvp == NULL)
304 return (EINVAL);
305
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);
352 }
353 return (0);
354 }
355
356 /*
357 * sysevent_attr_next - Get next attribute in event attribute list
358 */
359 sysevent_attr_t *
sysevent_attr_next(sysevent_t * ev,sysevent_attr_t * attr)360 sysevent_attr_next(sysevent_t *ev, sysevent_attr_t *attr)
361 {
362 nvlist_t *nvl;
363 nvpair_t *nvp = attr;
364
365 /* all user visible sysevent_t's are unpacked */
366 assert(SE_FLAG(ev) != SE_PACKED_BUF);
367
368 if (SE_ATTR_PTR(ev) == (uint64_t)0) {
369 return (NULL);
370 }
371
372 nvl = (nvlist_t *)(uintptr_t)SE_ATTR_PTR(ev);
373 return (nvlist_next_nvpair(nvl, nvp));
374 }
375
376 /*
377 * sysevent_lookup_attr - Lookup attribute by name and datatype.
378 */
379 int
sysevent_lookup_attr(sysevent_t * ev,char * name,int datatype,sysevent_value_t * se_value)380 sysevent_lookup_attr(sysevent_t *ev, char *name, int datatype,
381 sysevent_value_t *se_value)
382 {
383 nvpair_t *nvp;
384 nvlist_t *nvl;
385
386 assert(SE_FLAG(ev) != SE_PACKED_BUF);
387
388 if (SE_ATTR_PTR(ev) == (uint64_t)0) {
389 return (ENOENT);
390 }
391
392 /*
393 * sysevent matches on both name and datatype
394 * nvlist_look mataches name only. So we walk
395 * nvlist manually here.
396 */
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);
405 }
406 return (ENOENT);
407 }
408
409 /* Routines to extract event header information */
410
411 /*
412 * sysevent_get_class - Get class id
413 */
414 int
sysevent_get_class(sysevent_t * ev)415 sysevent_get_class(sysevent_t *ev)
416 {
417 return (SE_CLASS(ev));
418 }
419
420 /*
421 * sysevent_get_subclass - Get subclass id
422 */
423 int
sysevent_get_subclass(sysevent_t * ev)424 sysevent_get_subclass(sysevent_t *ev)
425 {
426 return (SE_SUBCLASS(ev));
427 }
428
429 /*
430 * sysevent_get_class_name - Get class name string
431 */
432 char *
sysevent_get_class_name(sysevent_t * ev)433 sysevent_get_class_name(sysevent_t *ev)
434 {
435 return (SE_CLASS_NAME(ev));
436 }
437
438 typedef enum {
439 PUB_VEND,
440 PUB_KEYWD,
441 PUB_NAME,
442 PUB_PID
443 } se_pub_id_t;
444
445 /*
446 * sysevent_get_pub - Get publisher name string
447 */
448 char *
sysevent_get_pub(sysevent_t * ev)449 sysevent_get_pub(sysevent_t *ev)
450 {
451 return (SE_PUB_NAME(ev));
452 }
453
454 /*
455 * Get the requested string pointed by the token.
456 *
457 * Return NULL if not found or for insufficient memory.
458 */
459 static char *
parse_pub_id(sysevent_t * ev,se_pub_id_t token)460 parse_pub_id(sysevent_t *ev, se_pub_id_t token)
461 {
462 int i;
463 char *pub_id, *pub_element, *str, *next;
464
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);
471 }
472 }
473
474 pub_element = strdup(str);
475 free(pub_id);
476 return (pub_element);
477 }
478
479 /*
480 * Return a pointer to the string following the token
481 *
482 * Note: This is a dedicated function for parsing
483 * publisher strings and not for general purpose.
484 */
485 static const char *
pub_idx(const char * pstr,int token)486 pub_idx(const char *pstr, int token)
487 {
488 int i;
489
490 for (i = 1; i <= token; i++) {
491 if ((pstr = index(pstr, ':')) == NULL)
492 return (NULL);
493 pstr++;
494 }
495
496 /* String might be empty */
497 if (pstr) {
498 if (*pstr == '\0' || *pstr == ':')
499 return (NULL);
500 }
501 return (pstr);
502 }
503
504 char *
sysevent_get_vendor_name(sysevent_t * ev)505 sysevent_get_vendor_name(sysevent_t *ev)
506 {
507 return (parse_pub_id(ev, PUB_VEND));
508 }
509
510 char *
sysevent_get_pub_name(sysevent_t * ev)511 sysevent_get_pub_name(sysevent_t *ev)
512 {
513 return (parse_pub_id(ev, PUB_NAME));
514 }
515
516 /*
517 * Provide the pid encoded in the publisher string
518 * w/o allocating any resouces.
519 */
520 void
sysevent_get_pid(sysevent_t * ev,pid_t * pid)521 sysevent_get_pid(sysevent_t *ev, pid_t *pid)
522 {
523 const char *part_str;
524 const char *pub_str = sysevent_get_pub(ev);
525
526 *pid = (pid_t)SE_KERN_PID;
527
528 part_str = pub_idx(pub_str, PUB_KEYWD);
529 if (part_str != NULL && strstr(part_str, SE_KERN_PUB) != NULL)
530 return;
531
532 if ((part_str = pub_idx(pub_str, PUB_PID)) == NULL)
533 return;
534
535 *pid = (pid_t)atoi(part_str);
536 }
537
538 /*
539 * sysevent_get_subclass_name - Get subclass name string
540 */
541 char *
sysevent_get_subclass_name(sysevent_t * ev)542 sysevent_get_subclass_name(sysevent_t *ev)
543 {
544 return (SE_SUBCLASS_NAME(ev));
545 }
546
547 /*
548 * sysevent_get_seq - Get event sequence id
549 */
550 uint64_t
sysevent_get_seq(sysevent_t * ev)551 sysevent_get_seq(sysevent_t *ev)
552 {
553 return (SE_SEQ(ev));
554 }
555
556 /*
557 * sysevent_get_time - Get event timestamp
558 */
559 void
sysevent_get_time(sysevent_t * ev,hrtime_t * etime)560 sysevent_get_time(sysevent_t *ev, hrtime_t *etime)
561 {
562 *etime = SE_TIME(ev);
563 }
564
565 /*
566 * sysevent_get_size - Get event buffer size
567 */
568 size_t
sysevent_get_size(sysevent_t * ev)569 sysevent_get_size(sysevent_t *ev)
570 {
571 return ((size_t)SE_SIZE(ev));
572 }
573
574 /*
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.
578 */
579
580 /*
581 * sysevent_alloc_event -
582 * allocate a sysevent buffer for sending through an established event
583 * channel.
584 */
585 sysevent_t *
sysevent_alloc_event(char * class,char * subclass,char * vendor,char * pub_name,nvlist_t * attr_list)586 sysevent_alloc_event(char *class, char *subclass, char *vendor, char *pub_name,
587 nvlist_t *attr_list)
588 {
589 int class_sz, subclass_sz, pub_sz;
590 char *pub_id;
591 sysevent_t *ev;
592
593 if ((class == NULL) || (subclass == NULL) || (vendor == NULL) ||
594 (pub_name == NULL)) {
595 errno = EINVAL;
596 return (NULL);
597 }
598
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);
605 }
606
607 /*
608 * Calculate the publisher size plus string seperators and maximum
609 * pid characters
610 */
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);
615 }
616 pub_id = malloc(pub_sz);
617 if (pub_id == NULL) {
618 errno = ENOMEM;
619 return (NULL);
620 }
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);
626 }
627 pub_sz = strlen(pub_id) + 1;
628
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);
635 }
636
637 return (ev);
638 }
639
640 /*
641 * se_unpack - unpack nvlist to a searchable list.
642 * If already unpacked, will do a dup.
643 */
644 static sysevent_t *
se_unpack(sysevent_t * ev)645 se_unpack(sysevent_t *ev)
646 {
647 caddr_t attr;
648 size_t attr_len;
649 nvlist_t *attrp = NULL;
650 uint64_t attr_offset;
651 sysevent_t *copy;
652
653 assert(SE_FLAG(ev) == SE_PACKED_BUF);
654
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 */
662
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);
668 }
669 if (nvlist_unpack(attr, attr_len, &attrp, 0) != 0) {
670 free(copy);
671 return (NULL);
672 }
673
674 SE_ATTR_PTR(copy) = (uintptr_t)attrp;
675 return (copy);
676 }
677
678 /*
679 * se_print - Prints elements in an event buffer
680 */
681 void
se_print(FILE * fp,sysevent_t * ev)682 se_print(FILE *fp, sysevent_t *ev)
683 {
684 char *vendor, *pub;
685 pid_t pid;
686 hrtime_t hrt;
687 nvlist_t *attr_list = NULL;
688
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);
697 }
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);
702 }
703
704 if (sysevent_get_attr_list(ev, &attr_list) == 0 && attr_list != NULL) {
705 nvlist_print(fp, attr_list);
706 nvlist_free(attr_list);
707 }
708 }
709
710 /*
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.
716 *
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.
721 *
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.
728 *
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.
733 *
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.
738 *
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.
744 *
745 */
746
747 /*
748 * Door handlers for the channel subscribers
749 */
750
751 /*
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.
757 */
758 static void
subscriber_event_handler(sysevent_handle_t * shp)759 subscriber_event_handler(sysevent_handle_t *shp)
760 {
761 subscriber_priv_t *sub_info;
762 sysevent_queue_t *evqp;
763
764 sub_info = (subscriber_priv_t *)SH_PRIV_DATA(shp);
765
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();
769
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);
774 }
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;
784 }
785 if (!SH_BOUND(shp)) {
786 (void) mutex_unlock(&sub_info->sp_qlock);
787 return;
788 }
789 }
790
791 /* NOTREACHED */
792 }
793
794 /*
795 * Data structure used to communicate event subscription cache updates
796 * to publishers via a registration door
797 */
798 struct reg_args {
799 uint32_t ra_sub_id;
800 uint32_t ra_op;
801 uint64_t ra_buf_ptr;
802 };
803
804
805 /*
806 * event_deliver_service - generic event delivery service routine. This routine
807 * is called in response to a door call to post an event.
808 *
809 */
810 /*ARGSUSED*/
811 static void
event_deliver_service(void * cookie,char * args,size_t alen,door_desc_t * ddp,uint_t ndid)812 event_deliver_service(void *cookie, char *args, size_t alen,
813 door_desc_t *ddp, uint_t ndid)
814 {
815 int ret = 0;
816 subscriber_priv_t *sub_info;
817 sysevent_handle_t *shp;
818 sysevent_queue_t *new_eq;
819
820 if (args == NULL || alen < sizeof (uint32_t)) {
821 ret = EINVAL;
822 goto return_from_door;
823 }
824
825 /* Publisher checking on subscriber */
826 if (alen == sizeof (uint32_t)) {
827 ret = 0;
828 goto return_from_door;
829 }
830
831 shp = (sysevent_handle_t *)cookie;
832 if (shp == NULL) {
833 ret = EBADF;
834 goto return_from_door;
835 }
836
837 /*
838 * Mustn't block if we are trying to update the registration with
839 * the publisher
840 */
841 if (mutex_trylock(SH_LOCK(shp)) != 0) {
842 ret = EAGAIN;
843 goto return_from_door;
844 }
845
846 if (!SH_BOUND(shp)) {
847 ret = EBADF;
848 (void) mutex_unlock(SH_LOCK(shp));
849 goto return_from_door;
850 }
851
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;
857 }
858
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;
865 }
866
867 /*
868 * Allocate and copy the event buffer into the subscriber's
869 * address space
870 */
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;
877 }
878 (void) bcopy(args, new_eq->sq_ev, alen);
879
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;
885 }
886 sub_info->sp_evq_tail = new_eq;
887
888 (void) cond_signal(&sub_info->sp_cv);
889 (void) mutex_unlock(&sub_info->sp_qlock);
890 (void) mutex_unlock(SH_LOCK(shp));
891
892 return_from_door:
893 (void) door_return((void *)&ret, sizeof (ret), NULL, 0);
894 (void) door_return(NULL, 0, NULL, 0);
895 }
896
897 /*
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.
902 *
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.
906 *
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.
910 *
911 * The following routines provide a mechanism to update publisher and subscriber
912 * information for a specified channel.
913 */
914
915 /*
916 * clnt_deliver_event - Deliver an event through the consumer's event
917 * delivery door
918 *
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.
921 */
922 static int
clnt_deliver_event(int service_door,void * data,size_t datalen,void * result,size_t rlen)923 clnt_deliver_event(int service_door, void *data, size_t datalen,
924 void *result, size_t rlen)
925 {
926 int error = 0;
927 door_arg_t door_arg;
928
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;
935
936 /*
937 * Make door call
938 */
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;
945 }
946 }
947
948 return (error);
949 }
950
951 static int
update_publisher_cache(subscriber_priv_t * sub_info,int update_op,uint32_t sub_id,size_t datasz,uchar_t * data)952 update_publisher_cache(subscriber_priv_t *sub_info, int update_op,
953 uint32_t sub_id, size_t datasz, uchar_t *data)
954 {
955 int pub_fd;
956 uint32_t result = 0;
957 struct reg_args *rargs;
958
959 rargs = (struct reg_args *)calloc(1, sizeof (struct reg_args) +
960 datasz);
961 if (rargs == NULL) {
962 errno = ENOMEM;
963 return (-1);
964 }
965
966 rargs->ra_sub_id = sub_id;
967 rargs->ra_op = update_op;
968 bcopy(data, (char *)&rargs->ra_buf_ptr, datasz);
969
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);
974
975 free(rargs);
976 if (result != 0) {
977 errno = result;
978 return (-1);
979 }
980
981 return (0);
982 }
983
984
985 /*
986 * update_kernel_registration - update the in-kernel registration for the
987 * given channel.
988 */
989 static int
update_kernel_registration(sysevent_handle_t * shp,int update_type,int update_op,uint32_t * sub_id,size_t datasz,uchar_t * data)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)
992 {
993 int error;
994 char *channel_name = SH_CHANNEL_NAME(shp);
995 se_pubsub_t udata;
996
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;
1002
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);
1007 }
1008
1009 *sub_id = udata.ps_id;
1010
1011 return (error);
1012 }
1013
1014 /*
1015 * get_kernel_registration - get the current subscriber registration for
1016 * the given channel
1017 */
1018 static nvlist_t *
get_kernel_registration(char * channel_name,uint32_t class_id)1019 get_kernel_registration(char *channel_name, uint32_t class_id)
1020 {
1021 char *nvlbuf;
1022 nvlist_t *nvl;
1023 se_pubsub_t udata;
1024
1025 nvlbuf = calloc(1, MAX_SUBSCRIPTION_SZ);
1026 if (nvlbuf == NULL) {
1027 return (NULL);
1028 }
1029
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;
1035
1036 if (modctl(MODEVENTS, (uintptr_t)MODEVENTS_REGISTER_EVENT,
1037 (uintptr_t)channel_name, (uintptr_t)nvlbuf, (uintptr_t)&udata, 0)
1038 != 0) {
1039
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);
1046
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);
1054 }
1055 } else {
1056 free(nvlbuf);
1057 return (NULL);
1058 }
1059 }
1060
1061 if (nvlist_unpack(nvlbuf, udata.ps_buflen, &nvl, 0) != 0) {
1062 free(nvlbuf);
1063 return (NULL);
1064 }
1065 free(nvlbuf);
1066
1067 return (nvl);
1068 }
1069
1070 /*
1071 * The following routines provide a mechanism for publishers to maintain
1072 * subscriber information.
1073 */
1074
1075 static void
dealloc_subscribers(sysevent_handle_t * shp)1076 dealloc_subscribers(sysevent_handle_t *shp)
1077 {
1078 int i;
1079 subscriber_data_t *sub;
1080
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);
1086 }
1087 SH_SUBSCRIBER(shp, i) = NULL;
1088 }
1089 }
1090
1091 /*ARGSUSED*/
1092 static int
alloc_subscriber(sysevent_handle_t * shp,uint32_t sub_id,int oflag)1093 alloc_subscriber(sysevent_handle_t *shp, uint32_t sub_id, int oflag)
1094 {
1095 subscriber_data_t *sub;
1096 char door_name[MAXPATHLEN];
1097
1098 if (SH_SUBSCRIBER(shp, sub_id) != NULL) {
1099 return (0);
1100 }
1101
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);
1107 }
1108 if (snprintf(door_name, MAXPATHLEN, "%s/%d",
1109 SH_CHANNEL_PATH(shp), sub_id) >= MAXPATHLEN) {
1110 free(sub);
1111 return (-1);
1112 }
1113
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);
1119 }
1120
1121 SH_SUBSCRIBER(shp, sub_id) = sub;
1122 return (0);
1123
1124 }
1125
1126 /*
1127 * The following routines are used to update and maintain the registration cache
1128 * for a particular sysevent channel.
1129 */
1130
1131 static uint32_t
hash_func(const char * s)1132 hash_func(const char *s)
1133 {
1134 uint32_t result = 0;
1135 uint_t g;
1136
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;
1144 }
1145 }
1146
1147 return (result);
1148 }
1149
1150 subclass_lst_t *
cache_find_subclass(class_lst_t * c_list,char * subclass)1151 cache_find_subclass(class_lst_t *c_list, char *subclass)
1152 {
1153 subclass_lst_t *sc_list;
1154
1155 if (c_list == NULL)
1156 return (NULL);
1157
1158 sc_list = c_list->cl_subclass_list;
1159
1160 while (sc_list != NULL) {
1161 if (strcmp(sc_list->sl_name, subclass) == 0) {
1162 return (sc_list);
1163 }
1164 sc_list = sc_list->sl_next;
1165 }
1166
1167 return (NULL);
1168 }
1169
1170
1171 static class_lst_t *
cache_find_class(sysevent_handle_t * shp,char * class)1172 cache_find_class(sysevent_handle_t *shp, char *class)
1173 {
1174 int index;
1175 class_lst_t *c_list;
1176 class_lst_t **class_hash = SH_CLASS_HASH(shp);
1177
1178 if (strcmp(class, EC_ALL) == 0) {
1179 return (class_hash[0]);
1180 }
1181
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;
1187 }
1188 c_list = c_list->cl_next;
1189 }
1190
1191 return (c_list);
1192 }
1193
1194 static int
cache_insert_subclass(class_lst_t * c_list,char ** subclass_names,int subclass_num,uint32_t sub_id)1195 cache_insert_subclass(class_lst_t *c_list, char **subclass_names,
1196 int subclass_num, uint32_t sub_id)
1197 {
1198 int i;
1199 subclass_lst_t *sc_list;
1200
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);
1210
1211 sc_list->sl_name = strdup(subclass_names[i]);
1212 if (sc_list->sl_name == NULL) {
1213 free(sc_list);
1214 return (-1);
1215 }
1216
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;
1220 }
1221 }
1222
1223 return (0);
1224 }
1225
1226 static int
cache_insert_class(sysevent_handle_t * shp,char * class,char ** subclass_names,int subclass_num,uint32_t sub_id)1227 cache_insert_class(sysevent_handle_t *shp, char *class,
1228 char **subclass_names, int subclass_num, uint32_t sub_id)
1229 {
1230 class_lst_t *c_list;
1231
1232 if (strcmp(class, EC_ALL) == 0) {
1233 char *subclass_all = EC_SUB_ALL;
1234
1235 (void) cache_insert_subclass(SH_CLASS_HASH(shp)[0],
1236 (char **)&subclass_all, 1, sub_id);
1237 return (0);
1238 }
1239
1240 /* New class, add to the registration cache */
1241 if ((c_list = cache_find_class(shp, class)) == NULL) {
1242
1243 c_list = (class_lst_t *)calloc(1, sizeof (class_lst_t));
1244 if (c_list == NULL) {
1245 return (1);
1246 }
1247 c_list->cl_name = strdup(class);
1248 if (c_list->cl_name == NULL) {
1249 free(c_list);
1250 return (1);
1251 }
1252
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);
1259 }
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);
1266 }
1267 c_list->cl_next = SH_CLASS_HASH(shp)[CLASS_HASH(class)];
1268 SH_CLASS_HASH(shp)[CLASS_HASH(class)] = c_list;
1269
1270 }
1271
1272 /* Update the subclass list */
1273 if (cache_insert_subclass(c_list, subclass_names, subclass_num,
1274 sub_id) != 0)
1275 return (1);
1276
1277 return (0);
1278 }
1279
1280 static void
cache_remove_all_class(sysevent_handle_t * shp,uint32_t sub_id)1281 cache_remove_all_class(sysevent_handle_t *shp, uint32_t sub_id)
1282 {
1283 int i;
1284 class_lst_t *c_list;
1285 subclass_lst_t *sc_list;
1286
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;
1294 }
1295 c_list = c_list->cl_next;
1296 }
1297 }
1298 }
1299
1300 static void
cache_remove_class(sysevent_handle_t * shp,char * class,uint32_t sub_id)1301 cache_remove_class(sysevent_handle_t *shp, char *class, uint32_t sub_id)
1302 {
1303 class_lst_t *c_list;
1304 subclass_lst_t *sc_list;
1305
1306 if (strcmp(class, EC_ALL) == 0) {
1307 cache_remove_all_class(shp, sub_id);
1308 return;
1309 }
1310
1311 if ((c_list = cache_find_class(shp, class)) == NULL) {
1312 return;
1313 }
1314
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;
1319 }
1320 }
1321
1322 static void
free_cached_registration(sysevent_handle_t * shp)1323 free_cached_registration(sysevent_handle_t *shp)
1324 {
1325 int i;
1326 class_lst_t *clist, *next_clist;
1327 subclass_lst_t *sc_list, *next_sc;
1328
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;
1338 }
1339 free(clist->cl_name);
1340 next_clist = clist->cl_next;
1341 free(clist);
1342 clist = next_clist;
1343 }
1344 SH_CLASS_HASH(shp)[i] = NULL;
1345 }
1346 }
1347
1348 static int
create_cached_registration(sysevent_handle_t * shp,class_lst_t ** class_hash)1349 create_cached_registration(sysevent_handle_t *shp,
1350 class_lst_t **class_hash)
1351 {
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;
1360
1361 for (i = 0; i < CLASS_HASH_SZ + 1; ++i) {
1362
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;
1370 }
1371 }
1372
1373
1374 nvpair = NULL;
1375 if ((nvpair = nvlist_next_nvpair(nvl, nvpair)) == NULL) {
1376 goto create_failed;
1377 }
1378
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;
1384 }
1385 clist = (class_lst_t *)
1386 calloc(1, sizeof (class_lst_t));
1387 if (clist == NULL) {
1388 goto create_failed;
1389 }
1390
1391 clist->cl_name = strdup(class_name);
1392 if (clist->cl_name == NULL) {
1393 free(clist);
1394 goto create_failed;
1395 }
1396
1397 /*
1398 * Extract the subclass name and registration
1399 * from the nvpair
1400 */
1401 if ((nvpair = nvlist_next_nvpair(nvl, nvpair))
1402 == NULL) {
1403 free(clist->cl_name);
1404 free(clist);
1405 goto create_failed;
1406 }
1407
1408 clist->cl_next = class_hash[i];
1409 class_hash[i] = clist;
1410
1411 for (;;) {
1412
1413 sc_list = (subclass_lst_t *)calloc(1,
1414 sizeof (subclass_lst_t));
1415 if (sc_list == NULL) {
1416 goto create_failed;
1417 }
1418
1419 sc_list->sl_next = clist->cl_subclass_list;
1420 clist->cl_subclass_list = sc_list;
1421
1422 sc_list->sl_name = strdup(nvpair_name(nvpair));
1423 if (sc_list->sl_name == NULL) {
1424 goto create_failed;
1425 }
1426
1427 if (nvpair_value_byte_array(nvpair,
1428 &subscribers, &num_elem) != 0) {
1429 goto create_failed;
1430 }
1431 bcopy(subscribers, (uchar_t *)sc_list->sl_num,
1432 MAX_SUBSCRIBERS + 1);
1433
1434 for (j = 1; j <= MAX_SUBSCRIBERS; ++j) {
1435 if (sc_list->sl_num[j] == 0)
1436 continue;
1437
1438 if (alloc_subscriber(shp, j, 1) != 0) {
1439 goto create_failed;
1440 }
1441 }
1442
1443 /*
1444 * Check next nvpair - either subclass or
1445 * class
1446 */
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;
1454 }
1455 }
1456 }
1457 nvlist_free(nvl);
1458 }
1459 return (0);
1460
1461 create_failed:
1462 dealloc_subscribers(shp);
1463 free_cached_registration(shp);
1464 nvlist_free(nvl);
1465 return (-1);
1466
1467 }
1468
1469 /*
1470 * cache_update_service - generic event publisher service routine. This routine
1471 * is called in response to a registration cache update.
1472 *
1473 */
1474 /*ARGSUSED*/
1475 static void
cache_update_service(void * cookie,char * args,size_t alen,door_desc_t * ddp,uint_t ndid)1476 cache_update_service(void *cookie, char *args, size_t alen,
1477 door_desc_t *ddp, uint_t ndid)
1478 {
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;
1489
1490 if (alen < sizeof (struct reg_args) || cookie == NULL) {
1491 ret = EINVAL;
1492 goto return_from_door;
1493 }
1494
1495 /* LINTED: E_BAD_PTR_CAST_ALIGN */
1496 rargs = (struct reg_args *)args;
1497 shp = (sysevent_handle_t *)cookie;
1498
1499 datalen = alen - sizeof (struct reg_args);
1500 sub_id = rargs->ra_sub_id;
1501
1502 (void) mutex_lock(SH_LOCK(shp));
1503
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:
1511
1512 sub = SH_SUBSCRIBER(shp, sub_id);
1513 if (sub == NULL)
1514 break;
1515
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;
1520
1521 break;
1522 case SE_BIND_REGISTRATION:
1523
1524 /* New subscriber */
1525 if (alloc_subscriber(shp, sub_id, 0) != 0) {
1526 ret = ENOMEM;
1527 break;
1528 }
1529 break;
1530 case SE_REGISTER:
1531
1532 if (SH_SUBSCRIBER(shp, sub_id) == NULL) {
1533 ret = EINVAL;
1534 break;
1535 }
1536 /* Get new registration data */
1537 if (nvlist_unpack((char *)&rargs->ra_buf_ptr, datalen,
1538 &nvl, 0) != 0) {
1539 ret = EFAULT;
1540 break;
1541 }
1542 if ((nvpair = nvlist_next_nvpair(nvl, nvpair)) == NULL) {
1543 nvlist_free(nvl);
1544 ret = EFAULT;
1545 break;
1546 }
1547 if (nvpair_value_string_array(nvpair, &event_list, &num_elem)
1548 != 0) {
1549 nvlist_free(nvl);
1550 ret = EFAULT;
1551 break;
1552 }
1553 class = nvpair_name(nvpair);
1554
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;
1562 }
1563
1564 nvlist_free(nvl);
1565
1566 break;
1567 case SE_CLEANUP:
1568 /* Cleanup stale subscribers */
1569 sysevent_cleanup_subscribers(shp);
1570 break;
1571 default:
1572 ret = EINVAL;
1573 }
1574
1575 (void) mutex_unlock(SH_LOCK(shp));
1576
1577 return_from_door:
1578 (void) door_return((void *)&ret, sizeof (ret), NULL, 0);
1579 (void) door_return(NULL, 0, NULL, 0);
1580 }
1581
1582 /*
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()
1589 */
1590 int
sysevent_send_event(sysevent_handle_t * shp,sysevent_t * ev)1591 sysevent_send_event(sysevent_handle_t *shp, sysevent_t *ev)
1592 {
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;
1602
1603 /* Check for proper registration */
1604 event_class = sysevent_get_class_name(ev);
1605 event_subclass = sysevent_get_subclass_name(ev);
1606
1607 (void) mutex_lock(SH_LOCK(shp));
1608
1609 send_event:
1610
1611 want_resend = 0;
1612 if (!SH_BOUND(shp)) {
1613 (void) mutex_unlock(SH_LOCK(shp));
1614 errno = EINVAL;
1615 return (-1);
1616 }
1617
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;
1622
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;
1629
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;
1636
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;
1643
1644 sub = SH_SUBSCRIBER(shp, i);
1645 assert(sub != NULL);
1646
1647 /* Check for active subscriber */
1648 if (!(sub->sd_flag & ACTIVE)) {
1649 dprint("sysevent_send_event: subscriber %d inactive\n",
1650 i);
1651 continue;
1652 }
1653
1654 /* Process only resend requests */
1655 if (resend_cnt > 0 && !(sub->sd_flag & SEND_AGAIN)) {
1656 continue;
1657 }
1658
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;
1663 }
1664 result = 0;
1665 error = clnt_deliver_event(sub_fd, ev,
1666 sysevent_get_size(ev), &result, sizeof (result));
1667
1668 (void) close(sub_fd);
1669
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;
1682 }
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;
1696 }
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;
1703 }
1704 }
1705
1706 if (want_resend) {
1707 resend_cnt++;
1708 goto send_event;
1709 }
1710
1711 if (deliver_error) {
1712 sysevent_cleanup_subscribers(shp);
1713 (void) mutex_unlock(SH_LOCK(shp));
1714 errno = EFAULT;
1715 return (-1);
1716 }
1717
1718 (void) mutex_unlock(SH_LOCK(shp));
1719
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);
1725 }
1726
1727 return (0);
1728 }
1729
1730 /*
1731 * Common routine to establish an event channel through which an event
1732 * publisher or subscriber may post or receive events.
1733 */
1734 static sysevent_handle_t *
sysevent_open_channel_common(const char * channel_path)1735 sysevent_open_channel_common(const char *channel_path)
1736 {
1737 uint32_t sub_id = 0;
1738 char *begin_path;
1739 struct stat chan_stat;
1740 sysevent_handle_t *shp;
1741
1742
1743 if (channel_path == NULL || strlen(channel_path) + 1 > MAXPATHLEN) {
1744 errno = EINVAL;
1745 return (NULL);
1746 }
1747
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);
1752 }
1753 }
1754
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);
1767
1768 errno = EACCES;
1769 return (NULL);
1770 }
1771
1772 shp = calloc(1, sizeof (sysevent_impl_hdl_t));
1773 if (shp == NULL) {
1774 errno = ENOMEM;
1775 return (NULL);
1776 }
1777
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);
1784 }
1785
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;
1792 }
1793
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);
1802 }
1803
1804 (void) mutex_init(SH_LOCK(shp), USYNC_THREAD, NULL);
1805
1806 return (shp);
1807 }
1808
1809 /*
1810 * Establish a sysevent channel for publication and subscription
1811 */
1812 sysevent_handle_t *
sysevent_open_channel(const char * channel)1813 sysevent_open_channel(const char *channel)
1814 {
1815 int var_run_mounted = 0;
1816 char full_channel[MAXPATHLEN + 1];
1817 FILE *fp;
1818 struct stat chan_stat;
1819 struct extmnttab m;
1820
1821 if (channel == NULL) {
1822 errno = EINVAL;
1823 return (NULL);
1824 }
1825
1826 /*
1827 * Check that /var/run is mounted as tmpfs before allowing a channel
1828 * to be opened.
1829 */
1830 if ((fp = fopen(MNTTAB, "rF")) == NULL) {
1831 errno = EACCES;
1832 return (NULL);
1833 }
1834
1835 resetmnttab(fp);
1836
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;
1842 }
1843 }
1844 (void) fclose(fp);
1845
1846 if (!var_run_mounted) {
1847 errno = EACCES;
1848 return (NULL);
1849 }
1850
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);
1860 }
1861 }
1862 }
1863
1864 if (snprintf(full_channel, MAXPATHLEN, "%s/%s", CHAN_PATH, channel) >=
1865 MAXPATHLEN) {
1866 errno = EINVAL;
1867 return (NULL);
1868 }
1869
1870 return (sysevent_open_channel_common(full_channel));
1871 }
1872
1873 /*
1874 * Establish a sysevent channel for publication and subscription
1875 * Full path to the channel determined by the caller
1876 */
1877 sysevent_handle_t *
sysevent_open_channel_alt(const char * channel_path)1878 sysevent_open_channel_alt(const char *channel_path)
1879 {
1880 return (sysevent_open_channel_common(channel_path));
1881 }
1882
1883 /*
1884 * sysevent_close_channel - Clean up resources associated with a previously
1885 * opened sysevent channel
1886 */
1887 void
sysevent_close_channel(sysevent_handle_t * shp)1888 sysevent_close_channel(sysevent_handle_t *shp)
1889 {
1890 int error = errno;
1891 uint32_t sub_id = 0;
1892
1893 if (shp == NULL) {
1894 return;
1895 }
1896
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));
1905 }
1906
1907 (void) update_kernel_registration(shp, 0,
1908 SE_CLOSE_REGISTRATION, &sub_id, 0, NULL);
1909 (void) mutex_unlock(SH_LOCK(shp));
1910
1911 free(SH_CHANNEL_PATH(shp));
1912 free(shp);
1913 errno = error;
1914 }
1915
1916 /*
1917 * sysevent_bind_publisher - Bind an event publisher to an event channel
1918 */
1919 int
sysevent_bind_publisher(sysevent_handle_t * shp)1920 sysevent_bind_publisher(sysevent_handle_t *shp)
1921 {
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;
1928
1929 if (shp == NULL) {
1930 errno = EINVAL;
1931 return (-1);
1932 }
1933
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);
1939 }
1940
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);
1946 }
1947 SH_PRIV_DATA(shp) = (void *)pub;
1948
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);
1955 }
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);
1961 }
1962
1963 /* Only one publisher allowed per channel */
1964 if (stat(SH_DOOR_NAME(shp), ®_stat) != 0) {
1965 if (errno != ENOENT) {
1966 error = EINVAL;
1967 goto fail;
1968 }
1969 }
1970
1971 /*
1972 * Remove door file for robustness.
1973 */
1974 if (unlink(SH_DOOR_NAME(shp)) != 0)
1975 dprint("sysevent_bind_publisher: Unlink of %s failed.\n",
1976 SH_DOOR_NAME(shp));
1977
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;
1984 }
1985
1986 /*
1987 * Create the registration service for this publisher.
1988 */
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;
1995 }
1996
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;
2004 }
2005
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;
2011 }
2012
2013 SH_ID(shp) = pub_id;
2014 SH_BOUND(shp) = 1;
2015 SH_TYPE(shp) = PUBLISHER;
2016
2017
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;
2024 }
2025 (void) close(fd);
2026
2027 (void) mutex_unlock(SH_LOCK(shp));
2028
2029 return (0);
2030
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);
2041 }
2042
2043 static pthread_once_t xdoor_thrattr_once = PTHREAD_ONCE_INIT;
2044 static pthread_attr_t xdoor_thrattr;
2045
2046 static void
xdoor_thrattr_init(void)2047 xdoor_thrattr_init(void)
2048 {
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);
2053 }
2054
2055 static int
xdoor_server_create(door_info_t * dip,void * (* startf)(void *),void * startfarg,void * cookie)2056 xdoor_server_create(door_info_t *dip, void *(*startf)(void *),
2057 void *startfarg, void *cookie)
2058 {
2059 struct sysevent_subattr_impl *xsa = cookie;
2060 pthread_attr_t *thrattr;
2061 sigset_t oset;
2062 int err;
2063
2064 if (xsa->xs_thrcreate) {
2065 return (xsa->xs_thrcreate(dip, startf, startfarg,
2066 xsa->xs_thrcreate_cookie));
2067 }
2068
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;
2074 }
2075
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);
2079
2080 return (err == 0 ? 1 : -1);
2081 }
2082
2083 static void
xdoor_server_setup(void * cookie)2084 xdoor_server_setup(void *cookie)
2085 {
2086 struct sysevent_subattr_impl *xsa = cookie;
2087
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);
2093 }
2094 }
2095
2096 static int
sysevent_bind_subscriber_cmn(sysevent_handle_t * shp,void (* event_handler)(sysevent_t * ev),sysevent_subattr_t * subattr)2097 sysevent_bind_subscriber_cmn(sysevent_handle_t *shp,
2098 void (*event_handler)(sysevent_t *ev),
2099 sysevent_subattr_t *subattr)
2100 {
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;
2109
2110 if (shp == NULL || event_handler == NULL) {
2111 errno = EINVAL;
2112 return (-1);
2113 }
2114
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);
2120 }
2121
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);
2127 }
2128
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);
2135 }
2136
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);
2142 }
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;
2146
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;
2152 }
2153 SH_ID(shp) = sub_id;
2154
2155 if (snprintf(door_name, MAXPATHLEN, "%s/%d",
2156 SH_CHANNEL_PATH(shp), sub_id) >= MAXPATHLEN) {
2157 error = EINVAL;
2158 goto fail;
2159 }
2160 if ((SH_DOOR_NAME(shp) = strdup(door_name)) == NULL) {
2161 error = ENOMEM;
2162 goto fail;
2163 }
2164
2165 /*
2166 * Remove door file for robustness.
2167 */
2168 if (unlink(SH_DOOR_NAME(shp)) != 0)
2169 dprint("sysevent_bind_subscriber: Unlink of %s failed.\n",
2170 SH_DOOR_NAME(shp));
2171
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;
2176 }
2177
2178 /*
2179 * Create the sysevent door service for this client.
2180 * syseventd will use this door service to propagate
2181 * events to the client.
2182 */
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);
2192 }
2193
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;
2199 }
2200
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;
2205 }
2206 (void) close(fd);
2207
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;
2214 }
2215
2216 SH_BOUND(shp) = 1;
2217 SH_TYPE(shp) = SUBSCRIBER;
2218 SH_PRIV_DATA(shp) = (void *)sub_info;
2219
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 {
2226 /*
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.
2239 */
2240
2241 created = xsa->xs_thrcreate(NULL,
2242 (void *(*)(void *))subscriber_event_handler,
2243 shp, xsa->xs_thrcreate_cookie) == 1;
2244 }
2245
2246 if (!created) {
2247 error = EFAULT;
2248 goto fail;
2249 }
2250
2251 (void) mutex_unlock(SH_LOCK(shp));
2252
2253 return (0);
2254
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;
2267 }
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;
2273 }
2274 (void) mutex_unlock(SH_LOCK(shp));
2275
2276 errno = error;
2277
2278 return (-1);
2279 }
2280
2281 /*
2282 * sysevent_bind_subscriber - Bind an event receiver to an event channel
2283 */
2284 int
sysevent_bind_subscriber(sysevent_handle_t * shp,void (* event_handler)(sysevent_t * ev))2285 sysevent_bind_subscriber(sysevent_handle_t *shp,
2286 void (*event_handler)(sysevent_t *ev))
2287 {
2288 return (sysevent_bind_subscriber_cmn(shp, event_handler, NULL));
2289 }
2290
2291 /*
2292 * sysevent_bind_xsubscriber - Bind a subscriber using door_xcreate with
2293 * attributes specified.
2294 */
2295 int
sysevent_bind_xsubscriber(sysevent_handle_t * shp,void (* event_handler)(sysevent_t * ev),sysevent_subattr_t * subattr)2296 sysevent_bind_xsubscriber(sysevent_handle_t *shp,
2297 void (*event_handler)(sysevent_t *ev), sysevent_subattr_t *subattr)
2298 {
2299 return (sysevent_bind_subscriber_cmn(shp, event_handler, subattr));
2300 }
2301
2302 /*
2303 * sysevent_register_event - register an event class and associated subclasses
2304 * for an event subscriber
2305 */
2306 int
sysevent_register_event(sysevent_handle_t * shp,const char * ev_class,const char ** ev_subclass,int subclass_num)2307 sysevent_register_event(sysevent_handle_t *shp,
2308 const char *ev_class, const char **ev_subclass,
2309 int subclass_num)
2310 {
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;
2317
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);
2325 }
2326
2327 if (nvlist_alloc(&nvl, NV_UNIQUE_NAME_TYPE, 0) != 0) {
2328 (void) mutex_unlock(SH_LOCK(shp));
2329 return (-1);
2330 }
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);
2336 }
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);
2341 }
2342 nvlist_free(nvl);
2343
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);
2353 }
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);
2363 }
2364
2365 free(nvlbuf);
2366
2367 (void) mutex_unlock(SH_LOCK(shp));
2368
2369 return (0);
2370 }
2371
2372 /*
2373 * sysevent_unregister_event - Unregister an event class and associated
2374 * subclasses for an event subscriber
2375 */
2376 void
sysevent_unregister_event(sysevent_handle_t * shp,const char * class)2377 sysevent_unregister_event(sysevent_handle_t *shp, const char *class)
2378 {
2379 size_t class_sz;
2380
2381 (void) mutex_lock(SH_LOCK(shp));
2382
2383 if (!SH_BOUND(shp)) {
2384 (void) mutex_unlock(SH_LOCK(shp));
2385 return;
2386 }
2387
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);
2396
2397 (void) mutex_unlock(SH_LOCK(shp));
2398 }
2399
2400 static int
cleanup_id(sysevent_handle_t * shp,uint32_t id,int type)2401 cleanup_id(sysevent_handle_t *shp, uint32_t id, int type)
2402 {
2403 dprint("cleanup_id: Cleaning up %s/%d\n", SH_CHANNEL_NAME(shp), id);
2404
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);
2411 }
2412
2413 return (0);
2414 }
2415
2416 /*
2417 * sysevent_cleanup_subscribers: Allows the caller to cleanup resources
2418 * allocated to unresponsive subscribers.
2419 */
2420 void
sysevent_cleanup_subscribers(sysevent_handle_t * shp)2421 sysevent_cleanup_subscribers(sysevent_handle_t *shp)
2422 {
2423 uint32_t ping, result;
2424 int i, error, sub_fd;
2425 subscriber_data_t *sub;
2426
2427 if (!SH_BOUND(shp)) {
2428 return;
2429 }
2430
2431 for (i = 1; i <= MAX_SUBSCRIBERS; ++i) {
2432
2433 sub = SH_SUBSCRIBER(shp, i);
2434 if (sub == NULL) {
2435 continue;
2436 }
2437
2438 if ((sub_fd = open(sub->sd_door_name, O_RDONLY)) == -1) {
2439 continue;
2440 }
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);
2445
2446 /* Only cleanup on EBADF (Invalid door descriptor) */
2447 if (error != EBADF)
2448 continue;
2449
2450 if (cleanup_id(shp, i, SUBSCRIBER) != 0)
2451 continue;
2452
2453 cache_remove_class(shp, EC_ALL, i);
2454
2455 free(sub->sd_door_name);
2456 free(sub);
2457 SH_SUBSCRIBER(shp, i) = NULL;
2458 }
2459
2460 }
2461
2462 /*
2463 * sysevent_cleanup_publishers: Allows stale publisher handles to be deallocated
2464 * as needed.
2465 */
2466 void
sysevent_cleanup_publishers(sysevent_handle_t * shp)2467 sysevent_cleanup_publishers(sysevent_handle_t *shp)
2468 {
2469 (void) cleanup_id(shp, 1, PUBLISHER);
2470 }
2471
2472 /*
2473 * sysevent_unbind_subscriber: Unbind the subscriber from the sysevent channel.
2474 */
2475 void
sysevent_unbind_subscriber(sysevent_handle_t * shp)2476 sysevent_unbind_subscriber(sysevent_handle_t *shp)
2477 {
2478 subscriber_priv_t *sub_info;
2479
2480 if (shp == NULL)
2481 return;
2482
2483 (void) mutex_lock(SH_LOCK(shp));
2484 if (SH_BOUND(shp) == 0) {
2485 (void) mutex_unlock(SH_LOCK(shp));
2486 return;
2487 }
2488
2489 /* Update the in-kernel registration */
2490 (void) update_kernel_registration(shp, SUBSCRIBER,
2491 SE_UNBIND_REGISTRATION, &SH_ID(shp), 0, NULL);
2492
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);
2497
2498 /* Close down event delivery facilities */
2499 (void) door_revoke(SH_DOOR_DESC(shp));
2500 (void) fdetach(SH_DOOR_NAME(shp));
2501
2502 /*
2503 * Release resources and wait for pending event delivery to
2504 * complete.
2505 */
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);
2513
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));
2520 }
2521
2522 /*
2523 * sysevent_unbind_publisher: Unbind publisher from the sysevent channel.
2524 */
2525 void
sysevent_unbind_publisher(sysevent_handle_t * shp)2526 sysevent_unbind_publisher(sysevent_handle_t *shp)
2527 {
2528 if (shp == NULL)
2529 return;
2530
2531 (void) mutex_lock(SH_LOCK(shp));
2532 if (SH_BOUND(shp) == 0) {
2533 (void) mutex_unlock(SH_LOCK(shp));
2534 return;
2535 }
2536
2537 /* Close down the registration facilities */
2538 (void) door_revoke(SH_DOOR_DESC(shp));
2539 (void) fdetach(SH_DOOR_NAME(shp));
2540
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;
2545
2546 /* Free resources associated with bind */
2547 free_cached_registration(shp);
2548 dealloc_subscribers(shp);
2549
2550 free(SH_PRIV_DATA(shp));
2551 free(SH_DOOR_NAME(shp));
2552 SH_ID(shp) = 0;
2553 (void) mutex_unlock(SH_LOCK(shp));
2554 }
2555
2556 /*
2557 * Evolving APIs to subscribe to syseventd(1M) system events.
2558 */
2559
2560 static sysevent_handle_t *
sysevent_bind_handle_cmn(void (* event_handler)(sysevent_t * ev),sysevent_subattr_t * subattr)2561 sysevent_bind_handle_cmn(void (*event_handler)(sysevent_t *ev),
2562 sysevent_subattr_t *subattr)
2563 {
2564 sysevent_handle_t *shp;
2565
2566 if (getuid() != 0) {
2567 errno = EACCES;
2568 return (NULL);
2569 }
2570
2571 if (event_handler == NULL) {
2572 errno = EINVAL;
2573 return (NULL);
2574 }
2575
2576 if ((shp = sysevent_open_channel(SYSEVENTD_CHAN)) == NULL) {
2577 return (NULL);
2578 }
2579
2580 if (sysevent_bind_xsubscriber(shp, event_handler, subattr) != 0) {
2581 /*
2582 * Ask syseventd to clean-up any stale subcribers and try to
2583 * to bind again
2584 */
2585 if (errno == EBUSY) {
2586 int pub_fd;
2587 char door_name[MAXPATHLEN];
2588 uint32_t result;
2589 struct reg_args rargs;
2590
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);
2596 }
2597
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);
2603
2604 /* Try to bind again */
2605 if (sysevent_bind_xsubscriber(shp, event_handler,
2606 subattr) != 0) {
2607 sysevent_close_channel(shp);
2608 return (NULL);
2609 }
2610 } else {
2611 sysevent_close_channel(shp);
2612 return (NULL);
2613 }
2614 }
2615
2616 return (shp);
2617 }
2618
2619 /*
2620 * sysevent_bind_handle - Bind application event handler for syseventd
2621 * subscription.
2622 */
2623 sysevent_handle_t *
sysevent_bind_handle(void (* event_handler)(sysevent_t * ev))2624 sysevent_bind_handle(void (*event_handler)(sysevent_t *ev))
2625 {
2626 return (sysevent_bind_handle_cmn(event_handler, NULL));
2627 }
2628
2629 /*
2630 * sysevent_bind_xhandle - Bind application event handler for syseventd
2631 * subscription, using door_xcreate and attributes as specified.
2632 */
2633 sysevent_handle_t *
sysevent_bind_xhandle(void (* event_handler)(sysevent_t * ev),sysevent_subattr_t * subattr)2634 sysevent_bind_xhandle(void (*event_handler)(sysevent_t *ev),
2635 sysevent_subattr_t *subattr)
2636 {
2637 return (sysevent_bind_handle_cmn(event_handler, subattr));
2638 }
2639
2640 /*
2641 * sysevent_unbind_handle - Unbind caller from syseventd subscriptions
2642 */
2643 void
sysevent_unbind_handle(sysevent_handle_t * shp)2644 sysevent_unbind_handle(sysevent_handle_t *shp)
2645 {
2646 sysevent_unbind_subscriber(shp);
2647 sysevent_close_channel(shp);
2648 }
2649
2650 /*
2651 * sysevent_subscribe_event - Subscribe to system event notification from
2652 * syseventd(1M) for the class and subclasses specified.
2653 */
2654 int
sysevent_subscribe_event(sysevent_handle_t * shp,const char * event_class,const char ** event_subclass_list,int num_subclasses)2655 sysevent_subscribe_event(sysevent_handle_t *shp, const char *event_class,
2656 const char **event_subclass_list, int num_subclasses)
2657 {
2658 return (sysevent_register_event(shp, event_class,
2659 event_subclass_list, num_subclasses));
2660 }
2661
2662 void
sysevent_unsubscribe_event(sysevent_handle_t * shp,const char * event_class)2663 sysevent_unsubscribe_event(sysevent_handle_t *shp, const char *event_class)
2664 {
2665 sysevent_unregister_event(shp, event_class);
2666 }
2667