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(8), the sysevent publisher
740 * for the syseventd channel. Processes wishing to receive sysevent
741 * notifications from syseventd may use a set of public
742 * APIs designed to subscribe to syseventd sysevents. The subscription
743 * APIs are implemented in accordance with PSARC/2001/076.
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(void * arg)759 subscriber_event_handler(void *arg)
760 {
761 sysevent_handle_t *shp = arg;
762 subscriber_priv_t *sub_info;
763 sysevent_queue_t *evqp;
764
765 sub_info = (subscriber_priv_t *)SH_PRIV_DATA(shp);
766
767 /* See hack alert in sysevent_bind_subscriber_cmn */
768 if (sub_info->sp_handler_tid == 0)
769 sub_info->sp_handler_tid = thr_self();
770
771 (void) mutex_lock(&sub_info->sp_qlock);
772 for (;;) {
773 while (sub_info->sp_evq_head == NULL && SH_BOUND(shp)) {
774 (void) cond_wait(&sub_info->sp_cv, &sub_info->sp_qlock);
775 }
776 evqp = sub_info->sp_evq_head;
777 while (evqp) {
778 (void) mutex_unlock(&sub_info->sp_qlock);
779 (void) sub_info->sp_func(evqp->sq_ev);
780 (void) mutex_lock(&sub_info->sp_qlock);
781 sub_info->sp_evq_head = sub_info->sp_evq_head->sq_next;
782 free(evqp->sq_ev);
783 free(evqp);
784 evqp = sub_info->sp_evq_head;
785 }
786 if (!SH_BOUND(shp)) {
787 (void) mutex_unlock(&sub_info->sp_qlock);
788 return (NULL);
789 }
790 }
791
792 /* NOTREACHED */
793 }
794
795 /*
796 * Data structure used to communicate event subscription cache updates
797 * to publishers via a registration door
798 */
799 struct reg_args {
800 uint32_t ra_sub_id;
801 uint32_t ra_op;
802 uint64_t ra_buf_ptr;
803 };
804
805
806 /*
807 * event_deliver_service - generic event delivery service routine. This routine
808 * is called in response to a door call to post an event.
809 *
810 */
811 /*ARGSUSED*/
812 static void
event_deliver_service(void * cookie,char * args,size_t alen,door_desc_t * ddp,uint_t ndid)813 event_deliver_service(void *cookie, char *args, size_t alen,
814 door_desc_t *ddp, uint_t ndid)
815 {
816 int ret = 0;
817 subscriber_priv_t *sub_info;
818 sysevent_handle_t *shp;
819 sysevent_queue_t *new_eq;
820
821 if (args == NULL || alen < sizeof (uint32_t)) {
822 ret = EINVAL;
823 goto return_from_door;
824 }
825
826 /* Publisher checking on subscriber */
827 if (alen == sizeof (uint32_t)) {
828 ret = 0;
829 goto return_from_door;
830 }
831
832 shp = (sysevent_handle_t *)cookie;
833 if (shp == NULL) {
834 ret = EBADF;
835 goto return_from_door;
836 }
837
838 /*
839 * Mustn't block if we are trying to update the registration with
840 * the publisher
841 */
842 if (mutex_trylock(SH_LOCK(shp)) != 0) {
843 ret = EAGAIN;
844 goto return_from_door;
845 }
846
847 if (!SH_BOUND(shp)) {
848 ret = EBADF;
849 (void) mutex_unlock(SH_LOCK(shp));
850 goto return_from_door;
851 }
852
853 sub_info = (subscriber_priv_t *)SH_PRIV_DATA(shp);
854 if (sub_info == NULL) {
855 ret = EBADF;
856 (void) mutex_unlock(SH_LOCK(shp));
857 goto return_from_door;
858 }
859
860 new_eq = (sysevent_queue_t *)calloc(1,
861 sizeof (sysevent_queue_t));
862 if (new_eq == NULL) {
863 ret = EAGAIN;
864 (void) mutex_unlock(SH_LOCK(shp));
865 goto return_from_door;
866 }
867
868 /*
869 * Allocate and copy the event buffer into the subscriber's
870 * address space
871 */
872 new_eq->sq_ev = calloc(1, alen);
873 if (new_eq->sq_ev == NULL) {
874 free(new_eq);
875 ret = EAGAIN;
876 (void) mutex_unlock(SH_LOCK(shp));
877 goto return_from_door;
878 }
879 (void) bcopy(args, new_eq->sq_ev, alen);
880
881 (void) mutex_lock(&sub_info->sp_qlock);
882 if (sub_info->sp_evq_head == NULL) {
883 sub_info->sp_evq_head = new_eq;
884 } else {
885 sub_info->sp_evq_tail->sq_next = new_eq;
886 }
887 sub_info->sp_evq_tail = new_eq;
888
889 (void) cond_signal(&sub_info->sp_cv);
890 (void) mutex_unlock(&sub_info->sp_qlock);
891 (void) mutex_unlock(SH_LOCK(shp));
892
893 return_from_door:
894 (void) door_return((void *)&ret, sizeof (ret), NULL, 0);
895 (void) door_return(NULL, 0, NULL, 0);
896 }
897
898 /*
899 * Sysevent subscription information is maintained in the kernel. Updates
900 * to the in-kernel registration database is expected to be infrequent and
901 * offers consistency for publishers and subscribers that may come and go
902 * for a given channel.
903 *
904 * To expedite registration lookups by publishers, a cached copy of the
905 * kernel registration database is kept per-channel. Caches are invalidated
906 * and refreshed upon state changes to the in-kernel registration database.
907 *
908 * To prevent stale subscriber data, publishers may remove subsriber
909 * registrations from the in-kernel registration database in the event
910 * that a particular subscribing process is unresponsive.
911 *
912 * The following routines provide a mechanism to update publisher and subscriber
913 * information for a specified channel.
914 */
915
916 /*
917 * clnt_deliver_event - Deliver an event through the consumer's event
918 * delivery door
919 *
920 * Returns -1 if message not delivered. With errno set to cause of error.
921 * Returns 0 for success with the results returned in posting buffer.
922 */
923 static int
clnt_deliver_event(int service_door,void * data,size_t datalen,void * result,size_t rlen)924 clnt_deliver_event(int service_door, void *data, size_t datalen,
925 void *result, size_t rlen)
926 {
927 int error = 0;
928 door_arg_t door_arg;
929
930 door_arg.rbuf = result;
931 door_arg.rsize = rlen;
932 door_arg.data_ptr = data;
933 door_arg.data_size = datalen;
934 door_arg.desc_ptr = NULL;
935 door_arg.desc_num = 0;
936
937 /*
938 * Make door call
939 */
940 while ((error = door_call(service_door, &door_arg)) != 0) {
941 if (errno == EAGAIN || errno == EINTR) {
942 continue;
943 } else {
944 error = errno;
945 break;
946 }
947 }
948
949 return (error);
950 }
951
952 static int
update_publisher_cache(subscriber_priv_t * sub_info,int update_op,uint32_t sub_id,size_t datasz,uchar_t * data)953 update_publisher_cache(subscriber_priv_t *sub_info, int update_op,
954 uint32_t sub_id, size_t datasz, uchar_t *data)
955 {
956 int pub_fd;
957 uint32_t result = 0;
958 struct reg_args *rargs;
959
960 rargs = (struct reg_args *)calloc(1, sizeof (struct reg_args) +
961 datasz);
962 if (rargs == NULL) {
963 errno = ENOMEM;
964 return (-1);
965 }
966
967 rargs->ra_sub_id = sub_id;
968 rargs->ra_op = update_op;
969 bcopy(data, (char *)&rargs->ra_buf_ptr, datasz);
970
971 pub_fd = open(sub_info->sp_door_name, O_RDONLY);
972 (void) clnt_deliver_event(pub_fd, (void *)rargs,
973 sizeof (struct reg_args) + datasz, &result, sizeof (result));
974 (void) close(pub_fd);
975
976 free(rargs);
977 if (result != 0) {
978 errno = result;
979 return (-1);
980 }
981
982 return (0);
983 }
984
985
986 /*
987 * update_kernel_registration - update the in-kernel registration for the
988 * given channel.
989 */
990 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)991 update_kernel_registration(sysevent_handle_t *shp, int update_type,
992 int update_op, uint32_t *sub_id, size_t datasz, uchar_t *data)
993 {
994 int error;
995 char *channel_name = SH_CHANNEL_NAME(shp);
996 se_pubsub_t udata;
997
998 udata.ps_channel_name_len = strlen(channel_name) + 1;
999 udata.ps_op = update_op;
1000 udata.ps_type = update_type;
1001 udata.ps_buflen = datasz;
1002 udata.ps_id = *sub_id;
1003
1004 if ((error = modctl(MODEVENTS, (uintptr_t)MODEVENTS_REGISTER_EVENT,
1005 (uintptr_t)channel_name, (uintptr_t)data, (uintptr_t)&udata, 0))
1006 != 0) {
1007 return (error);
1008 }
1009
1010 *sub_id = udata.ps_id;
1011
1012 return (error);
1013 }
1014
1015 /*
1016 * get_kernel_registration - get the current subscriber registration for
1017 * the given channel
1018 */
1019 static nvlist_t *
get_kernel_registration(char * channel_name,uint32_t class_id)1020 get_kernel_registration(char *channel_name, uint32_t class_id)
1021 {
1022 char *nvlbuf;
1023 nvlist_t *nvl;
1024 se_pubsub_t udata;
1025
1026 nvlbuf = calloc(1, MAX_SUBSCRIPTION_SZ);
1027 if (nvlbuf == NULL) {
1028 return (NULL);
1029 }
1030
1031 udata.ps_buflen = MAX_SUBSCRIPTION_SZ;
1032 udata.ps_channel_name_len = strlen(channel_name) + 1;
1033 udata.ps_id = class_id;
1034 udata.ps_op = SE_GET_REGISTRATION;
1035 udata.ps_type = PUBLISHER;
1036
1037 if (modctl(MODEVENTS, (uintptr_t)MODEVENTS_REGISTER_EVENT,
1038 (uintptr_t)channel_name, (uintptr_t)nvlbuf, (uintptr_t)&udata, 0)
1039 != 0) {
1040
1041 /* Need a bigger buffer to hold channel registration */
1042 if (errno == EAGAIN) {
1043 free(nvlbuf);
1044 nvlbuf = calloc(1, udata.ps_buflen);
1045 if (nvlbuf == NULL)
1046 return (NULL);
1047
1048 /* Try again */
1049 if (modctl(MODEVENTS,
1050 (uintptr_t)MODEVENTS_REGISTER_EVENT,
1051 (uintptr_t)channel_name, (uintptr_t)nvlbuf,
1052 (uintptr_t)&udata, 0) != 0) {
1053 free(nvlbuf);
1054 return (NULL);
1055 }
1056 } else {
1057 free(nvlbuf);
1058 return (NULL);
1059 }
1060 }
1061
1062 if (nvlist_unpack(nvlbuf, udata.ps_buflen, &nvl, 0) != 0) {
1063 free(nvlbuf);
1064 return (NULL);
1065 }
1066 free(nvlbuf);
1067
1068 return (nvl);
1069 }
1070
1071 /*
1072 * The following routines provide a mechanism for publishers to maintain
1073 * subscriber information.
1074 */
1075
1076 static void
dealloc_subscribers(sysevent_handle_t * shp)1077 dealloc_subscribers(sysevent_handle_t *shp)
1078 {
1079 int i;
1080 subscriber_data_t *sub;
1081
1082 for (i = 1; i <= MAX_SUBSCRIBERS; ++i) {
1083 sub = SH_SUBSCRIBER(shp, i);
1084 if (sub != NULL) {
1085 free(sub->sd_door_name);
1086 free(sub);
1087 }
1088 SH_SUBSCRIBER(shp, i) = NULL;
1089 }
1090 }
1091
1092 /*ARGSUSED*/
1093 static int
alloc_subscriber(sysevent_handle_t * shp,uint32_t sub_id,int oflag)1094 alloc_subscriber(sysevent_handle_t *shp, uint32_t sub_id, int oflag)
1095 {
1096 subscriber_data_t *sub;
1097 char door_name[MAXPATHLEN];
1098
1099 if (SH_SUBSCRIBER(shp, sub_id) != NULL) {
1100 return (0);
1101 }
1102
1103 /* Allocate and initialize the subscriber data */
1104 sub = (subscriber_data_t *)calloc(1,
1105 sizeof (subscriber_data_t));
1106 if (sub == NULL) {
1107 return (-1);
1108 }
1109 if (snprintf(door_name, MAXPATHLEN, "%s/%d",
1110 SH_CHANNEL_PATH(shp), sub_id) >= MAXPATHLEN) {
1111 free(sub);
1112 return (-1);
1113 }
1114
1115 sub->sd_flag = ACTIVE;
1116 sub->sd_door_name = strdup(door_name);
1117 if (sub->sd_door_name == NULL) {
1118 free(sub);
1119 return (-1);
1120 }
1121
1122 SH_SUBSCRIBER(shp, sub_id) = sub;
1123 return (0);
1124
1125 }
1126
1127 /*
1128 * The following routines are used to update and maintain the registration cache
1129 * for a particular sysevent channel.
1130 */
1131
1132 static uint32_t
hash_func(const char * s)1133 hash_func(const char *s)
1134 {
1135 uint32_t result = 0;
1136 uint_t g;
1137
1138 while (*s != '\0') {
1139 result <<= 4;
1140 result += (uint32_t)*s++;
1141 g = result & 0xf0000000;
1142 if (g != 0) {
1143 result ^= g >> 24;
1144 result ^= g;
1145 }
1146 }
1147
1148 return (result);
1149 }
1150
1151 subclass_lst_t *
cache_find_subclass(class_lst_t * c_list,char * subclass)1152 cache_find_subclass(class_lst_t *c_list, char *subclass)
1153 {
1154 subclass_lst_t *sc_list;
1155
1156 if (c_list == NULL)
1157 return (NULL);
1158
1159 sc_list = c_list->cl_subclass_list;
1160
1161 while (sc_list != NULL) {
1162 if (strcmp(sc_list->sl_name, subclass) == 0) {
1163 return (sc_list);
1164 }
1165 sc_list = sc_list->sl_next;
1166 }
1167
1168 return (NULL);
1169 }
1170
1171
1172 static class_lst_t *
cache_find_class(sysevent_handle_t * shp,char * class)1173 cache_find_class(sysevent_handle_t *shp, char *class)
1174 {
1175 int index;
1176 class_lst_t *c_list;
1177 class_lst_t **class_hash = SH_CLASS_HASH(shp);
1178
1179 if (strcmp(class, EC_ALL) == 0) {
1180 return (class_hash[0]);
1181 }
1182
1183 index = CLASS_HASH(class);
1184 c_list = class_hash[index];
1185 while (c_list != NULL) {
1186 if (strcmp(class, c_list->cl_name) == 0) {
1187 break;
1188 }
1189 c_list = c_list->cl_next;
1190 }
1191
1192 return (c_list);
1193 }
1194
1195 static int
cache_insert_subclass(class_lst_t * c_list,char ** subclass_names,int subclass_num,uint32_t sub_id)1196 cache_insert_subclass(class_lst_t *c_list, char **subclass_names,
1197 int subclass_num, uint32_t sub_id)
1198 {
1199 int i;
1200 subclass_lst_t *sc_list;
1201
1202 for (i = 0; i < subclass_num; ++i) {
1203 if ((sc_list = cache_find_subclass(c_list, subclass_names[i]))
1204 != NULL) {
1205 sc_list->sl_num[sub_id] = 1;
1206 } else {
1207 sc_list = (subclass_lst_t *)calloc(1,
1208 sizeof (subclass_lst_t));
1209 if (sc_list == NULL)
1210 return (-1);
1211
1212 sc_list->sl_name = strdup(subclass_names[i]);
1213 if (sc_list->sl_name == NULL) {
1214 free(sc_list);
1215 return (-1);
1216 }
1217
1218 sc_list->sl_num[sub_id] = 1;
1219 sc_list->sl_next = c_list->cl_subclass_list;
1220 c_list->cl_subclass_list = sc_list;
1221 }
1222 }
1223
1224 return (0);
1225 }
1226
1227 static int
cache_insert_class(sysevent_handle_t * shp,char * class,char ** subclass_names,int subclass_num,uint32_t sub_id)1228 cache_insert_class(sysevent_handle_t *shp, char *class,
1229 char **subclass_names, int subclass_num, uint32_t sub_id)
1230 {
1231 class_lst_t *c_list;
1232
1233 if (strcmp(class, EC_ALL) == 0) {
1234 char *subclass_all = EC_SUB_ALL;
1235
1236 (void) cache_insert_subclass(SH_CLASS_HASH(shp)[0],
1237 (char **)&subclass_all, 1, sub_id);
1238 return (0);
1239 }
1240
1241 /* New class, add to the registration cache */
1242 if ((c_list = cache_find_class(shp, class)) == NULL) {
1243
1244 c_list = (class_lst_t *)calloc(1, sizeof (class_lst_t));
1245 if (c_list == NULL) {
1246 return (1);
1247 }
1248 c_list->cl_name = strdup(class);
1249 if (c_list->cl_name == NULL) {
1250 free(c_list);
1251 return (1);
1252 }
1253
1254 c_list->cl_subclass_list = (subclass_lst_t *)
1255 calloc(1, sizeof (subclass_lst_t));
1256 if (c_list->cl_subclass_list == NULL) {
1257 free(c_list->cl_name);
1258 free(c_list);
1259 return (1);
1260 }
1261 c_list->cl_subclass_list->sl_name = strdup(EC_SUB_ALL);
1262 if (c_list->cl_subclass_list->sl_name == NULL) {
1263 free(c_list->cl_subclass_list);
1264 free(c_list->cl_name);
1265 free(c_list);
1266 return (1);
1267 }
1268 c_list->cl_next = SH_CLASS_HASH(shp)[CLASS_HASH(class)];
1269 SH_CLASS_HASH(shp)[CLASS_HASH(class)] = c_list;
1270
1271 }
1272
1273 /* Update the subclass list */
1274 if (cache_insert_subclass(c_list, subclass_names, subclass_num,
1275 sub_id) != 0)
1276 return (1);
1277
1278 return (0);
1279 }
1280
1281 static void
cache_remove_all_class(sysevent_handle_t * shp,uint32_t sub_id)1282 cache_remove_all_class(sysevent_handle_t *shp, uint32_t sub_id)
1283 {
1284 int i;
1285 class_lst_t *c_list;
1286 subclass_lst_t *sc_list;
1287
1288 for (i = 0; i < CLASS_HASH_SZ + 1; ++i) {
1289 c_list = SH_CLASS_HASH(shp)[i];
1290 while (c_list != NULL) {
1291 sc_list = c_list->cl_subclass_list;
1292 while (sc_list != NULL) {
1293 sc_list->sl_num[sub_id] = 0;
1294 sc_list = sc_list->sl_next;
1295 }
1296 c_list = c_list->cl_next;
1297 }
1298 }
1299 }
1300
1301 static void
cache_remove_class(sysevent_handle_t * shp,char * class,uint32_t sub_id)1302 cache_remove_class(sysevent_handle_t *shp, char *class, uint32_t sub_id)
1303 {
1304 class_lst_t *c_list;
1305 subclass_lst_t *sc_list;
1306
1307 if (strcmp(class, EC_ALL) == 0) {
1308 cache_remove_all_class(shp, sub_id);
1309 return;
1310 }
1311
1312 if ((c_list = cache_find_class(shp, class)) == NULL) {
1313 return;
1314 }
1315
1316 sc_list = c_list->cl_subclass_list;
1317 while (sc_list != NULL) {
1318 sc_list->sl_num[sub_id] = 0;
1319 sc_list = sc_list->sl_next;
1320 }
1321 }
1322
1323 static void
free_cached_registration(sysevent_handle_t * shp)1324 free_cached_registration(sysevent_handle_t *shp)
1325 {
1326 int i;
1327 class_lst_t *clist, *next_clist;
1328 subclass_lst_t *sc_list, *next_sc;
1329
1330 for (i = 0; i < CLASS_HASH_SZ + 1; i++) {
1331 clist = SH_CLASS_HASH(shp)[i];
1332 while (clist != NULL) {
1333 sc_list = clist->cl_subclass_list;
1334 while (sc_list != NULL) {
1335 free(sc_list->sl_name);
1336 next_sc = sc_list->sl_next;
1337 free(sc_list);
1338 sc_list = next_sc;
1339 }
1340 free(clist->cl_name);
1341 next_clist = clist->cl_next;
1342 free(clist);
1343 clist = next_clist;
1344 }
1345 SH_CLASS_HASH(shp)[i] = NULL;
1346 }
1347 }
1348
1349 static int
create_cached_registration(sysevent_handle_t * shp,class_lst_t ** class_hash)1350 create_cached_registration(sysevent_handle_t *shp,
1351 class_lst_t **class_hash)
1352 {
1353 int i, j, new_class;
1354 char *class_name;
1355 uint_t num_elem;
1356 uchar_t *subscribers;
1357 nvlist_t *nvl;
1358 nvpair_t *nvpair;
1359 class_lst_t *clist;
1360 subclass_lst_t *sc_list;
1361
1362 for (i = 0; i < CLASS_HASH_SZ + 1; ++i) {
1363
1364 if ((nvl = get_kernel_registration(SH_CHANNEL_NAME(shp), i))
1365 == NULL) {
1366 if (errno == ENOENT) {
1367 class_hash[i] = NULL;
1368 continue;
1369 } else {
1370 goto create_failed;
1371 }
1372 }
1373
1374
1375 nvpair = NULL;
1376 if ((nvpair = nvlist_next_nvpair(nvl, nvpair)) == NULL) {
1377 goto create_failed;
1378 }
1379
1380 new_class = 1;
1381 while (new_class) {
1382 /* Extract the class name from the nvpair */
1383 if (nvpair_value_string(nvpair, &class_name) != 0) {
1384 goto create_failed;
1385 }
1386 clist = (class_lst_t *)
1387 calloc(1, sizeof (class_lst_t));
1388 if (clist == NULL) {
1389 goto create_failed;
1390 }
1391
1392 clist->cl_name = strdup(class_name);
1393 if (clist->cl_name == NULL) {
1394 free(clist);
1395 goto create_failed;
1396 }
1397
1398 /*
1399 * Extract the subclass name and registration
1400 * from the nvpair
1401 */
1402 if ((nvpair = nvlist_next_nvpair(nvl, nvpair))
1403 == NULL) {
1404 free(clist->cl_name);
1405 free(clist);
1406 goto create_failed;
1407 }
1408
1409 clist->cl_next = class_hash[i];
1410 class_hash[i] = clist;
1411
1412 for (;;) {
1413
1414 sc_list = (subclass_lst_t *)calloc(1,
1415 sizeof (subclass_lst_t));
1416 if (sc_list == NULL) {
1417 goto create_failed;
1418 }
1419
1420 sc_list->sl_next = clist->cl_subclass_list;
1421 clist->cl_subclass_list = sc_list;
1422
1423 sc_list->sl_name = strdup(nvpair_name(nvpair));
1424 if (sc_list->sl_name == NULL) {
1425 goto create_failed;
1426 }
1427
1428 if (nvpair_value_byte_array(nvpair,
1429 &subscribers, &num_elem) != 0) {
1430 goto create_failed;
1431 }
1432 bcopy(subscribers, (uchar_t *)sc_list->sl_num,
1433 MAX_SUBSCRIBERS + 1);
1434
1435 for (j = 1; j <= MAX_SUBSCRIBERS; ++j) {
1436 if (sc_list->sl_num[j] == 0)
1437 continue;
1438
1439 if (alloc_subscriber(shp, j, 1) != 0) {
1440 goto create_failed;
1441 }
1442 }
1443
1444 /*
1445 * Check next nvpair - either subclass or
1446 * class
1447 */
1448 if ((nvpair = nvlist_next_nvpair(nvl, nvpair))
1449 == NULL) {
1450 new_class = 0;
1451 break;
1452 } else if (strcmp(nvpair_name(nvpair),
1453 CLASS_NAME) == 0) {
1454 break;
1455 }
1456 }
1457 }
1458 nvlist_free(nvl);
1459 }
1460 return (0);
1461
1462 create_failed:
1463 dealloc_subscribers(shp);
1464 free_cached_registration(shp);
1465 nvlist_free(nvl);
1466 return (-1);
1467
1468 }
1469
1470 /*
1471 * cache_update_service - generic event publisher service routine. This routine
1472 * is called in response to a registration cache update.
1473 *
1474 */
1475 /*ARGSUSED*/
1476 static void
cache_update_service(void * cookie,char * args,size_t alen,door_desc_t * ddp,uint_t ndid)1477 cache_update_service(void *cookie, char *args, size_t alen,
1478 door_desc_t *ddp, uint_t ndid)
1479 {
1480 int ret = 0;
1481 uint_t num_elem;
1482 char *class, **event_list;
1483 size_t datalen;
1484 uint32_t sub_id;
1485 nvlist_t *nvl;
1486 nvpair_t *nvpair = NULL;
1487 struct reg_args *rargs;
1488 sysevent_handle_t *shp;
1489 subscriber_data_t *sub;
1490
1491 if (alen < sizeof (struct reg_args) || cookie == NULL) {
1492 ret = EINVAL;
1493 goto return_from_door;
1494 }
1495
1496 /* LINTED: E_BAD_PTR_CAST_ALIGN */
1497 rargs = (struct reg_args *)args;
1498 shp = (sysevent_handle_t *)cookie;
1499
1500 datalen = alen - sizeof (struct reg_args);
1501 sub_id = rargs->ra_sub_id;
1502
1503 (void) mutex_lock(SH_LOCK(shp));
1504
1505 switch (rargs->ra_op) {
1506 case SE_UNREGISTER:
1507 class = (char *)&rargs->ra_buf_ptr;
1508 cache_remove_class(shp, (char *)class,
1509 sub_id);
1510 break;
1511 case SE_UNBIND_REGISTRATION:
1512
1513 sub = SH_SUBSCRIBER(shp, sub_id);
1514 if (sub == NULL)
1515 break;
1516
1517 free(sub->sd_door_name);
1518 free(sub);
1519 cache_remove_class(shp, EC_ALL, sub_id);
1520 SH_SUBSCRIBER(shp, sub_id) = NULL;
1521
1522 break;
1523 case SE_BIND_REGISTRATION:
1524
1525 /* New subscriber */
1526 if (alloc_subscriber(shp, sub_id, 0) != 0) {
1527 ret = ENOMEM;
1528 break;
1529 }
1530 break;
1531 case SE_REGISTER:
1532
1533 if (SH_SUBSCRIBER(shp, sub_id) == NULL) {
1534 ret = EINVAL;
1535 break;
1536 }
1537 /* Get new registration data */
1538 if (nvlist_unpack((char *)&rargs->ra_buf_ptr, datalen,
1539 &nvl, 0) != 0) {
1540 ret = EFAULT;
1541 break;
1542 }
1543 if ((nvpair = nvlist_next_nvpair(nvl, nvpair)) == NULL) {
1544 nvlist_free(nvl);
1545 ret = EFAULT;
1546 break;
1547 }
1548 if (nvpair_value_string_array(nvpair, &event_list, &num_elem)
1549 != 0) {
1550 nvlist_free(nvl);
1551 ret = EFAULT;
1552 break;
1553 }
1554 class = nvpair_name(nvpair);
1555
1556 ret = cache_insert_class(shp, class,
1557 event_list, num_elem, sub_id);
1558 if (ret != 0) {
1559 cache_remove_class(shp, class, sub_id);
1560 nvlist_free(nvl);
1561 ret = EFAULT;
1562 break;
1563 }
1564
1565 nvlist_free(nvl);
1566
1567 break;
1568 case SE_CLEANUP:
1569 /* Cleanup stale subscribers */
1570 sysevent_cleanup_subscribers(shp);
1571 break;
1572 default:
1573 ret = EINVAL;
1574 }
1575
1576 (void) mutex_unlock(SH_LOCK(shp));
1577
1578 return_from_door:
1579 (void) door_return((void *)&ret, sizeof (ret), NULL, 0);
1580 (void) door_return(NULL, 0, NULL, 0);
1581 }
1582
1583 /*
1584 * sysevent_send_event -
1585 * Send an event via the communication channel associated with the sysevent
1586 * handle. Event notifications are broadcast to all subscribers based upon
1587 * the event class and subclass. The handle must have been previously
1588 * allocated and bound by
1589 * sysevent_open_channel() and sysevent_bind_publisher()
1590 */
1591 int
sysevent_send_event(sysevent_handle_t * shp,sysevent_t * ev)1592 sysevent_send_event(sysevent_handle_t *shp, sysevent_t *ev)
1593 {
1594 int i, error, sub_fd, result = 0;
1595 int deliver_error = 0;
1596 int subscribers_sent = 0;
1597 int want_resend, resend_cnt = 0;
1598 char *event_class, *event_subclass;
1599 uchar_t *all_class_subscribers, *all_subclass_subscribers;
1600 uchar_t *subclass_subscribers;
1601 subscriber_data_t *sub;
1602 subclass_lst_t *sc_lst;
1603
1604 /* Check for proper registration */
1605 event_class = sysevent_get_class_name(ev);
1606 event_subclass = sysevent_get_subclass_name(ev);
1607
1608 (void) mutex_lock(SH_LOCK(shp));
1609
1610 send_event:
1611
1612 want_resend = 0;
1613 if (!SH_BOUND(shp)) {
1614 (void) mutex_unlock(SH_LOCK(shp));
1615 errno = EINVAL;
1616 return (-1);
1617 }
1618
1619 /* Find all subscribers for this event class/subclass */
1620 sc_lst = cache_find_subclass(
1621 cache_find_class(shp, EC_ALL), EC_SUB_ALL);
1622 all_class_subscribers = sc_lst->sl_num;
1623
1624 sc_lst = cache_find_subclass(
1625 cache_find_class(shp, event_class), EC_SUB_ALL);
1626 if (sc_lst)
1627 all_subclass_subscribers = sc_lst->sl_num;
1628 else
1629 all_subclass_subscribers = NULL;
1630
1631 sc_lst = cache_find_subclass(
1632 cache_find_class(shp, event_class), event_subclass);
1633 if (sc_lst)
1634 subclass_subscribers = sc_lst->sl_num;
1635 else
1636 subclass_subscribers = NULL;
1637
1638 /* Send event buffer to all valid subscribers */
1639 for (i = 1; i <= MAX_SUBSCRIBERS; ++i) {
1640 if ((all_class_subscribers[i] |
1641 (all_subclass_subscribers && all_subclass_subscribers[i]) |
1642 (subclass_subscribers && subclass_subscribers[i])) == 0)
1643 continue;
1644
1645 sub = SH_SUBSCRIBER(shp, i);
1646 assert(sub != NULL);
1647
1648 /* Check for active subscriber */
1649 if (!(sub->sd_flag & ACTIVE)) {
1650 dprint("sysevent_send_event: subscriber %d inactive\n",
1651 i);
1652 continue;
1653 }
1654
1655 /* Process only resend requests */
1656 if (resend_cnt > 0 && !(sub->sd_flag & SEND_AGAIN)) {
1657 continue;
1658 }
1659
1660 if ((sub_fd = open(sub->sd_door_name, O_RDONLY)) == -1) {
1661 dprint("sysevent_send_event: Failed to open "
1662 "%s: %s\n", sub->sd_door_name, strerror(errno));
1663 continue;
1664 }
1665 result = 0;
1666 error = clnt_deliver_event(sub_fd, ev,
1667 sysevent_get_size(ev), &result, sizeof (result));
1668
1669 (void) close(sub_fd);
1670
1671 /* Successful door call */
1672 if (error == 0) {
1673 switch (result) {
1674 /* Subscriber requested EAGAIN */
1675 case EAGAIN:
1676 if (resend_cnt > SE_MAX_RETRY_LIMIT) {
1677 deliver_error = 1;
1678 } else {
1679 want_resend = 1;
1680 dprint("sysevent_send_event: resend "
1681 "requested for %d\n", i);
1682 sub->sd_flag |= SEND_AGAIN;
1683 }
1684 break;
1685 /* Bad sysevent handle for subscriber */
1686 case EBADF:
1687 case EINVAL:
1688 dprint("sysevent_send_event: Bad sysevent "
1689 "handle for %s", sub->sd_door_name);
1690 sub->sd_flag = 0;
1691 deliver_error = 1;
1692 break;
1693 /* Successful delivery */
1694 default:
1695 sub->sd_flag &= ~SEND_AGAIN;
1696 ++subscribers_sent;
1697 }
1698 } else {
1699 dprint("sysevent_send_event: Failed door call "
1700 "to %s: %s: %d\n", sub->sd_door_name,
1701 strerror(errno), result);
1702 sub->sd_flag = 0;
1703 deliver_error = 1;
1704 }
1705 }
1706
1707 if (want_resend) {
1708 resend_cnt++;
1709 goto send_event;
1710 }
1711
1712 if (deliver_error) {
1713 sysevent_cleanup_subscribers(shp);
1714 (void) mutex_unlock(SH_LOCK(shp));
1715 errno = EFAULT;
1716 return (-1);
1717 }
1718
1719 (void) mutex_unlock(SH_LOCK(shp));
1720
1721 if (subscribers_sent == 0) {
1722 dprint("sysevent_send_event: No subscribers for %s:%s\n",
1723 event_class, event_subclass);
1724 errno = ENOENT;
1725 return (-1);
1726 }
1727
1728 return (0);
1729 }
1730
1731 /*
1732 * Common routine to establish an event channel through which an event
1733 * publisher or subscriber may post or receive events.
1734 */
1735 static sysevent_handle_t *
sysevent_open_channel_common(const char * channel_path)1736 sysevent_open_channel_common(const char *channel_path)
1737 {
1738 uint32_t sub_id = 0;
1739 char *begin_path;
1740 struct stat chan_stat;
1741 sysevent_handle_t *shp;
1742
1743
1744 if (channel_path == NULL || strlen(channel_path) + 1 > MAXPATHLEN) {
1745 errno = EINVAL;
1746 return (NULL);
1747 }
1748
1749 if (mkdir(channel_path, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH) < 0) {
1750 if (errno != EEXIST) {
1751 errno = EACCES;
1752 return (NULL);
1753 }
1754 }
1755
1756 /* Check channel file permissions */
1757 if (stat(channel_path, &chan_stat) != 0) {
1758 dprint("sysevent_open_channel: Invalid permissions for channel "
1759 "%s\n", channel_path);
1760 errno = EACCES;
1761 return (NULL);
1762 } else if (chan_stat.st_uid != getuid() ||
1763 !S_ISDIR(chan_stat.st_mode)) {
1764 dprint("sysevent_open_channel: Invalid "
1765 "permissions for channel %s\n: %d:%d:%d", channel_path,
1766 (int)chan_stat.st_uid, (int)chan_stat.st_gid,
1767 (int)chan_stat.st_mode);
1768
1769 errno = EACCES;
1770 return (NULL);
1771 }
1772
1773 shp = calloc(1, sizeof (sysevent_impl_hdl_t));
1774 if (shp == NULL) {
1775 errno = ENOMEM;
1776 return (NULL);
1777 }
1778
1779 SH_CHANNEL_NAME(shp) = NULL;
1780 SH_CHANNEL_PATH(shp) = strdup(channel_path);
1781 if (SH_CHANNEL_PATH(shp) == NULL) {
1782 free(shp);
1783 errno = ENOMEM;
1784 return (NULL);
1785 }
1786
1787 /* Extract the channel name */
1788 begin_path = SH_CHANNEL_PATH(shp);
1789 while (*begin_path != '\0' &&
1790 (begin_path = strpbrk(begin_path, "/")) != NULL) {
1791 ++begin_path;
1792 SH_CHANNEL_NAME(shp) = begin_path;
1793 }
1794
1795 if (update_kernel_registration(shp, 0,
1796 SE_OPEN_REGISTRATION, &sub_id, 0, NULL) != 0) {
1797 dprint("sysevent_open_channel: Failed for channel %s\n",
1798 SH_CHANNEL_NAME(shp));
1799 free(SH_CHANNEL_PATH(shp));
1800 free(shp);
1801 errno = EFAULT;
1802 return (NULL);
1803 }
1804
1805 (void) mutex_init(SH_LOCK(shp), USYNC_THREAD, NULL);
1806
1807 return (shp);
1808 }
1809
1810 /*
1811 * Establish a sysevent channel for publication and subscription
1812 */
1813 sysevent_handle_t *
sysevent_open_channel(const char * channel)1814 sysevent_open_channel(const char *channel)
1815 {
1816 int var_run_mounted = 0;
1817 char full_channel[MAXPATHLEN + 1];
1818 FILE *fp;
1819 struct stat chan_stat;
1820 struct extmnttab m;
1821
1822 if (channel == NULL) {
1823 errno = EINVAL;
1824 return (NULL);
1825 }
1826
1827 /*
1828 * Check that /var/run is mounted as tmpfs before allowing a channel
1829 * to be opened.
1830 */
1831 if ((fp = fopen(MNTTAB, "rF")) == NULL) {
1832 errno = EACCES;
1833 return (NULL);
1834 }
1835
1836 resetmnttab(fp);
1837
1838 while (getextmntent(fp, &m, sizeof (struct extmnttab)) == 0) {
1839 if (strcmp(m.mnt_mountp, "/var/run") == 0 &&
1840 strcmp(m.mnt_fstype, "tmpfs") == 0) {
1841 var_run_mounted = 1;
1842 break;
1843 }
1844 }
1845 (void) fclose(fp);
1846
1847 if (!var_run_mounted) {
1848 errno = EACCES;
1849 return (NULL);
1850 }
1851
1852 if (stat(CHAN_PATH, &chan_stat) < 0) {
1853 if (mkdir(CHAN_PATH,
1854 S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH) < 0) {
1855 dprint("sysevent_open_channel: Unable "
1856 "to create channel directory %s:%s\n", CHAN_PATH,
1857 strerror(errno));
1858 if (errno != EEXIST) {
1859 errno = EACCES;
1860 return (NULL);
1861 }
1862 }
1863 }
1864
1865 if (snprintf(full_channel, MAXPATHLEN, "%s/%s", CHAN_PATH, channel) >=
1866 MAXPATHLEN) {
1867 errno = EINVAL;
1868 return (NULL);
1869 }
1870
1871 return (sysevent_open_channel_common(full_channel));
1872 }
1873
1874 /*
1875 * Establish a sysevent channel for publication and subscription
1876 * Full path to the channel determined by the caller
1877 */
1878 sysevent_handle_t *
sysevent_open_channel_alt(const char * channel_path)1879 sysevent_open_channel_alt(const char *channel_path)
1880 {
1881 return (sysevent_open_channel_common(channel_path));
1882 }
1883
1884 /*
1885 * sysevent_close_channel - Clean up resources associated with a previously
1886 * opened sysevent channel
1887 */
1888 void
sysevent_close_channel(sysevent_handle_t * shp)1889 sysevent_close_channel(sysevent_handle_t *shp)
1890 {
1891 int error = errno;
1892 uint32_t sub_id = 0;
1893
1894 if (shp == NULL) {
1895 return;
1896 }
1897
1898 (void) mutex_lock(SH_LOCK(shp));
1899 if (SH_BOUND(shp)) {
1900 (void) mutex_unlock(SH_LOCK(shp));
1901 if (SH_TYPE(shp) == PUBLISHER)
1902 sysevent_unbind_publisher(shp);
1903 else if (SH_TYPE(shp) == SUBSCRIBER)
1904 sysevent_unbind_subscriber(shp);
1905 (void) mutex_lock(SH_LOCK(shp));
1906 }
1907
1908 (void) update_kernel_registration(shp, 0,
1909 SE_CLOSE_REGISTRATION, &sub_id, 0, NULL);
1910 (void) mutex_unlock(SH_LOCK(shp));
1911
1912 free(SH_CHANNEL_PATH(shp));
1913 free(shp);
1914 errno = error;
1915 }
1916
1917 /*
1918 * sysevent_bind_publisher - Bind an event publisher to an event channel
1919 */
1920 int
sysevent_bind_publisher(sysevent_handle_t * shp)1921 sysevent_bind_publisher(sysevent_handle_t *shp)
1922 {
1923 int error = 0;
1924 int fd = -1;
1925 char door_name[MAXPATHLEN];
1926 uint32_t pub_id;
1927 struct stat reg_stat;
1928 publisher_priv_t *pub;
1929
1930 if (shp == NULL) {
1931 errno = EINVAL;
1932 return (-1);
1933 }
1934
1935 (void) mutex_lock(SH_LOCK(shp));
1936 if (SH_BOUND(shp)) {
1937 (void) mutex_unlock(SH_LOCK(shp));
1938 errno = EINVAL;
1939 return (-1);
1940 }
1941
1942 if ((pub = (publisher_priv_t *)calloc(1, sizeof (publisher_priv_t))) ==
1943 NULL) {
1944 (void) mutex_unlock(SH_LOCK(shp));
1945 errno = ENOMEM;
1946 return (-1);
1947 }
1948 SH_PRIV_DATA(shp) = (void *)pub;
1949
1950 if (snprintf(door_name, MAXPATHLEN, "%s/%s",
1951 SH_CHANNEL_PATH(shp), REG_DOOR) >= MAXPATHLEN) {
1952 free(pub);
1953 (void) mutex_unlock(SH_LOCK(shp));
1954 errno = ENOMEM;
1955 return (-1);
1956 }
1957 if ((SH_DOOR_NAME(shp) = strdup(door_name)) == NULL) {
1958 free(pub);
1959 (void) mutex_unlock(SH_LOCK(shp));
1960 errno = ENOMEM;
1961 return (-1);
1962 }
1963
1964 /* Only one publisher allowed per channel */
1965 if (stat(SH_DOOR_NAME(shp), ®_stat) != 0) {
1966 if (errno != ENOENT) {
1967 error = EINVAL;
1968 goto fail;
1969 }
1970 }
1971
1972 /*
1973 * Remove door file for robustness.
1974 */
1975 if (unlink(SH_DOOR_NAME(shp)) != 0)
1976 dprint("sysevent_bind_publisher: Unlink of %s failed.\n",
1977 SH_DOOR_NAME(shp));
1978
1979 /* Open channel registration door */
1980 fd = open(SH_DOOR_NAME(shp), O_CREAT|O_RDWR,
1981 S_IREAD|S_IWRITE);
1982 if (fd == -1) {
1983 error = EINVAL;
1984 goto fail;
1985 }
1986
1987 /*
1988 * Create the registration service for this publisher.
1989 */
1990 if ((SH_DOOR_DESC(shp) = door_create(cache_update_service,
1991 (void *)shp, DOOR_REFUSE_DESC | DOOR_NO_CANCEL)) == -1) {
1992 dprint("sysevent_bind_publisher: door create failed: "
1993 "%s\n", strerror(errno));
1994 error = EFAULT;
1995 goto fail;
1996 }
1997
1998 (void) fdetach(SH_DOOR_NAME(shp));
1999 if (fattach(SH_DOOR_DESC(shp), SH_DOOR_NAME(shp)) != 0) {
2000 dprint("sysevent_bind_publisher: unable to "
2001 "bind event channel: fattach: %s\n",
2002 SH_DOOR_NAME(shp));
2003 error = EACCES;
2004 goto fail;
2005 }
2006
2007 /* Bind this publisher in the kernel registration database */
2008 if (update_kernel_registration(shp, PUBLISHER,
2009 SE_BIND_REGISTRATION, &pub_id, 0, NULL) != 0) {
2010 error = errno;
2011 goto fail;
2012 }
2013
2014 SH_ID(shp) = pub_id;
2015 SH_BOUND(shp) = 1;
2016 SH_TYPE(shp) = PUBLISHER;
2017
2018
2019 /* Create the subscription registration cache */
2020 if (create_cached_registration(shp, SH_CLASS_HASH(shp)) != 0) {
2021 (void) update_kernel_registration(shp,
2022 PUBLISHER, SE_UNBIND_REGISTRATION, &pub_id, 0, NULL);
2023 error = EFAULT;
2024 goto fail;
2025 }
2026 (void) close(fd);
2027
2028 (void) mutex_unlock(SH_LOCK(shp));
2029
2030 return (0);
2031
2032 fail:
2033 SH_BOUND(shp) = 0;
2034 (void) door_revoke(SH_DOOR_DESC(shp));
2035 (void) fdetach(SH_DOOR_NAME(shp));
2036 free(SH_DOOR_NAME(shp));
2037 free(pub);
2038 (void) close(fd);
2039 (void) mutex_unlock(SH_LOCK(shp));
2040 errno = error;
2041 return (-1);
2042 }
2043
2044 static pthread_once_t xdoor_thrattr_once = PTHREAD_ONCE_INIT;
2045 static pthread_attr_t xdoor_thrattr;
2046
2047 static void
xdoor_thrattr_init(void)2048 xdoor_thrattr_init(void)
2049 {
2050 (void) pthread_attr_init(&xdoor_thrattr);
2051 (void) pthread_attr_setdetachstate(&xdoor_thrattr,
2052 PTHREAD_CREATE_DETACHED);
2053 (void) pthread_attr_setscope(&xdoor_thrattr, PTHREAD_SCOPE_SYSTEM);
2054 }
2055
2056 static int
xdoor_server_create(door_info_t * dip,void * (* startf)(void *),void * startfarg,void * cookie)2057 xdoor_server_create(door_info_t *dip, void *(*startf)(void *),
2058 void *startfarg, void *cookie)
2059 {
2060 struct sysevent_subattr_impl *xsa = cookie;
2061 pthread_attr_t *thrattr;
2062 sigset_t oset;
2063 int err;
2064
2065 if (xsa->xs_thrcreate) {
2066 return (xsa->xs_thrcreate(dip, startf, startfarg,
2067 xsa->xs_thrcreate_cookie));
2068 }
2069
2070 if (xsa->xs_thrattr == NULL) {
2071 (void) pthread_once(&xdoor_thrattr_once, xdoor_thrattr_init);
2072 thrattr = &xdoor_thrattr;
2073 } else {
2074 thrattr = xsa->xs_thrattr;
2075 }
2076
2077 (void) pthread_sigmask(SIG_SETMASK, &xsa->xs_sigmask, &oset);
2078 err = pthread_create(NULL, thrattr, startf, startfarg);
2079 (void) pthread_sigmask(SIG_SETMASK, &oset, NULL);
2080
2081 return (err == 0 ? 1 : -1);
2082 }
2083
2084 static void
xdoor_server_setup(void * cookie)2085 xdoor_server_setup(void *cookie)
2086 {
2087 struct sysevent_subattr_impl *xsa = cookie;
2088
2089 if (xsa->xs_thrsetup) {
2090 xsa->xs_thrsetup(xsa->xs_thrsetup_cookie);
2091 } else {
2092 (void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
2093 (void) pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
2094 }
2095 }
2096
2097 static int
sysevent_bind_subscriber_cmn(sysevent_handle_t * shp,void (* event_handler)(sysevent_t * ev),sysevent_subattr_t * subattr)2098 sysevent_bind_subscriber_cmn(sysevent_handle_t *shp,
2099 void (*event_handler)(sysevent_t *ev),
2100 sysevent_subattr_t *subattr)
2101 {
2102 int fd = -1;
2103 int error = 0;
2104 uint32_t sub_id = 0;
2105 char door_name[MAXPATHLEN];
2106 subscriber_priv_t *sub_info;
2107 int created;
2108 struct sysevent_subattr_impl *xsa =
2109 (struct sysevent_subattr_impl *)subattr;
2110
2111 if (shp == NULL || event_handler == NULL) {
2112 errno = EINVAL;
2113 return (-1);
2114 }
2115
2116 (void) mutex_lock(SH_LOCK(shp));
2117 if (SH_BOUND(shp)) {
2118 errno = EINVAL;
2119 (void) mutex_unlock(SH_LOCK(shp));
2120 return (-1);
2121 }
2122
2123 if ((sub_info = (subscriber_priv_t *)calloc(1,
2124 sizeof (subscriber_priv_t))) == NULL) {
2125 errno = ENOMEM;
2126 (void) mutex_unlock(SH_LOCK(shp));
2127 return (-1);
2128 }
2129
2130 if (snprintf(door_name, MAXPATHLEN, "%s/%s",
2131 SH_CHANNEL_PATH(shp), REG_DOOR) >= MAXPATHLEN) {
2132 free(sub_info);
2133 errno = EINVAL;
2134 (void) mutex_unlock(SH_LOCK(shp));
2135 return (-1);
2136 }
2137
2138 if ((sub_info->sp_door_name = strdup(door_name)) == NULL) {
2139 free(sub_info);
2140 errno = ENOMEM;
2141 (void) mutex_unlock(SH_LOCK(shp));
2142 return (-1);
2143 }
2144 (void) cond_init(&sub_info->sp_cv, USYNC_THREAD, NULL);
2145 (void) mutex_init(&sub_info->sp_qlock, USYNC_THREAD, NULL);
2146 sub_info->sp_func = event_handler;
2147
2148 /* Update the in-kernel registration */
2149 if (update_kernel_registration(shp, SUBSCRIBER,
2150 SE_BIND_REGISTRATION, &sub_id, 0, NULL) != 0) {
2151 error = errno;
2152 goto fail;
2153 }
2154 SH_ID(shp) = sub_id;
2155
2156 if (snprintf(door_name, MAXPATHLEN, "%s/%d",
2157 SH_CHANNEL_PATH(shp), sub_id) >= MAXPATHLEN) {
2158 error = EINVAL;
2159 goto fail;
2160 }
2161 if ((SH_DOOR_NAME(shp) = strdup(door_name)) == NULL) {
2162 error = ENOMEM;
2163 goto fail;
2164 }
2165
2166 /*
2167 * Remove door file for robustness.
2168 */
2169 if (unlink(SH_DOOR_NAME(shp)) != 0)
2170 dprint("sysevent_bind_subscriber: Unlink of %s failed.\n",
2171 SH_DOOR_NAME(shp));
2172
2173 fd = open(SH_DOOR_NAME(shp), O_CREAT|O_RDWR, S_IREAD|S_IWRITE);
2174 if (fd == -1) {
2175 error = EFAULT;
2176 goto fail;
2177 }
2178
2179 /*
2180 * Create the sysevent door service for this client.
2181 * syseventd will use this door service to propagate
2182 * events to the client.
2183 */
2184 if (subattr == NULL) {
2185 SH_DOOR_DESC(shp) = door_create(event_deliver_service,
2186 (void *)shp, DOOR_REFUSE_DESC | DOOR_NO_CANCEL);
2187 } else {
2188 SH_DOOR_DESC(shp) = door_xcreate(event_deliver_service,
2189 (void *)shp,
2190 DOOR_REFUSE_DESC | DOOR_NO_CANCEL | DOOR_NO_DEPLETION_CB,
2191 xdoor_server_create, xdoor_server_setup,
2192 (void *)subattr, 1);
2193 }
2194
2195 if (SH_DOOR_DESC(shp) == -1) {
2196 dprint("sysevent_bind_subscriber: door create failed: "
2197 "%s\n", strerror(errno));
2198 error = EFAULT;
2199 goto fail;
2200 }
2201
2202 (void) fdetach(SH_DOOR_NAME(shp));
2203 if (fattach(SH_DOOR_DESC(shp), SH_DOOR_NAME(shp)) != 0) {
2204 error = EFAULT;
2205 goto fail;
2206 }
2207 (void) close(fd);
2208
2209 if (update_publisher_cache(sub_info, SE_BIND_REGISTRATION,
2210 sub_id, 0, NULL) != 0) {
2211 error = errno;
2212 (void) update_kernel_registration(shp, SUBSCRIBER,
2213 SE_UNBIND_REGISTRATION, &sub_id, 0, NULL);
2214 goto fail;
2215 }
2216
2217 SH_BOUND(shp) = 1;
2218 SH_TYPE(shp) = SUBSCRIBER;
2219 SH_PRIV_DATA(shp) = (void *)sub_info;
2220
2221 /* Create an event handler thread */
2222 if (xsa == NULL || xsa->xs_thrcreate == NULL) {
2223 created = thr_create(NULL, 0, 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, subscriber_event_handler,
2242 shp, xsa->xs_thrcreate_cookie) == 1;
2243 }
2244
2245 if (!created) {
2246 error = EFAULT;
2247 goto fail;
2248 }
2249
2250 (void) mutex_unlock(SH_LOCK(shp));
2251
2252 return (0);
2253
2254 fail:
2255 (void) close(fd);
2256 (void) door_revoke(SH_DOOR_DESC(shp));
2257 (void) fdetach(SH_DOOR_NAME(shp));
2258 (void) cond_destroy(&sub_info->sp_cv);
2259 (void) mutex_destroy(&sub_info->sp_qlock);
2260 free(sub_info->sp_door_name);
2261 free(sub_info);
2262 if (SH_ID(shp)) {
2263 (void) update_kernel_registration(shp, SUBSCRIBER,
2264 SE_UNBIND_REGISTRATION, &sub_id, 0, NULL);
2265 SH_ID(shp) = 0;
2266 }
2267 if (SH_BOUND(shp)) {
2268 (void) update_publisher_cache(sub_info, SE_UNBIND_REGISTRATION,
2269 sub_id, 0, NULL);
2270 free(SH_DOOR_NAME(shp));
2271 SH_BOUND(shp) = 0;
2272 }
2273 (void) mutex_unlock(SH_LOCK(shp));
2274
2275 errno = error;
2276
2277 return (-1);
2278 }
2279
2280 /*
2281 * sysevent_bind_subscriber - Bind an event receiver to an event channel
2282 */
2283 int
sysevent_bind_subscriber(sysevent_handle_t * shp,void (* event_handler)(sysevent_t * ev))2284 sysevent_bind_subscriber(sysevent_handle_t *shp,
2285 void (*event_handler)(sysevent_t *ev))
2286 {
2287 return (sysevent_bind_subscriber_cmn(shp, event_handler, NULL));
2288 }
2289
2290 /*
2291 * sysevent_bind_xsubscriber - Bind a subscriber using door_xcreate with
2292 * attributes specified.
2293 */
2294 int
sysevent_bind_xsubscriber(sysevent_handle_t * shp,void (* event_handler)(sysevent_t * ev),sysevent_subattr_t * subattr)2295 sysevent_bind_xsubscriber(sysevent_handle_t *shp,
2296 void (*event_handler)(sysevent_t *ev), sysevent_subattr_t *subattr)
2297 {
2298 return (sysevent_bind_subscriber_cmn(shp, event_handler, subattr));
2299 }
2300
2301 /*
2302 * sysevent_register_event - register an event class and associated subclasses
2303 * for an event subscriber
2304 */
2305 int
sysevent_register_event(sysevent_handle_t * shp,const char * ev_class,const char ** ev_subclass,int subclass_num)2306 sysevent_register_event(sysevent_handle_t *shp,
2307 const char *ev_class, const char **ev_subclass,
2308 int subclass_num)
2309 {
2310 int error;
2311 char *event_class = (char *)ev_class;
2312 char **event_subclass_list = (char **)ev_subclass;
2313 char *nvlbuf = NULL;
2314 size_t datalen;
2315 nvlist_t *nvl;
2316
2317 (void) mutex_lock(SH_LOCK(shp));
2318 if (event_class == NULL || event_subclass_list == NULL ||
2319 event_subclass_list[0] == NULL || SH_BOUND(shp) != 1 ||
2320 subclass_num <= 0) {
2321 (void) mutex_unlock(SH_LOCK(shp));
2322 errno = EINVAL;
2323 return (-1);
2324 }
2325
2326 if (nvlist_alloc(&nvl, NV_UNIQUE_NAME_TYPE, 0) != 0) {
2327 (void) mutex_unlock(SH_LOCK(shp));
2328 return (-1);
2329 }
2330 if (nvlist_add_string_array(nvl, event_class, event_subclass_list,
2331 subclass_num) != 0) {
2332 nvlist_free(nvl);
2333 (void) mutex_unlock(SH_LOCK(shp));
2334 return (-1);
2335 }
2336 if (nvlist_pack(nvl, &nvlbuf, &datalen, NV_ENCODE_NATIVE, 0) != 0) {
2337 nvlist_free(nvl);
2338 (void) mutex_unlock(SH_LOCK(shp));
2339 return (-1);
2340 }
2341 nvlist_free(nvl);
2342
2343 /* Store new subscriber in in-kernel registration */
2344 if (update_kernel_registration(shp, SUBSCRIBER,
2345 SE_REGISTER, &SH_ID(shp), datalen, (uchar_t *)nvlbuf)
2346 != 0) {
2347 error = errno;
2348 free(nvlbuf);
2349 (void) mutex_unlock(SH_LOCK(shp));
2350 errno = error;
2351 return (-1);
2352 }
2353 /* Update the publisher's cached registration */
2354 if (update_publisher_cache(
2355 (subscriber_priv_t *)SH_PRIV_DATA(shp), SE_REGISTER,
2356 SH_ID(shp), datalen, (uchar_t *)nvlbuf) != 0) {
2357 error = errno;
2358 free(nvlbuf);
2359 (void) mutex_unlock(SH_LOCK(shp));
2360 errno = error;
2361 return (-1);
2362 }
2363
2364 free(nvlbuf);
2365
2366 (void) mutex_unlock(SH_LOCK(shp));
2367
2368 return (0);
2369 }
2370
2371 /*
2372 * sysevent_unregister_event - Unregister an event class and associated
2373 * subclasses for an event subscriber
2374 */
2375 void
sysevent_unregister_event(sysevent_handle_t * shp,const char * class)2376 sysevent_unregister_event(sysevent_handle_t *shp, const char *class)
2377 {
2378 size_t class_sz;
2379
2380 (void) mutex_lock(SH_LOCK(shp));
2381
2382 if (!SH_BOUND(shp)) {
2383 (void) mutex_unlock(SH_LOCK(shp));
2384 return;
2385 }
2386
2387 /* Remove subscriber from in-kernel registration */
2388 class_sz = strlen(class) + 1;
2389 (void) update_kernel_registration(shp, SUBSCRIBER,
2390 SE_UNREGISTER, &SH_ID(shp), class_sz, (uchar_t *)class);
2391 /* Update the publisher's cached registration */
2392 (void) update_publisher_cache(
2393 (subscriber_priv_t *)SH_PRIV_DATA(shp), SE_UNREGISTER,
2394 SH_ID(shp), class_sz, (uchar_t *)class);
2395
2396 (void) mutex_unlock(SH_LOCK(shp));
2397 }
2398
2399 static int
cleanup_id(sysevent_handle_t * shp,uint32_t id,int type)2400 cleanup_id(sysevent_handle_t *shp, uint32_t id, int type)
2401 {
2402 dprint("cleanup_id: Cleaning up %s/%d\n", SH_CHANNEL_NAME(shp), id);
2403
2404 /* Remove registration from the kernel */
2405 if (update_kernel_registration(shp, type, SE_CLEANUP, &id,
2406 0, NULL) != 0) {
2407 dprint("cleanup_id: Unable to clean "
2408 "up %s/%d\n", SH_CHANNEL_NAME(shp), id);
2409 return (-1);
2410 }
2411
2412 return (0);
2413 }
2414
2415 /*
2416 * sysevent_cleanup_subscribers: Allows the caller to cleanup resources
2417 * allocated to unresponsive subscribers.
2418 */
2419 void
sysevent_cleanup_subscribers(sysevent_handle_t * shp)2420 sysevent_cleanup_subscribers(sysevent_handle_t *shp)
2421 {
2422 uint32_t ping, result;
2423 int i, error, sub_fd;
2424 subscriber_data_t *sub;
2425
2426 if (!SH_BOUND(shp)) {
2427 return;
2428 }
2429
2430 for (i = 1; i <= MAX_SUBSCRIBERS; ++i) {
2431
2432 sub = SH_SUBSCRIBER(shp, i);
2433 if (sub == NULL) {
2434 continue;
2435 }
2436
2437 if ((sub_fd = open(sub->sd_door_name, O_RDONLY)) == -1) {
2438 continue;
2439 }
2440 /* Check for valid and responsive subscriber */
2441 error = clnt_deliver_event(sub_fd, &ping,
2442 sizeof (uint32_t), &result, sizeof (result));
2443 (void) close(sub_fd);
2444
2445 /* Only cleanup on EBADF (Invalid door descriptor) */
2446 if (error != EBADF)
2447 continue;
2448
2449 if (cleanup_id(shp, i, SUBSCRIBER) != 0)
2450 continue;
2451
2452 cache_remove_class(shp, EC_ALL, i);
2453
2454 free(sub->sd_door_name);
2455 free(sub);
2456 SH_SUBSCRIBER(shp, i) = NULL;
2457 }
2458
2459 }
2460
2461 /*
2462 * sysevent_cleanup_publishers: Allows stale publisher handles to be deallocated
2463 * as needed.
2464 */
2465 void
sysevent_cleanup_publishers(sysevent_handle_t * shp)2466 sysevent_cleanup_publishers(sysevent_handle_t *shp)
2467 {
2468 (void) cleanup_id(shp, 1, PUBLISHER);
2469 }
2470
2471 /*
2472 * sysevent_unbind_subscriber: Unbind the subscriber from the sysevent channel.
2473 */
2474 void
sysevent_unbind_subscriber(sysevent_handle_t * shp)2475 sysevent_unbind_subscriber(sysevent_handle_t *shp)
2476 {
2477 subscriber_priv_t *sub_info;
2478
2479 if (shp == NULL)
2480 return;
2481
2482 (void) mutex_lock(SH_LOCK(shp));
2483 if (SH_BOUND(shp) == 0) {
2484 (void) mutex_unlock(SH_LOCK(shp));
2485 return;
2486 }
2487
2488 /* Update the in-kernel registration */
2489 (void) update_kernel_registration(shp, SUBSCRIBER,
2490 SE_UNBIND_REGISTRATION, &SH_ID(shp), 0, NULL);
2491
2492 /* Update the sysevent channel publisher */
2493 sub_info = (subscriber_priv_t *)SH_PRIV_DATA(shp);
2494 (void) update_publisher_cache(sub_info, SE_UNBIND_REGISTRATION,
2495 SH_ID(shp), 0, NULL);
2496
2497 /* Close down event delivery facilities */
2498 (void) door_revoke(SH_DOOR_DESC(shp));
2499 (void) fdetach(SH_DOOR_NAME(shp));
2500
2501 /*
2502 * Release resources and wait for pending event delivery to
2503 * complete.
2504 */
2505 (void) mutex_lock(&sub_info->sp_qlock);
2506 SH_BOUND(shp) = 0;
2507 /* Signal event handler and drain the subscriber's event queue */
2508 (void) cond_signal(&sub_info->sp_cv);
2509 (void) mutex_unlock(&sub_info->sp_qlock);
2510 if (sub_info->sp_handler_tid != 0)
2511 (void) thr_join(sub_info->sp_handler_tid, NULL, NULL);
2512
2513 (void) cond_destroy(&sub_info->sp_cv);
2514 (void) mutex_destroy(&sub_info->sp_qlock);
2515 free(sub_info->sp_door_name);
2516 free(sub_info);
2517 free(SH_DOOR_NAME(shp));
2518 (void) mutex_unlock(SH_LOCK(shp));
2519 }
2520
2521 /*
2522 * sysevent_unbind_publisher: Unbind publisher from the sysevent channel.
2523 */
2524 void
sysevent_unbind_publisher(sysevent_handle_t * shp)2525 sysevent_unbind_publisher(sysevent_handle_t *shp)
2526 {
2527 if (shp == NULL)
2528 return;
2529
2530 (void) mutex_lock(SH_LOCK(shp));
2531 if (SH_BOUND(shp) == 0) {
2532 (void) mutex_unlock(SH_LOCK(shp));
2533 return;
2534 }
2535
2536 /* Close down the registration facilities */
2537 (void) door_revoke(SH_DOOR_DESC(shp));
2538 (void) fdetach(SH_DOOR_NAME(shp));
2539
2540 /* Update the in-kernel registration */
2541 (void) update_kernel_registration(shp, PUBLISHER,
2542 SE_UNBIND_REGISTRATION, &SH_ID(shp), 0, NULL);
2543 SH_BOUND(shp) = 0;
2544
2545 /* Free resources associated with bind */
2546 free_cached_registration(shp);
2547 dealloc_subscribers(shp);
2548
2549 free(SH_PRIV_DATA(shp));
2550 free(SH_DOOR_NAME(shp));
2551 SH_ID(shp) = 0;
2552 (void) mutex_unlock(SH_LOCK(shp));
2553 }
2554
2555 /*
2556 * Evolving APIs to subscribe to syseventd(8) system events.
2557 */
2558
2559 static sysevent_handle_t *
sysevent_bind_handle_cmn(void (* event_handler)(sysevent_t * ev),sysevent_subattr_t * subattr)2560 sysevent_bind_handle_cmn(void (*event_handler)(sysevent_t *ev),
2561 sysevent_subattr_t *subattr)
2562 {
2563 sysevent_handle_t *shp;
2564
2565 if (getuid() != 0) {
2566 errno = EACCES;
2567 return (NULL);
2568 }
2569
2570 if (event_handler == NULL) {
2571 errno = EINVAL;
2572 return (NULL);
2573 }
2574
2575 if ((shp = sysevent_open_channel(SYSEVENTD_CHAN)) == NULL) {
2576 return (NULL);
2577 }
2578
2579 if (sysevent_bind_xsubscriber(shp, event_handler, subattr) != 0) {
2580 /*
2581 * Ask syseventd to clean-up any stale subcribers and try to
2582 * to bind again
2583 */
2584 if (errno == EBUSY) {
2585 int pub_fd;
2586 char door_name[MAXPATHLEN];
2587 uint32_t result;
2588 struct reg_args rargs;
2589
2590 if (snprintf(door_name, MAXPATHLEN, "%s/%s",
2591 SH_CHANNEL_PATH(shp), REG_DOOR) >= MAXPATHLEN) {
2592 sysevent_close_channel(shp);
2593 errno = EINVAL;
2594 return (NULL);
2595 }
2596
2597 rargs.ra_op = SE_CLEANUP;
2598 pub_fd = open(door_name, O_RDONLY);
2599 (void) clnt_deliver_event(pub_fd, (void *)&rargs,
2600 sizeof (struct reg_args), &result, sizeof (result));
2601 (void) close(pub_fd);
2602
2603 /* Try to bind again */
2604 if (sysevent_bind_xsubscriber(shp, event_handler,
2605 subattr) != 0) {
2606 sysevent_close_channel(shp);
2607 return (NULL);
2608 }
2609 } else {
2610 sysevent_close_channel(shp);
2611 return (NULL);
2612 }
2613 }
2614
2615 return (shp);
2616 }
2617
2618 /*
2619 * sysevent_bind_handle - Bind application event handler for syseventd
2620 * subscription.
2621 */
2622 sysevent_handle_t *
sysevent_bind_handle(void (* event_handler)(sysevent_t * ev))2623 sysevent_bind_handle(void (*event_handler)(sysevent_t *ev))
2624 {
2625 return (sysevent_bind_handle_cmn(event_handler, NULL));
2626 }
2627
2628 /*
2629 * sysevent_bind_xhandle - Bind application event handler for syseventd
2630 * subscription, using door_xcreate and attributes as specified.
2631 */
2632 sysevent_handle_t *
sysevent_bind_xhandle(void (* event_handler)(sysevent_t * ev),sysevent_subattr_t * subattr)2633 sysevent_bind_xhandle(void (*event_handler)(sysevent_t *ev),
2634 sysevent_subattr_t *subattr)
2635 {
2636 return (sysevent_bind_handle_cmn(event_handler, subattr));
2637 }
2638
2639 /*
2640 * sysevent_unbind_handle - Unbind caller from syseventd subscriptions
2641 */
2642 void
sysevent_unbind_handle(sysevent_handle_t * shp)2643 sysevent_unbind_handle(sysevent_handle_t *shp)
2644 {
2645 sysevent_unbind_subscriber(shp);
2646 sysevent_close_channel(shp);
2647 }
2648
2649 /*
2650 * sysevent_subscribe_event - Subscribe to system event notification from
2651 * syseventd(8) for the class and subclasses specified.
2652 */
2653 int
sysevent_subscribe_event(sysevent_handle_t * shp,const char * event_class,const char ** event_subclass_list,int num_subclasses)2654 sysevent_subscribe_event(sysevent_handle_t *shp, const char *event_class,
2655 const char **event_subclass_list, int num_subclasses)
2656 {
2657 return (sysevent_register_event(shp, event_class,
2658 event_subclass_list, num_subclasses));
2659 }
2660
2661 void
sysevent_unsubscribe_event(sysevent_handle_t * shp,const char * event_class)2662 sysevent_unsubscribe_event(sysevent_handle_t *shp, const char *event_class)
2663 {
2664 sysevent_unregister_event(shp, event_class);
2665 }
2666