xref: /titanic_41/usr/src/lib/libsysevent/libevchannel.c (revision 6a634c9dca3093f3922e4b7ab826d7bdf17bf78e)
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  * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
23  */
24 
25 #include <stdio.h>
26 #include <ctype.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 <strings.h>
34 #include <pthread.h>
35 #include <atomic.h>
36 #include <signal.h>
37 #include <sys/types.h>
38 #include <sys/varargs.h>
39 #include <sys/sysevent.h>
40 #include <sys/sysevent_impl.h>
41 
42 #include "libsysevent.h"
43 #include "libsysevent_impl.h"
44 
45 /*
46  * The functions below deal with the General Purpose Event Handling framework
47  *
48  * sysevent_evc_bind	    - create/bind application to named channel
49  * sysevent_evc_unbind	    - unbind from previously bound/created channel
50  * sysevent_evc_subscribe   - subscribe to existing event channel
51  * sysevent_evc_unsubscribe - unsubscribe from existing event channel
52  * sysevent_evc_publish	    - generate a system event via an event channel
53  * sysevent_evc_control	    - various channel based control operation
54  */
55 
56 static void kill_door_servers(evchan_subscr_t *);
57 
58 #define	misaligned(p)	((uintptr_t)(p) & 3)	/* 4-byte alignment required */
59 
60 static pthread_key_t nrkey = PTHREAD_ONCE_KEY_NP;
61 
62 /*
63  * If the current thread is a door server thread servicing a door created
64  * for us in sysevent_evc_xsubscribe, then an attempt to unsubscribe from
65  * within door invocation context on the same channel will deadlock in the
66  * kernel waiting for our own invocation to complete.  Such calls are
67  * forbidden, and we abort if they are encountered (better than hanging
68  * unkillably).
69  *
70  * We'd like to offer this detection to subscriptions established with
71  * sysevent_evc_subscribe, but we don't have control over the door service
72  * threads in that case.  Perhaps the fix is to always use door_xcreate
73  * even for sysevent_evc_subscribe?
74  */
75 static boolean_t
will_deadlock(evchan_t * scp)76 will_deadlock(evchan_t *scp)
77 {
78 	evchan_subscr_t *subp = pthread_getspecific(nrkey);
79 	evchan_impl_hdl_t *hdl = EVCHAN_IMPL_HNDL(scp);
80 
81 	return (subp != NULL && subp->ev_subhead == hdl ? B_TRUE : B_FALSE);
82 }
83 
84 /*
85  * Check syntax of a channel name
86  */
87 static int
sysevent_is_chan_name(const char * str)88 sysevent_is_chan_name(const char *str)
89 {
90 	for (; *str != '\0'; str++) {
91 		if (!EVCH_ISCHANCHAR(*str))
92 			return (0);
93 	}
94 
95 	return (1);
96 }
97 
98 /*
99  * Check for printable characters
100  */
101 static int
strisprint(const char * s)102 strisprint(const char *s)
103 {
104 	for (; *s != '\0'; s++) {
105 		if (*s < ' ' || *s > '~')
106 			return (0);
107 	}
108 
109 	return (1);
110 }
111 
112 /*
113  * sysevent_evc_bind - Create/bind application to named channel
114  */
115 int
sysevent_evc_bind(const char * channel,evchan_t ** scpp,uint32_t flags)116 sysevent_evc_bind(const char *channel, evchan_t **scpp, uint32_t flags)
117 {
118 	int chanlen;
119 	evchan_t *scp;
120 	sev_bind_args_t uargs;
121 	int ec;
122 
123 	if (scpp == NULL || misaligned(scpp)) {
124 		return (errno = EINVAL);
125 	}
126 
127 	/* Provide useful value in error case */
128 	*scpp = NULL;
129 
130 	if (channel == NULL ||
131 	    (chanlen = strlen(channel) + 1) > MAX_CHNAME_LEN) {
132 		return (errno = EINVAL);
133 	}
134 
135 	/* Check channel syntax */
136 	if (!sysevent_is_chan_name(channel)) {
137 		return (errno = EINVAL);
138 	}
139 
140 	if (flags & ~EVCH_B_FLAGS) {
141 		return (errno = EINVAL);
142 	}
143 
144 	scp = calloc(1, sizeof (evchan_impl_hdl_t));
145 	if (scp == NULL) {
146 		return (errno = ENOMEM);
147 	}
148 
149 	/*
150 	 * Enable sysevent driver.  Fallback if the device link doesn't exist;
151 	 * this situation can arise if a channel is bound early in system
152 	 * startup, prior to devfsadm(1M) being invoked.
153 	 */
154 	EV_FD(scp) = open(DEVSYSEVENT, O_RDWR);
155 	if (EV_FD(scp) == -1) {
156 		if (errno != ENOENT) {
157 			ec = errno == EACCES ? EPERM : errno;
158 			free(scp);
159 			return (errno = ec);
160 		}
161 
162 		EV_FD(scp) = open(DEVICESYSEVENT, O_RDWR);
163 		if (EV_FD(scp) == -1) {
164 			ec = errno == EACCES ? EPERM : errno;
165 			free(scp);
166 			return (errno = ec);
167 		}
168 	}
169 
170 	/*
171 	 * Force to close the fd's when process is doing exec.
172 	 * The driver will then release stale binding handles.
173 	 * The driver will release also the associated subscriptions
174 	 * if EVCH_SUB_KEEP flag was not set.
175 	 */
176 	(void) fcntl(EV_FD(scp), F_SETFD, FD_CLOEXEC);
177 
178 	uargs.chan_name.name = (uintptr_t)channel;
179 	uargs.chan_name.len = chanlen;
180 	uargs.flags = flags;
181 
182 	if (ioctl(EV_FD(scp), SEV_CHAN_OPEN, &uargs) != 0) {
183 		ec = errno;
184 		(void) close(EV_FD(scp));
185 		free(scp);
186 		return (errno = ec);
187 	}
188 
189 	/* Needed to detect a fork() */
190 	EV_PID(scp) = getpid();
191 	(void) mutex_init(EV_LOCK(scp), USYNC_THREAD, NULL);
192 
193 	*scpp = scp;
194 
195 	return (0);
196 }
197 
198 /*
199  * sysevent_evc_unbind - Unbind from previously bound/created channel
200  */
201 int
sysevent_evc_unbind(evchan_t * scp)202 sysevent_evc_unbind(evchan_t *scp)
203 {
204 	sev_unsubscribe_args_t uargs;
205 	evchan_subscr_t *subp;
206 	int errcp;
207 
208 	if (scp == NULL || misaligned(scp))
209 		return (errno = EINVAL);
210 
211 	if (will_deadlock(scp))
212 		return (errno = EDEADLK);
213 
214 	(void) mutex_lock(EV_LOCK(scp));
215 
216 	/*
217 	 * Unsubscribe, if we are in the process which did the bind.
218 	 */
219 	if (EV_PID(scp) == getpid()) {
220 		uargs.sid.name = NULL;
221 		uargs.sid.len = 0;
222 		/*
223 		 * The unsubscribe ioctl will block until all door upcalls have
224 		 * drained.
225 		 */
226 		if (ioctl(EV_FD(scp), SEV_UNSUBSCRIBE, (intptr_t)&uargs) != 0) {
227 			errcp = errno;
228 			(void) mutex_unlock(EV_LOCK(scp));
229 			return (errno = errcp);
230 		}
231 	}
232 
233 	while ((subp =  EV_SUB_NEXT(scp)) != NULL) {
234 		EV_SUB_NEXT(scp) = subp->evsub_next;
235 
236 		/* If door_xcreate was applied we can clean up */
237 		if (subp->evsub_attr)
238 			kill_door_servers(subp);
239 
240 		if (door_revoke(subp->evsub_door_desc) != 0 && errno == EPERM)
241 			(void) close(subp->evsub_door_desc);
242 
243 		free(subp->evsub_sid);
244 		free(subp);
245 	}
246 
247 	(void) mutex_unlock(EV_LOCK(scp));
248 
249 	/*
250 	 * The close of the driver will do the unsubscribe if a) it is the last
251 	 * close and b) we are in a child which inherited subscriptions.
252 	 */
253 	(void) close(EV_FD(scp));
254 	(void) mutex_destroy(EV_LOCK(scp));
255 	free(scp);
256 
257 	return (0);
258 }
259 
260 /*
261  * sysevent_evc_publish - Generate a system event via an event channel
262  */
263 int
sysevent_evc_publish(evchan_t * scp,const char * class,const char * subclass,const char * vendor,const char * pub_name,nvlist_t * attr_list,uint32_t flags)264 sysevent_evc_publish(evchan_t *scp, const char *class,
265     const char *subclass, const char *vendor,
266     const char *pub_name, nvlist_t *attr_list,
267     uint32_t flags)
268 {
269 	sysevent_t *ev;
270 	sev_publish_args_t uargs;
271 	int rc;
272 	int ec;
273 
274 	if (scp == NULL || misaligned(scp)) {
275 		return (errno = EINVAL);
276 	}
277 
278 	/* No inheritance of binding handles via fork() */
279 	if (EV_PID(scp) != getpid()) {
280 		return (errno = EINVAL);
281 	}
282 
283 	ev = sysevent_alloc_event((char *)class, (char *)subclass,
284 	    (char *)vendor, (char *)pub_name, attr_list);
285 	if (ev == NULL) {
286 		return (errno);
287 	}
288 
289 	uargs.ev.name = (uintptr_t)ev;
290 	uargs.ev.len = SE_SIZE(ev);
291 	uargs.flags = flags;
292 
293 	(void) mutex_lock(EV_LOCK(scp));
294 
295 	rc = ioctl(EV_FD(scp), SEV_PUBLISH, (intptr_t)&uargs);
296 	ec = errno;
297 
298 	(void) mutex_unlock(EV_LOCK(scp));
299 
300 	sysevent_free(ev);
301 
302 	if (rc != 0) {
303 		return (ec);
304 	}
305 	return (0);
306 }
307 
308 /*
309  * Generic callback which catches events from the kernel and calls
310  * subscribers call back routine.
311  *
312  * Kernel guarantees that door_upcalls are disabled when unsubscription
313  * was issued that's why cookie points always to a valid evchan_subscr_t *.
314  *
315  * Furthermore it's not necessary to lock subp because the sysevent
316  * framework guarantees no unsubscription until door_return.
317  */
318 /*ARGSUSED3*/
319 static void
door_upcall(void * cookie,char * args,size_t alen,door_desc_t * ddp,uint_t ndid)320 door_upcall(void *cookie, char *args, size_t alen,
321     door_desc_t *ddp, uint_t ndid)
322 {
323 	evchan_subscr_t *subp = EVCHAN_SUBSCR(cookie);
324 	int rval = 0;
325 
326 	/*
327 	 * If we've been invoked simply to kill the thread then
328 	 * exit now.
329 	 */
330 	if (subp->evsub_state == EVCHAN_SUB_STATE_CLOSING)
331 		pthread_exit(NULL);
332 
333 	if (args == NULL || alen <= (size_t)0) {
334 		/* Skip callback execution */
335 		rval = EINVAL;
336 	} else {
337 		rval = subp->evsub_func((sysevent_t *)(void *)args,
338 		    subp->evsub_cookie);
339 	}
340 
341 	/*
342 	 * Fill in return values for door_return
343 	 */
344 	alen = sizeof (rval);
345 	bcopy(&rval, args, alen);
346 
347 	(void) door_return(args, alen, NULL, 0);
348 }
349 
350 static pthread_once_t xsub_thrattr_once = PTHREAD_ONCE_INIT;
351 static pthread_attr_t xsub_thrattr;
352 
353 static void
xsub_thrattr_init(void)354 xsub_thrattr_init(void)
355 {
356 	(void) pthread_attr_init(&xsub_thrattr);
357 	(void) pthread_attr_setdetachstate(&xsub_thrattr,
358 	    PTHREAD_CREATE_DETACHED);
359 	(void) pthread_attr_setscope(&xsub_thrattr, PTHREAD_SCOPE_SYSTEM);
360 }
361 
362 /*
363  * Our door server create function is only called during initial
364  * door_xcreate since we specify DOOR_NO_DEPLETION_CB.
365  */
366 int
xsub_door_server_create(door_info_t * dip,void * (* startf)(void *),void * startfarg,void * cookie)367 xsub_door_server_create(door_info_t *dip, void *(*startf)(void *),
368     void *startfarg, void *cookie)
369 {
370 	evchan_subscr_t *subp = EVCHAN_SUBSCR(cookie);
371 	struct sysevent_subattr_impl *xsa = subp->evsub_attr;
372 	pthread_attr_t *thrattr;
373 	sigset_t oset;
374 	int err;
375 
376 	if (subp->evsub_state == EVCHAN_SUB_STATE_CLOSING)
377 		return (0);	/* shouldn't happen, but just in case */
378 
379 	/*
380 	 * If sysevent_evc_xsubscribe was called electing to use a
381 	 * different door server create function then let it take it
382 	 * from here.
383 	 */
384 	if (xsa->xs_thrcreate) {
385 		return (xsa->xs_thrcreate(dip, startf, startfarg,
386 		    xsa->xs_thrcreate_cookie));
387 	}
388 
389 	if (xsa->xs_thrattr == NULL) {
390 		(void) pthread_once(&xsub_thrattr_once, xsub_thrattr_init);
391 		thrattr = &xsub_thrattr;
392 	} else {
393 		thrattr = xsa->xs_thrattr;
394 	}
395 
396 	(void) pthread_sigmask(SIG_SETMASK, &xsa->xs_sigmask, &oset);
397 	err = pthread_create(NULL, thrattr, startf, startfarg);
398 	(void) pthread_sigmask(SIG_SETMASK, &oset, NULL);
399 
400 	return (err == 0 ? 1 : -1);
401 }
402 
403 void
xsub_door_server_setup(void * cookie)404 xsub_door_server_setup(void *cookie)
405 {
406 	evchan_subscr_t *subp = EVCHAN_SUBSCR(cookie);
407 	struct sysevent_subattr_impl *xsa = subp->evsub_attr;
408 
409 	if (xsa->xs_thrsetup == NULL) {
410 		(void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
411 		(void) pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
412 	}
413 
414 	(void) pthread_setspecific(nrkey, (void *)subp);
415 
416 	if (xsa->xs_thrsetup)
417 		xsa->xs_thrsetup(xsa->xs_thrsetup_cookie);
418 }
419 
420 /*
421  * Cause private door server threads to exit.  We have already performed the
422  * unsubscribe ioctl which stops new invocations and waits until all
423  * existing invocations are complete.  So all server threads should be
424  * blocked in door_return.  The door has not yet been revoked.  We will
425  * invoke repeatedly after setting the evsub_state to be noticed on
426  * wakeup; each invocation will result in the death of one server thread.
427  *
428  * You'd think it would be easier to kill these threads, such as through
429  * pthread_cancel.  Unfortunately door_return is not a cancellation point,
430  * and if you do cancel a thread blocked in door_return the EINTR check in
431  * the door_return assembly logic causes us to loop with EINTR forever!
432  */
433 static void
kill_door_servers(evchan_subscr_t * subp)434 kill_door_servers(evchan_subscr_t *subp)
435 {
436 	door_arg_t da;
437 
438 	bzero(&da, sizeof (da));
439 	subp->evsub_state = EVCHAN_SUB_STATE_CLOSING;
440 	membar_producer();
441 
442 	(void) door_call(subp->evsub_door_desc, &da);
443 }
444 
445 static int
sysevent_evc_subscribe_cmn(evchan_t * scp,const char * sid,const char * class,int (* event_handler)(sysevent_t * ev,void * cookie),void * cookie,uint32_t flags,struct sysevent_subattr_impl * xsa)446 sysevent_evc_subscribe_cmn(evchan_t *scp, const char *sid, const char *class,
447     int (*event_handler)(sysevent_t *ev, void *cookie),
448     void *cookie, uint32_t flags, struct sysevent_subattr_impl *xsa)
449 {
450 	evchan_subscr_t *subp;
451 	int upcall_door;
452 	sev_subscribe_args_t uargs;
453 	uint32_t sid_len;
454 	uint32_t class_len;
455 	int ec;
456 
457 	if (scp == NULL || misaligned(scp) || sid == NULL || class == NULL) {
458 		return (errno = EINVAL);
459 	}
460 
461 	/* No inheritance of binding handles via fork() */
462 	if (EV_PID(scp) != getpid()) {
463 		return (errno = EINVAL);
464 	}
465 
466 	if ((sid_len = strlen(sid) + 1) > MAX_SUBID_LEN || sid_len == 1 ||
467 	    (class_len = strlen(class) + 1) > MAX_CLASS_LEN) {
468 		return (errno = EINVAL);
469 	}
470 
471 	/* Check for printable characters */
472 	if (!strisprint(sid)) {
473 		return (errno = EINVAL);
474 	}
475 
476 	if (event_handler == NULL) {
477 		return (errno = EINVAL);
478 	}
479 
480 	if (pthread_key_create_once_np(&nrkey, NULL) != 0)
481 		return (errno);	/* ENOMEM or EAGAIN */
482 
483 	/* Create subscriber data */
484 	if ((subp = calloc(1, sizeof (evchan_subscr_t))) == NULL) {
485 		return (errno);
486 	}
487 
488 	if ((subp->evsub_sid = strdup(sid)) == NULL) {
489 		ec = errno;
490 		free(subp);
491 		return (ec);
492 	}
493 
494 	/*
495 	 * EC_ALL string will not be copied to kernel - NULL is assumed
496 	 */
497 	if (strcmp(class, EC_ALL) == 0) {
498 		class = NULL;
499 		class_len = 0;
500 	}
501 
502 	/*
503 	 * Fill this in now for the xsub_door_server_setup dance
504 	 */
505 	subp->ev_subhead = EVCHAN_IMPL_HNDL(scp);
506 	subp->evsub_state = EVCHAN_SUB_STATE_ACTIVE;
507 
508 	if (xsa == NULL) {
509 		upcall_door = door_create(door_upcall, (void *)subp,
510 		    DOOR_REFUSE_DESC | DOOR_NO_CANCEL);
511 	} else {
512 		subp->evsub_attr = xsa;
513 
514 		/*
515 		 * Create a private door with exactly one thread to
516 		 * service the callbacks (the GPEC kernel implementation
517 		 * serializes deliveries for each subscriber id).
518 		 */
519 		upcall_door = door_xcreate(door_upcall, (void *)subp,
520 		    DOOR_REFUSE_DESC | DOOR_NO_CANCEL | DOOR_NO_DEPLETION_CB,
521 		    xsub_door_server_create, xsub_door_server_setup,
522 		    (void *)subp, 1);
523 	}
524 
525 	if (upcall_door == -1) {
526 		ec = errno;
527 		free(subp->evsub_sid);
528 		free(subp);
529 		return (ec);
530 	}
531 
532 	/* Complete subscriber information */
533 	subp->evsub_door_desc = upcall_door;
534 	subp->evsub_func = event_handler;
535 	subp->evsub_cookie = cookie;
536 
537 	(void) mutex_lock(EV_LOCK(scp));
538 
539 	uargs.sid.name = (uintptr_t)sid;
540 	uargs.sid.len = sid_len;
541 	uargs.class_info.name = (uintptr_t)class;
542 	uargs.class_info.len = class_len;
543 	uargs.door_desc = subp->evsub_door_desc;
544 	uargs.flags = flags;
545 	if (ioctl(EV_FD(scp), SEV_SUBSCRIBE, (intptr_t)&uargs) != 0) {
546 		ec = errno;
547 		(void) mutex_unlock(EV_LOCK(scp));
548 		if (xsa)
549 			kill_door_servers(subp);
550 		(void) door_revoke(upcall_door);
551 		free(subp->evsub_sid);
552 		free(subp);
553 		return (ec);
554 	}
555 
556 	/* Attach to subscriber list */
557 	subp->evsub_next = EV_SUB_NEXT(scp);
558 	EV_SUB_NEXT(scp) = subp;
559 
560 	(void) mutex_unlock(EV_LOCK(scp));
561 
562 	return (0);
563 }
564 
565 /*
566  * sysevent_evc_subscribe - subscribe to an existing event channel
567  * using a non-private door (which will create as many server threads
568  * as the apparent maximum concurrency requirements suggest).
569  */
570 int
sysevent_evc_subscribe(evchan_t * scp,const char * sid,const char * class,int (* event_handler)(sysevent_t * ev,void * cookie),void * cookie,uint32_t flags)571 sysevent_evc_subscribe(evchan_t *scp, const char *sid, const char *class,
572     int (*event_handler)(sysevent_t *ev, void *cookie),
573     void *cookie, uint32_t flags)
574 {
575 	return (sysevent_evc_subscribe_cmn(scp, sid, class, event_handler,
576 	    cookie, flags, NULL));
577 }
578 
579 static void
subattr_dfltinit(struct sysevent_subattr_impl * xsa)580 subattr_dfltinit(struct sysevent_subattr_impl *xsa)
581 {
582 	(void) sigfillset(&xsa->xs_sigmask);
583 	(void) sigdelset(&xsa->xs_sigmask, SIGABRT);
584 }
585 
586 static struct sysevent_subattr_impl dfltsa;
587 pthread_once_t dfltsa_inited = PTHREAD_ONCE_INIT;
588 
589 static void
init_dfltsa(void)590 init_dfltsa(void)
591 {
592 	subattr_dfltinit(&dfltsa);
593 }
594 
595 /*
596  * sysevent_evc_subscribe - subscribe to an existing event channel
597  * using a private door with control over thread creation.
598  */
599 int
sysevent_evc_xsubscribe(evchan_t * scp,const char * sid,const char * class,int (* event_handler)(sysevent_t * ev,void * cookie),void * cookie,uint32_t flags,sysevent_subattr_t * attr)600 sysevent_evc_xsubscribe(evchan_t *scp, const char *sid, const char *class,
601     int (*event_handler)(sysevent_t *ev, void *cookie),
602     void *cookie, uint32_t flags, sysevent_subattr_t *attr)
603 {
604 	struct sysevent_subattr_impl *xsa;
605 
606 	if (attr != NULL) {
607 		xsa = (struct sysevent_subattr_impl *)attr;
608 	} else {
609 		xsa = &dfltsa;
610 		(void) pthread_once(&dfltsa_inited, init_dfltsa);
611 	}
612 
613 	return (sysevent_evc_subscribe_cmn(scp, sid, class, event_handler,
614 	    cookie, flags, xsa));
615 }
616 
617 sysevent_subattr_t *
sysevent_subattr_alloc(void)618 sysevent_subattr_alloc(void)
619 {
620 	struct sysevent_subattr_impl *xsa = calloc(1, sizeof (*xsa));
621 
622 	if (xsa != NULL)
623 		subattr_dfltinit(xsa);
624 
625 	return (xsa != NULL ? (sysevent_subattr_t *)xsa : NULL);
626 }
627 
628 void
sysevent_subattr_free(sysevent_subattr_t * attr)629 sysevent_subattr_free(sysevent_subattr_t *attr)
630 {
631 	struct sysevent_subattr_impl *xsa =
632 	    (struct sysevent_subattr_impl *)attr;
633 
634 	free(xsa);
635 }
636 
637 void
sysevent_subattr_thrcreate(sysevent_subattr_t * attr,door_xcreate_server_func_t * thrcreate,void * cookie)638 sysevent_subattr_thrcreate(sysevent_subattr_t *attr,
639     door_xcreate_server_func_t *thrcreate, void *cookie)
640 {
641 	struct sysevent_subattr_impl *xsa =
642 	    (struct sysevent_subattr_impl *)attr;
643 
644 	xsa->xs_thrcreate = thrcreate;
645 	xsa->xs_thrcreate_cookie = cookie;
646 }
647 
648 void
sysevent_subattr_thrsetup(sysevent_subattr_t * attr,door_xcreate_thrsetup_func_t * thrsetup,void * cookie)649 sysevent_subattr_thrsetup(sysevent_subattr_t *attr,
650     door_xcreate_thrsetup_func_t *thrsetup, void *cookie)
651 {
652 	struct sysevent_subattr_impl *xsa =
653 	    (struct sysevent_subattr_impl *)attr;
654 
655 	xsa->xs_thrsetup = thrsetup;
656 	xsa->xs_thrsetup_cookie = cookie;
657 }
658 
659 void
sysevent_subattr_sigmask(sysevent_subattr_t * attr,sigset_t * set)660 sysevent_subattr_sigmask(sysevent_subattr_t *attr, sigset_t *set)
661 {
662 	struct sysevent_subattr_impl *xsa =
663 	    (struct sysevent_subattr_impl *)attr;
664 
665 	if (set) {
666 		xsa->xs_sigmask = *set;
667 	} else {
668 		(void) sigfillset(&xsa->xs_sigmask);
669 		(void) sigdelset(&xsa->xs_sigmask, SIGABRT);
670 	}
671 }
672 
673 void
sysevent_subattr_thrattr(sysevent_subattr_t * attr,pthread_attr_t * thrattr)674 sysevent_subattr_thrattr(sysevent_subattr_t *attr, pthread_attr_t *thrattr)
675 {
676 	struct sysevent_subattr_impl *xsa =
677 	    (struct sysevent_subattr_impl *)attr;
678 
679 	xsa->xs_thrattr = thrattr;
680 }
681 
682 /*
683  * sysevent_evc_unsubscribe - Unsubscribe from an existing event channel
684  */
685 int
sysevent_evc_unsubscribe(evchan_t * scp,const char * sid)686 sysevent_evc_unsubscribe(evchan_t *scp, const char *sid)
687 {
688 	int all_subscribers = 0;
689 	sev_unsubscribe_args_t uargs;
690 	evchan_subscr_t *subp, *prevsubp, *tofree;
691 	int errcp;
692 	int rc;
693 
694 	if (scp == NULL || misaligned(scp))
695 		return (errno = EINVAL);
696 
697 	if (sid == NULL || strlen(sid) == 0 ||
698 	    (strlen(sid) >= MAX_SUBID_LEN))
699 		return (errno = EINVAL);
700 
701 	/* No inheritance of binding handles via fork() */
702 	if (EV_PID(scp) != getpid())
703 		return (errno = EINVAL);
704 
705 	if (strcmp(sid, EVCH_ALLSUB) == 0) {
706 		all_subscribers++;
707 		/* Indicates all subscriber id's for this channel */
708 		uargs.sid.name = NULL;
709 		uargs.sid.len = 0;
710 	} else {
711 		uargs.sid.name = (uintptr_t)sid;
712 		uargs.sid.len = strlen(sid) + 1;
713 	}
714 
715 	if (will_deadlock(scp))
716 		return (errno = EDEADLK);
717 
718 	(void) mutex_lock(EV_LOCK(scp));
719 
720 	/*
721 	 * The unsubscribe ioctl will block until all door upcalls have drained.
722 	 */
723 	rc = ioctl(EV_FD(scp), SEV_UNSUBSCRIBE, (intptr_t)&uargs);
724 
725 	if (rc != 0) {
726 		errcp = errno;
727 		(void) mutex_unlock(EV_LOCK(scp));
728 		return (errno = errcp); /* EFAULT, ENXIO, EINVAL possible */
729 	}
730 
731 
732 	/*
733 	 * Search for the matching subscriber.  If EVCH_ALLSUB was specified
734 	 * then the ioctl above will have returned 0 even if there are
735 	 * no subscriptions, so the initial EV_SUB_NEXT can be NULL.
736 	 */
737 	prevsubp = NULL;
738 	subp =  EV_SUB_NEXT(scp);
739 	while (subp != NULL) {
740 		if (all_subscribers || strcmp(subp->evsub_sid, sid) == 0) {
741 			if (prevsubp == NULL) {
742 				EV_SUB_NEXT(scp) = subp->evsub_next;
743 			} else {
744 				prevsubp->evsub_next = subp->evsub_next;
745 			}
746 
747 			tofree = subp;
748 			subp = subp->evsub_next;
749 
750 			/* If door_xcreate was applied we can clean up */
751 			if (tofree->evsub_attr)
752 				kill_door_servers(tofree);
753 
754 			(void) door_revoke(tofree->evsub_door_desc);
755 			free(tofree->evsub_sid);
756 			free(tofree);
757 
758 			/* Freed single subscriber already? */
759 			if (all_subscribers == 0)
760 				break;
761 		} else {
762 			prevsubp = subp;
763 			subp = subp->evsub_next;
764 		}
765 	}
766 
767 	(void) mutex_unlock(EV_LOCK(scp));
768 
769 	return (0);
770 }
771 
772 /*
773  * sysevent_evc_control - Various channel based control operation
774  */
775 int
sysevent_evc_control(evchan_t * scp,int cmd,...)776 sysevent_evc_control(evchan_t *scp, int cmd, /* arg */ ...)
777 {
778 	va_list ap;
779 	uint32_t *chlenp;
780 	sev_control_args_t uargs;
781 	int rc = 0;
782 
783 	if (scp == NULL || misaligned(scp)) {
784 		return (errno = EINVAL);
785 	}
786 
787 	/* No inheritance of binding handles via fork() */
788 	if (EV_PID(scp) != getpid()) {
789 		return (errno = EINVAL);
790 	}
791 
792 	va_start(ap, cmd);
793 
794 	uargs.cmd = cmd;
795 
796 	(void) mutex_lock(EV_LOCK(scp));
797 
798 	switch (cmd) {
799 	case EVCH_GET_CHAN_LEN:
800 	case EVCH_GET_CHAN_LEN_MAX:
801 		chlenp = va_arg(ap, uint32_t *);
802 		if (chlenp == NULL || misaligned(chlenp)) {
803 			rc = EINVAL;
804 			break;
805 		}
806 		rc = ioctl(EV_FD(scp), SEV_CHAN_CONTROL, (intptr_t)&uargs);
807 		*chlenp = uargs.value;
808 		break;
809 
810 	case EVCH_SET_CHAN_LEN:
811 		/* Range change will be handled in framework */
812 		uargs.value = va_arg(ap, uint32_t);
813 		rc = ioctl(EV_FD(scp), SEV_CHAN_CONTROL, (intptr_t)&uargs);
814 		break;
815 
816 	default:
817 		rc = EINVAL;
818 	}
819 
820 	(void) mutex_unlock(EV_LOCK(scp));
821 
822 	if (rc == -1) {
823 		rc = errno;
824 	}
825 
826 	va_end(ap);
827 
828 	return (errno = rc);
829 }
830 
831 int
sysevent_evc_setpropnvl(evchan_t * scp,nvlist_t * nvl)832 sysevent_evc_setpropnvl(evchan_t *scp, nvlist_t *nvl)
833 {
834 	sev_propnvl_args_t uargs;
835 	char *buf = NULL;
836 	size_t nvlsz = 0;
837 	int rc;
838 
839 	if (scp == NULL || misaligned(scp))
840 		return (errno = EINVAL);
841 
842 	if (nvl != NULL &&
843 	    nvlist_pack(nvl, &buf, &nvlsz, NV_ENCODE_NATIVE, 0) != 0)
844 		return (errno);
845 
846 	uargs.packednvl.name = (uint64_t)(uintptr_t)buf;
847 	uargs.packednvl.len = (uint32_t)nvlsz;
848 
849 	rc = ioctl(EV_FD(scp), SEV_SETPROPNVL, (intptr_t)&uargs);
850 
851 	if (buf)
852 		free(buf);
853 
854 	return (rc);
855 }
856 
857 int
sysevent_evc_getpropnvl(evchan_t * scp,nvlist_t ** nvlp)858 sysevent_evc_getpropnvl(evchan_t *scp, nvlist_t **nvlp)
859 {
860 	sev_propnvl_args_t uargs;
861 	char buf[1024], *bufp = buf;	/* stack buffer */
862 	size_t sz = sizeof (buf);
863 	char *buf2 = NULL;		/* allocated if stack buf too small */
864 	int64_t expgen = -1;
865 	int rc;
866 
867 	if (scp == NULL || misaligned(scp) || nvlp == NULL)
868 		return (errno = EINVAL);
869 
870 	*nvlp = NULL;
871 
872 again:
873 	uargs.packednvl.name = (uint64_t)(uintptr_t)bufp;
874 	uargs.packednvl.len = (uint32_t)sz;
875 
876 	rc = ioctl(EV_FD(scp), SEV_GETPROPNVL, (intptr_t)&uargs);
877 
878 	if (rc == E2BIG)
879 		return (errno = E2BIG);	/* driver refuses to copyout */
880 
881 	/*
882 	 * If the packed nvlist is too big for the buffer size we offered
883 	 * then the ioctl returns EOVERFLOW and indicates in the 'len'
884 	 * the size required for the current property nvlist generation
885 	 * (itself returned in the generation member).
886 	 */
887 	if (rc == EOVERFLOW &&
888 	    (buf2 == NULL || uargs.generation != expgen)) {
889 		if (buf2 != NULL)
890 			free(buf2);
891 
892 		if ((sz = uargs.packednvl.len) > 1024 * 1024)
893 			return (E2BIG);
894 
895 		bufp = buf2 = malloc(sz);
896 
897 		if (buf2 == NULL)
898 			return (errno = ENOMEM);
899 
900 		expgen = uargs.generation;
901 		goto again;
902 	}
903 
904 	/*
905 	 * The chan prop nvlist can be absent, in which case the ioctl
906 	 * returns success and uargs.packednvl.len of 0;  we have already
907 	 * set *nvlp to NULL.  Otherwise we must unpack the nvl.
908 	 */
909 	if (rc == 0 && uargs.packednvl.len != 0 &&
910 	    nvlist_unpack(bufp, uargs.packednvl.len, nvlp, 0) != 0)
911 		rc = EINVAL;
912 
913 	if (buf2 != NULL)
914 		free(buf2);
915 
916 	return (rc ? errno = rc : 0);
917 }
918