xref: /titanic_44/usr/src/lib/libsysevent/libevchannel.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
1*7c478bd9Sstevel@tonic-gate /*
2*7c478bd9Sstevel@tonic-gate  * CDDL HEADER START
3*7c478bd9Sstevel@tonic-gate  *
4*7c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*7c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*7c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*7c478bd9Sstevel@tonic-gate  * with the License.
8*7c478bd9Sstevel@tonic-gate  *
9*7c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*7c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*7c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*7c478bd9Sstevel@tonic-gate  * and limitations under the License.
13*7c478bd9Sstevel@tonic-gate  *
14*7c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*7c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*7c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*7c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*7c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*7c478bd9Sstevel@tonic-gate  *
20*7c478bd9Sstevel@tonic-gate  * CDDL HEADER END
21*7c478bd9Sstevel@tonic-gate  */
22*7c478bd9Sstevel@tonic-gate /*
23*7c478bd9Sstevel@tonic-gate  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24*7c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
25*7c478bd9Sstevel@tonic-gate  */
26*7c478bd9Sstevel@tonic-gate 
27*7c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*7c478bd9Sstevel@tonic-gate 
29*7c478bd9Sstevel@tonic-gate #include <stdio.h>
30*7c478bd9Sstevel@tonic-gate #include <ctype.h>
31*7c478bd9Sstevel@tonic-gate #include <fcntl.h>
32*7c478bd9Sstevel@tonic-gate #include <errno.h>
33*7c478bd9Sstevel@tonic-gate #include <door.h>
34*7c478bd9Sstevel@tonic-gate #include <unistd.h>
35*7c478bd9Sstevel@tonic-gate #include <stddef.h>
36*7c478bd9Sstevel@tonic-gate #include <stdlib.h>
37*7c478bd9Sstevel@tonic-gate #include <strings.h>
38*7c478bd9Sstevel@tonic-gate #include <sys/types.h>
39*7c478bd9Sstevel@tonic-gate #include <sys/varargs.h>
40*7c478bd9Sstevel@tonic-gate #include <sys/sysevent.h>
41*7c478bd9Sstevel@tonic-gate #include <sys/sysevent_impl.h>
42*7c478bd9Sstevel@tonic-gate 
43*7c478bd9Sstevel@tonic-gate #include "libsysevent.h"
44*7c478bd9Sstevel@tonic-gate #include "libsysevent_impl.h"
45*7c478bd9Sstevel@tonic-gate 
46*7c478bd9Sstevel@tonic-gate /*
47*7c478bd9Sstevel@tonic-gate  * The functions below deal with the General Purpose Event Handling framework
48*7c478bd9Sstevel@tonic-gate  *
49*7c478bd9Sstevel@tonic-gate  * sysevent_evc_bind	    - create/bind application to named channel
50*7c478bd9Sstevel@tonic-gate  * sysevent_evc_unbind	    - unbind from previously bound/created channel
51*7c478bd9Sstevel@tonic-gate  * sysevent_evc_subscribe   - subscribe to existing event channel
52*7c478bd9Sstevel@tonic-gate  * sysevent_evc_unsubscribe - unsubscribe from existing event channel
53*7c478bd9Sstevel@tonic-gate  * sysevent_evc_publish	    - generate a system event via an event channel
54*7c478bd9Sstevel@tonic-gate  * sysevent_evc_control	    - various channel based control operation
55*7c478bd9Sstevel@tonic-gate  */
56*7c478bd9Sstevel@tonic-gate 
57*7c478bd9Sstevel@tonic-gate #define	misaligned(p)	((uintptr_t)(p) & 3)	/* 4-byte alignment required */
58*7c478bd9Sstevel@tonic-gate 
59*7c478bd9Sstevel@tonic-gate /*
60*7c478bd9Sstevel@tonic-gate  * Check syntax of a channel name
61*7c478bd9Sstevel@tonic-gate  */
62*7c478bd9Sstevel@tonic-gate static int
63*7c478bd9Sstevel@tonic-gate sysevent_is_chan_name(const char *str)
64*7c478bd9Sstevel@tonic-gate {
65*7c478bd9Sstevel@tonic-gate 	for (; *str != '\0'; str++) {
66*7c478bd9Sstevel@tonic-gate 		if (!EVCH_ISCHANCHAR(*str))
67*7c478bd9Sstevel@tonic-gate 			return (0);
68*7c478bd9Sstevel@tonic-gate 	}
69*7c478bd9Sstevel@tonic-gate 
70*7c478bd9Sstevel@tonic-gate 	return (1);
71*7c478bd9Sstevel@tonic-gate }
72*7c478bd9Sstevel@tonic-gate 
73*7c478bd9Sstevel@tonic-gate /*
74*7c478bd9Sstevel@tonic-gate  * Check for printable characters
75*7c478bd9Sstevel@tonic-gate  */
76*7c478bd9Sstevel@tonic-gate static int
77*7c478bd9Sstevel@tonic-gate strisprint(const char *s)
78*7c478bd9Sstevel@tonic-gate {
79*7c478bd9Sstevel@tonic-gate 	for (; *s != '\0'; s++) {
80*7c478bd9Sstevel@tonic-gate 		if (*s < ' ' || *s > '~')
81*7c478bd9Sstevel@tonic-gate 			return (0);
82*7c478bd9Sstevel@tonic-gate 	}
83*7c478bd9Sstevel@tonic-gate 
84*7c478bd9Sstevel@tonic-gate 	return (1);
85*7c478bd9Sstevel@tonic-gate }
86*7c478bd9Sstevel@tonic-gate 
87*7c478bd9Sstevel@tonic-gate /*
88*7c478bd9Sstevel@tonic-gate  * sysevent_evc_bind - Create/bind application to named channel
89*7c478bd9Sstevel@tonic-gate  */
90*7c478bd9Sstevel@tonic-gate int
91*7c478bd9Sstevel@tonic-gate sysevent_evc_bind(const char *channel, evchan_t **scpp, uint32_t flags)
92*7c478bd9Sstevel@tonic-gate {
93*7c478bd9Sstevel@tonic-gate 	int chanlen;
94*7c478bd9Sstevel@tonic-gate 	evchan_t *scp;
95*7c478bd9Sstevel@tonic-gate 	sev_bind_args_t uargs;
96*7c478bd9Sstevel@tonic-gate 	int ec;
97*7c478bd9Sstevel@tonic-gate 
98*7c478bd9Sstevel@tonic-gate 	if (scpp == NULL || misaligned(scpp)) {
99*7c478bd9Sstevel@tonic-gate 		return (errno = EINVAL);
100*7c478bd9Sstevel@tonic-gate 	}
101*7c478bd9Sstevel@tonic-gate 
102*7c478bd9Sstevel@tonic-gate 	/* Provide useful value in error case */
103*7c478bd9Sstevel@tonic-gate 	*scpp = NULL;
104*7c478bd9Sstevel@tonic-gate 
105*7c478bd9Sstevel@tonic-gate 	if (channel == NULL ||
106*7c478bd9Sstevel@tonic-gate 	    (chanlen = strlen(channel) + 1) > MAX_CHNAME_LEN) {
107*7c478bd9Sstevel@tonic-gate 		return (errno = EINVAL);
108*7c478bd9Sstevel@tonic-gate 	}
109*7c478bd9Sstevel@tonic-gate 
110*7c478bd9Sstevel@tonic-gate 	/* Check channel syntax */
111*7c478bd9Sstevel@tonic-gate 	if (!sysevent_is_chan_name(channel)) {
112*7c478bd9Sstevel@tonic-gate 		return (errno = EINVAL);
113*7c478bd9Sstevel@tonic-gate 	}
114*7c478bd9Sstevel@tonic-gate 
115*7c478bd9Sstevel@tonic-gate 	if (flags & ~EVCH_B_FLAGS) {
116*7c478bd9Sstevel@tonic-gate 		return (errno = EINVAL);
117*7c478bd9Sstevel@tonic-gate 	}
118*7c478bd9Sstevel@tonic-gate 
119*7c478bd9Sstevel@tonic-gate 	scp = calloc(1, sizeof (evchan_impl_hdl_t));
120*7c478bd9Sstevel@tonic-gate 	if (scp == NULL) {
121*7c478bd9Sstevel@tonic-gate 		return (errno = ENOMEM);
122*7c478bd9Sstevel@tonic-gate 	}
123*7c478bd9Sstevel@tonic-gate 
124*7c478bd9Sstevel@tonic-gate 	/*
125*7c478bd9Sstevel@tonic-gate 	 * Enable sysevent driver.  Fallback if the device link doesn't exist;
126*7c478bd9Sstevel@tonic-gate 	 * this situation can arise if a channel is bound early in system
127*7c478bd9Sstevel@tonic-gate 	 * startup, prior to devfsadm(1M) being invoked.
128*7c478bd9Sstevel@tonic-gate 	 */
129*7c478bd9Sstevel@tonic-gate 	EV_FD(scp) = open(DEVSYSEVENT, O_RDWR);
130*7c478bd9Sstevel@tonic-gate 	if (EV_FD(scp) == -1) {
131*7c478bd9Sstevel@tonic-gate 		if (errno != ENOENT) {
132*7c478bd9Sstevel@tonic-gate 			ec = errno == EACCES ? EPERM : errno;
133*7c478bd9Sstevel@tonic-gate 			free(scp);
134*7c478bd9Sstevel@tonic-gate 			return (errno = ec);
135*7c478bd9Sstevel@tonic-gate 		}
136*7c478bd9Sstevel@tonic-gate 
137*7c478bd9Sstevel@tonic-gate 		EV_FD(scp) = open(DEVICESYSEVENT, O_RDWR);
138*7c478bd9Sstevel@tonic-gate 		if (EV_FD(scp) == -1) {
139*7c478bd9Sstevel@tonic-gate 			ec = errno == EACCES ? EPERM : errno;
140*7c478bd9Sstevel@tonic-gate 			free(scp);
141*7c478bd9Sstevel@tonic-gate 			return (errno = ec);
142*7c478bd9Sstevel@tonic-gate 		}
143*7c478bd9Sstevel@tonic-gate 	}
144*7c478bd9Sstevel@tonic-gate 
145*7c478bd9Sstevel@tonic-gate 	/*
146*7c478bd9Sstevel@tonic-gate 	 * Force to close the fd's when process is doing exec.
147*7c478bd9Sstevel@tonic-gate 	 * The driver will then release stale binding handles.
148*7c478bd9Sstevel@tonic-gate 	 * The driver will release also the associated subscriptions
149*7c478bd9Sstevel@tonic-gate 	 * if EVCH_SUB_KEEP flag was not set.
150*7c478bd9Sstevel@tonic-gate 	 */
151*7c478bd9Sstevel@tonic-gate 	(void) fcntl(EV_FD(scp), F_SETFD, FD_CLOEXEC);
152*7c478bd9Sstevel@tonic-gate 
153*7c478bd9Sstevel@tonic-gate 	uargs.chan_name.name = (uintptr_t)channel;
154*7c478bd9Sstevel@tonic-gate 	uargs.chan_name.len = chanlen;
155*7c478bd9Sstevel@tonic-gate 	uargs.flags = flags;
156*7c478bd9Sstevel@tonic-gate 
157*7c478bd9Sstevel@tonic-gate 	if (ioctl(EV_FD(scp), SEV_CHAN_OPEN, &uargs) != 0) {
158*7c478bd9Sstevel@tonic-gate 		ec = errno;
159*7c478bd9Sstevel@tonic-gate 		(void) close(EV_FD(scp));
160*7c478bd9Sstevel@tonic-gate 		free(scp);
161*7c478bd9Sstevel@tonic-gate 		return (errno = ec);
162*7c478bd9Sstevel@tonic-gate 	}
163*7c478bd9Sstevel@tonic-gate 
164*7c478bd9Sstevel@tonic-gate 	/* Needed to detect a fork() */
165*7c478bd9Sstevel@tonic-gate 	EV_PID(scp) = getpid();
166*7c478bd9Sstevel@tonic-gate 	(void) mutex_init(EV_LOCK(scp), USYNC_THREAD, NULL);
167*7c478bd9Sstevel@tonic-gate 
168*7c478bd9Sstevel@tonic-gate 	*scpp = scp;
169*7c478bd9Sstevel@tonic-gate 
170*7c478bd9Sstevel@tonic-gate 	return (0);
171*7c478bd9Sstevel@tonic-gate }
172*7c478bd9Sstevel@tonic-gate 
173*7c478bd9Sstevel@tonic-gate /*
174*7c478bd9Sstevel@tonic-gate  * sysevent_evc_unbind - Unbind from previously bound/created channel
175*7c478bd9Sstevel@tonic-gate  */
176*7c478bd9Sstevel@tonic-gate void
177*7c478bd9Sstevel@tonic-gate sysevent_evc_unbind(evchan_t *scp)
178*7c478bd9Sstevel@tonic-gate {
179*7c478bd9Sstevel@tonic-gate 	sev_unsubscribe_args_t uargs;
180*7c478bd9Sstevel@tonic-gate 	evchan_subscr_t *subp, *tofree;
181*7c478bd9Sstevel@tonic-gate 
182*7c478bd9Sstevel@tonic-gate 	if (scp == NULL || misaligned(scp))
183*7c478bd9Sstevel@tonic-gate 		return;
184*7c478bd9Sstevel@tonic-gate 
185*7c478bd9Sstevel@tonic-gate 	(void) mutex_lock(EV_LOCK(scp));
186*7c478bd9Sstevel@tonic-gate 
187*7c478bd9Sstevel@tonic-gate 	/*
188*7c478bd9Sstevel@tonic-gate 	 * Unsubscribe, if we are in the process which did the bind.
189*7c478bd9Sstevel@tonic-gate 	 */
190*7c478bd9Sstevel@tonic-gate 	if (EV_PID(scp) == getpid()) {
191*7c478bd9Sstevel@tonic-gate 		uargs.sid.name = NULL;
192*7c478bd9Sstevel@tonic-gate 		uargs.sid.len = 0;
193*7c478bd9Sstevel@tonic-gate 		/*
194*7c478bd9Sstevel@tonic-gate 		 * The unsubscribe ioctl will block until all door upcalls have
195*7c478bd9Sstevel@tonic-gate 		 * drained.
196*7c478bd9Sstevel@tonic-gate 		 */
197*7c478bd9Sstevel@tonic-gate 		if (ioctl(EV_FD(scp), SEV_UNSUBSCRIBE, (intptr_t)&uargs) != 0) {
198*7c478bd9Sstevel@tonic-gate 			(void) mutex_unlock(EV_LOCK(scp));
199*7c478bd9Sstevel@tonic-gate 			return;
200*7c478bd9Sstevel@tonic-gate 		}
201*7c478bd9Sstevel@tonic-gate 	}
202*7c478bd9Sstevel@tonic-gate 
203*7c478bd9Sstevel@tonic-gate 	subp =  (evchan_subscr_t *)(void*)EV_SUB(scp);
204*7c478bd9Sstevel@tonic-gate 	while (subp->evsub_next != NULL) {
205*7c478bd9Sstevel@tonic-gate 		tofree = subp->evsub_next;
206*7c478bd9Sstevel@tonic-gate 		subp->evsub_next = tofree->evsub_next;
207*7c478bd9Sstevel@tonic-gate 		if (door_revoke(tofree->evsub_door_desc) != 0 && errno == EPERM)
208*7c478bd9Sstevel@tonic-gate 			(void) close(tofree->evsub_door_desc);
209*7c478bd9Sstevel@tonic-gate 		free(tofree->evsub_sid);
210*7c478bd9Sstevel@tonic-gate 		free(tofree);
211*7c478bd9Sstevel@tonic-gate 	}
212*7c478bd9Sstevel@tonic-gate 
213*7c478bd9Sstevel@tonic-gate 	(void) mutex_unlock(EV_LOCK(scp));
214*7c478bd9Sstevel@tonic-gate 
215*7c478bd9Sstevel@tonic-gate 	/*
216*7c478bd9Sstevel@tonic-gate 	 * The close of the driver will do the unsubscribe if a) it is the last
217*7c478bd9Sstevel@tonic-gate 	 * close and b) we are in a child which inherited subscriptions.
218*7c478bd9Sstevel@tonic-gate 	 */
219*7c478bd9Sstevel@tonic-gate 	(void) close(EV_FD(scp));
220*7c478bd9Sstevel@tonic-gate 	(void) mutex_destroy(EV_LOCK(scp));
221*7c478bd9Sstevel@tonic-gate 	free(scp);
222*7c478bd9Sstevel@tonic-gate }
223*7c478bd9Sstevel@tonic-gate 
224*7c478bd9Sstevel@tonic-gate /*
225*7c478bd9Sstevel@tonic-gate  * sysevent_evc_publish - Generate a system event via an event channel
226*7c478bd9Sstevel@tonic-gate  */
227*7c478bd9Sstevel@tonic-gate int
228*7c478bd9Sstevel@tonic-gate sysevent_evc_publish(evchan_t *scp, const char *class,
229*7c478bd9Sstevel@tonic-gate     const char *subclass, const char *vendor,
230*7c478bd9Sstevel@tonic-gate     const char *pub_name, nvlist_t *attr_list,
231*7c478bd9Sstevel@tonic-gate     uint32_t flags)
232*7c478bd9Sstevel@tonic-gate {
233*7c478bd9Sstevel@tonic-gate 	sysevent_t *ev;
234*7c478bd9Sstevel@tonic-gate 	sev_publish_args_t uargs;
235*7c478bd9Sstevel@tonic-gate 	int rc;
236*7c478bd9Sstevel@tonic-gate 	int ec;
237*7c478bd9Sstevel@tonic-gate 
238*7c478bd9Sstevel@tonic-gate 	if (scp == NULL || misaligned(scp)) {
239*7c478bd9Sstevel@tonic-gate 		return (errno = EINVAL);
240*7c478bd9Sstevel@tonic-gate 	}
241*7c478bd9Sstevel@tonic-gate 
242*7c478bd9Sstevel@tonic-gate 	/* No inheritance of binding handles via fork() */
243*7c478bd9Sstevel@tonic-gate 	if (EV_PID(scp) != getpid()) {
244*7c478bd9Sstevel@tonic-gate 		return (errno = EINVAL);
245*7c478bd9Sstevel@tonic-gate 	}
246*7c478bd9Sstevel@tonic-gate 
247*7c478bd9Sstevel@tonic-gate 	ev = sysevent_alloc_event((char *)class, (char *)subclass,
248*7c478bd9Sstevel@tonic-gate 	    (char *)vendor, (char *)pub_name, attr_list);
249*7c478bd9Sstevel@tonic-gate 	if (ev == NULL) {
250*7c478bd9Sstevel@tonic-gate 		return (errno);
251*7c478bd9Sstevel@tonic-gate 	}
252*7c478bd9Sstevel@tonic-gate 
253*7c478bd9Sstevel@tonic-gate 	uargs.ev.name = (uintptr_t)ev;
254*7c478bd9Sstevel@tonic-gate 	uargs.ev.len = SE_SIZE(ev);
255*7c478bd9Sstevel@tonic-gate 	uargs.flags = flags;
256*7c478bd9Sstevel@tonic-gate 
257*7c478bd9Sstevel@tonic-gate 	(void) mutex_lock(EV_LOCK(scp));
258*7c478bd9Sstevel@tonic-gate 
259*7c478bd9Sstevel@tonic-gate 	rc = ioctl(EV_FD(scp), SEV_PUBLISH, (intptr_t)&uargs);
260*7c478bd9Sstevel@tonic-gate 	ec = errno;
261*7c478bd9Sstevel@tonic-gate 
262*7c478bd9Sstevel@tonic-gate 	(void) mutex_unlock(EV_LOCK(scp));
263*7c478bd9Sstevel@tonic-gate 
264*7c478bd9Sstevel@tonic-gate 	sysevent_free(ev);
265*7c478bd9Sstevel@tonic-gate 
266*7c478bd9Sstevel@tonic-gate 	if (rc != 0) {
267*7c478bd9Sstevel@tonic-gate 		return (ec);
268*7c478bd9Sstevel@tonic-gate 	}
269*7c478bd9Sstevel@tonic-gate 	return (0);
270*7c478bd9Sstevel@tonic-gate }
271*7c478bd9Sstevel@tonic-gate 
272*7c478bd9Sstevel@tonic-gate /*
273*7c478bd9Sstevel@tonic-gate  * Generic callback which catches events from the kernel and calls
274*7c478bd9Sstevel@tonic-gate  * subscribers call back routine.
275*7c478bd9Sstevel@tonic-gate  *
276*7c478bd9Sstevel@tonic-gate  * Kernel guarantees that door_upcalls are disabled when unsubscription
277*7c478bd9Sstevel@tonic-gate  * was issued that's why cookie points always to a valid evchan_subscr_t *.
278*7c478bd9Sstevel@tonic-gate  *
279*7c478bd9Sstevel@tonic-gate  * Furthermore it's not necessary to lock subp because the sysevent
280*7c478bd9Sstevel@tonic-gate  * framework guarantees no unsubscription until door_return.
281*7c478bd9Sstevel@tonic-gate  */
282*7c478bd9Sstevel@tonic-gate /*ARGSUSED3*/
283*7c478bd9Sstevel@tonic-gate static void
284*7c478bd9Sstevel@tonic-gate door_upcall(void *cookie, char *args, size_t alen,
285*7c478bd9Sstevel@tonic-gate     door_desc_t *ddp, uint_t ndid)
286*7c478bd9Sstevel@tonic-gate {
287*7c478bd9Sstevel@tonic-gate 	evchan_subscr_t *subp = EVCHAN_SUBSCR(cookie);
288*7c478bd9Sstevel@tonic-gate 	int rval = 0;
289*7c478bd9Sstevel@tonic-gate 
290*7c478bd9Sstevel@tonic-gate 	if (args == NULL || alen <= (size_t)0) {
291*7c478bd9Sstevel@tonic-gate 		/* Skip callback execution */
292*7c478bd9Sstevel@tonic-gate 		rval = EINVAL;
293*7c478bd9Sstevel@tonic-gate 	} else {
294*7c478bd9Sstevel@tonic-gate 		rval = subp->evsub_func((sysevent_t *)(void *)args,
295*7c478bd9Sstevel@tonic-gate 		    subp->evsub_cookie);
296*7c478bd9Sstevel@tonic-gate 	}
297*7c478bd9Sstevel@tonic-gate 
298*7c478bd9Sstevel@tonic-gate 	/*
299*7c478bd9Sstevel@tonic-gate 	 * Fill in return values for door_return
300*7c478bd9Sstevel@tonic-gate 	 */
301*7c478bd9Sstevel@tonic-gate 	alen = sizeof (rval);
302*7c478bd9Sstevel@tonic-gate 	bcopy(&rval, args, alen);
303*7c478bd9Sstevel@tonic-gate 
304*7c478bd9Sstevel@tonic-gate 	(void) door_return(args, alen, NULL, 0);
305*7c478bd9Sstevel@tonic-gate }
306*7c478bd9Sstevel@tonic-gate 
307*7c478bd9Sstevel@tonic-gate /*
308*7c478bd9Sstevel@tonic-gate  * sysevent_evc_subscribe - Subscribe to an existing event channel
309*7c478bd9Sstevel@tonic-gate  */
310*7c478bd9Sstevel@tonic-gate int
311*7c478bd9Sstevel@tonic-gate sysevent_evc_subscribe(evchan_t *scp, const char *sid, const char *class,
312*7c478bd9Sstevel@tonic-gate     int (*event_handler)(sysevent_t *ev, void *cookie),
313*7c478bd9Sstevel@tonic-gate     void *cookie, uint32_t flags)
314*7c478bd9Sstevel@tonic-gate {
315*7c478bd9Sstevel@tonic-gate 	evchan_subscr_t *subp;
316*7c478bd9Sstevel@tonic-gate 	int upcall_door;
317*7c478bd9Sstevel@tonic-gate 	sev_subscribe_args_t uargs;
318*7c478bd9Sstevel@tonic-gate 	uint32_t sid_len;
319*7c478bd9Sstevel@tonic-gate 	uint32_t class_len;
320*7c478bd9Sstevel@tonic-gate 	int ec;
321*7c478bd9Sstevel@tonic-gate 
322*7c478bd9Sstevel@tonic-gate 	if (scp == NULL || misaligned(scp) || sid == NULL || class == NULL) {
323*7c478bd9Sstevel@tonic-gate 		return (errno = EINVAL);
324*7c478bd9Sstevel@tonic-gate 	}
325*7c478bd9Sstevel@tonic-gate 
326*7c478bd9Sstevel@tonic-gate 	/* No inheritance of binding handles via fork() */
327*7c478bd9Sstevel@tonic-gate 	if (EV_PID(scp) != getpid()) {
328*7c478bd9Sstevel@tonic-gate 		return (errno = EINVAL);
329*7c478bd9Sstevel@tonic-gate 	}
330*7c478bd9Sstevel@tonic-gate 
331*7c478bd9Sstevel@tonic-gate 	if ((sid_len = strlen(sid) + 1) > MAX_SUBID_LEN || sid_len == 1 ||
332*7c478bd9Sstevel@tonic-gate 	    (class_len = strlen(class) + 1) > MAX_CLASS_LEN) {
333*7c478bd9Sstevel@tonic-gate 		return (errno = EINVAL);
334*7c478bd9Sstevel@tonic-gate 	}
335*7c478bd9Sstevel@tonic-gate 
336*7c478bd9Sstevel@tonic-gate 	/* Check for printable characters */
337*7c478bd9Sstevel@tonic-gate 	if (!strisprint(sid)) {
338*7c478bd9Sstevel@tonic-gate 		return (errno = EINVAL);
339*7c478bd9Sstevel@tonic-gate 	}
340*7c478bd9Sstevel@tonic-gate 
341*7c478bd9Sstevel@tonic-gate 	if (event_handler == NULL) {
342*7c478bd9Sstevel@tonic-gate 		return (errno = EINVAL);
343*7c478bd9Sstevel@tonic-gate 	}
344*7c478bd9Sstevel@tonic-gate 
345*7c478bd9Sstevel@tonic-gate 	/* Create subscriber data */
346*7c478bd9Sstevel@tonic-gate 	if ((subp = calloc(1, sizeof (evchan_subscr_t))) == NULL) {
347*7c478bd9Sstevel@tonic-gate 		return (errno);
348*7c478bd9Sstevel@tonic-gate 	}
349*7c478bd9Sstevel@tonic-gate 
350*7c478bd9Sstevel@tonic-gate 	if ((subp->evsub_sid = strdup(sid)) == NULL) {
351*7c478bd9Sstevel@tonic-gate 		ec = errno;
352*7c478bd9Sstevel@tonic-gate 		free(subp);
353*7c478bd9Sstevel@tonic-gate 		return (ec);
354*7c478bd9Sstevel@tonic-gate 	}
355*7c478bd9Sstevel@tonic-gate 
356*7c478bd9Sstevel@tonic-gate 	/*
357*7c478bd9Sstevel@tonic-gate 	 * EC_ALL string will not be copied to kernel - NULL is assumed
358*7c478bd9Sstevel@tonic-gate 	 */
359*7c478bd9Sstevel@tonic-gate 	if (strcmp(class, EC_ALL) == 0) {
360*7c478bd9Sstevel@tonic-gate 		class = NULL;
361*7c478bd9Sstevel@tonic-gate 		class_len = 0;
362*7c478bd9Sstevel@tonic-gate 	}
363*7c478bd9Sstevel@tonic-gate 
364*7c478bd9Sstevel@tonic-gate 	upcall_door = door_create(door_upcall, (void *)subp,
365*7c478bd9Sstevel@tonic-gate 	    DOOR_REFUSE_DESC | DOOR_NO_CANCEL);
366*7c478bd9Sstevel@tonic-gate 	if (upcall_door == -1) {
367*7c478bd9Sstevel@tonic-gate 		ec = errno;
368*7c478bd9Sstevel@tonic-gate 		free(subp->evsub_sid);
369*7c478bd9Sstevel@tonic-gate 		free(subp);
370*7c478bd9Sstevel@tonic-gate 		return (ec);
371*7c478bd9Sstevel@tonic-gate 	}
372*7c478bd9Sstevel@tonic-gate 
373*7c478bd9Sstevel@tonic-gate 	/* Complete subscriber information */
374*7c478bd9Sstevel@tonic-gate 	subp->evsub_door_desc = upcall_door;
375*7c478bd9Sstevel@tonic-gate 	subp->evsub_func = event_handler;
376*7c478bd9Sstevel@tonic-gate 	subp->evsub_cookie = cookie;
377*7c478bd9Sstevel@tonic-gate 
378*7c478bd9Sstevel@tonic-gate 	(void) mutex_lock(EV_LOCK(scp));
379*7c478bd9Sstevel@tonic-gate 
380*7c478bd9Sstevel@tonic-gate 	subp->ev_subhead = EVCHAN_IMPL_HNDL(scp);
381*7c478bd9Sstevel@tonic-gate 
382*7c478bd9Sstevel@tonic-gate 	uargs.sid.name = (uintptr_t)sid;
383*7c478bd9Sstevel@tonic-gate 	uargs.sid.len = sid_len;
384*7c478bd9Sstevel@tonic-gate 	uargs.class_info.name = (uintptr_t)class;
385*7c478bd9Sstevel@tonic-gate 	uargs.class_info.len = class_len;
386*7c478bd9Sstevel@tonic-gate 	uargs.door_desc = subp->evsub_door_desc;
387*7c478bd9Sstevel@tonic-gate 	uargs.flags = flags;
388*7c478bd9Sstevel@tonic-gate 	if (ioctl(EV_FD(scp), SEV_SUBSCRIBE, (intptr_t)&uargs) != 0) {
389*7c478bd9Sstevel@tonic-gate 		ec = errno;
390*7c478bd9Sstevel@tonic-gate 		(void) mutex_unlock(EV_LOCK(scp));
391*7c478bd9Sstevel@tonic-gate 		(void) door_revoke(upcall_door);
392*7c478bd9Sstevel@tonic-gate 		free(subp->evsub_sid);
393*7c478bd9Sstevel@tonic-gate 		free(subp);
394*7c478bd9Sstevel@tonic-gate 		return (ec);
395*7c478bd9Sstevel@tonic-gate 	}
396*7c478bd9Sstevel@tonic-gate 
397*7c478bd9Sstevel@tonic-gate 	/* Attach to subscriber list */
398*7c478bd9Sstevel@tonic-gate 	subp->evsub_next = EV_SUB_NEXT(scp);
399*7c478bd9Sstevel@tonic-gate 	EV_SUB_NEXT(scp) = subp;
400*7c478bd9Sstevel@tonic-gate 
401*7c478bd9Sstevel@tonic-gate 	(void) mutex_unlock(EV_LOCK(scp));
402*7c478bd9Sstevel@tonic-gate 
403*7c478bd9Sstevel@tonic-gate 	return (0);
404*7c478bd9Sstevel@tonic-gate }
405*7c478bd9Sstevel@tonic-gate 
406*7c478bd9Sstevel@tonic-gate /*
407*7c478bd9Sstevel@tonic-gate  * sysevent_evc_unsubscribe - Unsubscribe from an existing event channel
408*7c478bd9Sstevel@tonic-gate  */
409*7c478bd9Sstevel@tonic-gate void
410*7c478bd9Sstevel@tonic-gate sysevent_evc_unsubscribe(evchan_t *scp, const char *sid)
411*7c478bd9Sstevel@tonic-gate {
412*7c478bd9Sstevel@tonic-gate 	int all_subscribers = 0;
413*7c478bd9Sstevel@tonic-gate 	sev_unsubscribe_args_t uargs;
414*7c478bd9Sstevel@tonic-gate 	evchan_subscr_t *subp, *tofree;
415*7c478bd9Sstevel@tonic-gate 	int rc;
416*7c478bd9Sstevel@tonic-gate 
417*7c478bd9Sstevel@tonic-gate 	if (scp == NULL || misaligned(scp))
418*7c478bd9Sstevel@tonic-gate 		return;
419*7c478bd9Sstevel@tonic-gate 
420*7c478bd9Sstevel@tonic-gate 	if (sid == NULL || strlen(sid) == 0 ||
421*7c478bd9Sstevel@tonic-gate 	    (strlen(sid) >= MAX_SUBID_LEN))
422*7c478bd9Sstevel@tonic-gate 		return;
423*7c478bd9Sstevel@tonic-gate 
424*7c478bd9Sstevel@tonic-gate 	/* No inheritance of binding handles via fork() */
425*7c478bd9Sstevel@tonic-gate 	if (EV_PID(scp) != getpid()) {
426*7c478bd9Sstevel@tonic-gate 		return;
427*7c478bd9Sstevel@tonic-gate 	}
428*7c478bd9Sstevel@tonic-gate 
429*7c478bd9Sstevel@tonic-gate 	if (strcmp(sid, EVCH_ALLSUB) == 0) {
430*7c478bd9Sstevel@tonic-gate 		all_subscribers++;
431*7c478bd9Sstevel@tonic-gate 		/* Indicates all subscriber id's for this channel */
432*7c478bd9Sstevel@tonic-gate 		uargs.sid.name = NULL;
433*7c478bd9Sstevel@tonic-gate 		uargs.sid.len = 0;
434*7c478bd9Sstevel@tonic-gate 	} else {
435*7c478bd9Sstevel@tonic-gate 		uargs.sid.name = (uintptr_t)sid;
436*7c478bd9Sstevel@tonic-gate 		uargs.sid.len = strlen(sid) + 1;
437*7c478bd9Sstevel@tonic-gate 	}
438*7c478bd9Sstevel@tonic-gate 
439*7c478bd9Sstevel@tonic-gate 	(void) mutex_lock(EV_LOCK(scp));
440*7c478bd9Sstevel@tonic-gate 
441*7c478bd9Sstevel@tonic-gate 	/*
442*7c478bd9Sstevel@tonic-gate 	 * The unsubscribe ioctl will block until all door upcalls have drained.
443*7c478bd9Sstevel@tonic-gate 	 */
444*7c478bd9Sstevel@tonic-gate 	rc = ioctl(EV_FD(scp), SEV_UNSUBSCRIBE, (intptr_t)&uargs);
445*7c478bd9Sstevel@tonic-gate 
446*7c478bd9Sstevel@tonic-gate 	if (rc != 0) {
447*7c478bd9Sstevel@tonic-gate 		(void) mutex_unlock(EV_LOCK(scp));
448*7c478bd9Sstevel@tonic-gate 		return;
449*7c478bd9Sstevel@tonic-gate 	}
450*7c478bd9Sstevel@tonic-gate 
451*7c478bd9Sstevel@tonic-gate 	/* Search for the matching subscriber */
452*7c478bd9Sstevel@tonic-gate 	subp =  (evchan_subscr_t *)(void*)EV_SUB(scp);
453*7c478bd9Sstevel@tonic-gate 	while (subp->evsub_next != NULL) {
454*7c478bd9Sstevel@tonic-gate 
455*7c478bd9Sstevel@tonic-gate 		if (all_subscribers ||
456*7c478bd9Sstevel@tonic-gate 		    (strcmp(subp->evsub_next->evsub_sid, sid) == 0)) {
457*7c478bd9Sstevel@tonic-gate 
458*7c478bd9Sstevel@tonic-gate 			tofree = subp->evsub_next;
459*7c478bd9Sstevel@tonic-gate 			subp->evsub_next = tofree->evsub_next;
460*7c478bd9Sstevel@tonic-gate 			(void) door_revoke(tofree->evsub_door_desc);
461*7c478bd9Sstevel@tonic-gate 			free(tofree->evsub_sid);
462*7c478bd9Sstevel@tonic-gate 			free(tofree);
463*7c478bd9Sstevel@tonic-gate 			/* Freed single subscriber already */
464*7c478bd9Sstevel@tonic-gate 			if (all_subscribers == 0) {
465*7c478bd9Sstevel@tonic-gate 				break;
466*7c478bd9Sstevel@tonic-gate 			}
467*7c478bd9Sstevel@tonic-gate 		} else
468*7c478bd9Sstevel@tonic-gate 			subp = subp->evsub_next;
469*7c478bd9Sstevel@tonic-gate 	}
470*7c478bd9Sstevel@tonic-gate 
471*7c478bd9Sstevel@tonic-gate 	(void) mutex_unlock(EV_LOCK(scp));
472*7c478bd9Sstevel@tonic-gate }
473*7c478bd9Sstevel@tonic-gate 
474*7c478bd9Sstevel@tonic-gate /*
475*7c478bd9Sstevel@tonic-gate  * sysevent_evc_control - Various channel based control operation
476*7c478bd9Sstevel@tonic-gate  */
477*7c478bd9Sstevel@tonic-gate int
478*7c478bd9Sstevel@tonic-gate sysevent_evc_control(evchan_t *scp, int cmd, /* arg */ ...)
479*7c478bd9Sstevel@tonic-gate {
480*7c478bd9Sstevel@tonic-gate 	va_list ap;
481*7c478bd9Sstevel@tonic-gate 	uint32_t *chlenp;
482*7c478bd9Sstevel@tonic-gate 	sev_control_args_t uargs;
483*7c478bd9Sstevel@tonic-gate 	int rc = 0;
484*7c478bd9Sstevel@tonic-gate 
485*7c478bd9Sstevel@tonic-gate 	if (scp == NULL || misaligned(scp)) {
486*7c478bd9Sstevel@tonic-gate 		return (errno = EINVAL);
487*7c478bd9Sstevel@tonic-gate 	}
488*7c478bd9Sstevel@tonic-gate 
489*7c478bd9Sstevel@tonic-gate 	/* No inheritance of binding handles via fork() */
490*7c478bd9Sstevel@tonic-gate 	if (EV_PID(scp) != getpid()) {
491*7c478bd9Sstevel@tonic-gate 		return (errno = EINVAL);
492*7c478bd9Sstevel@tonic-gate 	}
493*7c478bd9Sstevel@tonic-gate 
494*7c478bd9Sstevel@tonic-gate 	va_start(ap, cmd);
495*7c478bd9Sstevel@tonic-gate 
496*7c478bd9Sstevel@tonic-gate 	uargs.cmd = cmd;
497*7c478bd9Sstevel@tonic-gate 
498*7c478bd9Sstevel@tonic-gate 	(void) mutex_lock(EV_LOCK(scp));
499*7c478bd9Sstevel@tonic-gate 
500*7c478bd9Sstevel@tonic-gate 	switch (cmd) {
501*7c478bd9Sstevel@tonic-gate 	case EVCH_GET_CHAN_LEN:
502*7c478bd9Sstevel@tonic-gate 	case EVCH_GET_CHAN_LEN_MAX:
503*7c478bd9Sstevel@tonic-gate 		chlenp = va_arg(ap, uint32_t *);
504*7c478bd9Sstevel@tonic-gate 		if (chlenp == NULL || misaligned(chlenp)) {
505*7c478bd9Sstevel@tonic-gate 			rc = EINVAL;
506*7c478bd9Sstevel@tonic-gate 			break;
507*7c478bd9Sstevel@tonic-gate 		}
508*7c478bd9Sstevel@tonic-gate 		rc = ioctl(EV_FD(scp), SEV_CHAN_CONTROL, (intptr_t)&uargs);
509*7c478bd9Sstevel@tonic-gate 		*chlenp = uargs.value;
510*7c478bd9Sstevel@tonic-gate 		break;
511*7c478bd9Sstevel@tonic-gate 	case EVCH_SET_CHAN_LEN:
512*7c478bd9Sstevel@tonic-gate 		/* Range change will be handled in framework */
513*7c478bd9Sstevel@tonic-gate 		uargs.value = va_arg(ap, uint32_t);
514*7c478bd9Sstevel@tonic-gate 		rc = ioctl(EV_FD(scp), SEV_CHAN_CONTROL, (intptr_t)&uargs);
515*7c478bd9Sstevel@tonic-gate 		break;
516*7c478bd9Sstevel@tonic-gate 	default:
517*7c478bd9Sstevel@tonic-gate 		rc = EINVAL;
518*7c478bd9Sstevel@tonic-gate 	}
519*7c478bd9Sstevel@tonic-gate 
520*7c478bd9Sstevel@tonic-gate 	(void) mutex_unlock(EV_LOCK(scp));
521*7c478bd9Sstevel@tonic-gate 
522*7c478bd9Sstevel@tonic-gate 	if (rc == -1) {
523*7c478bd9Sstevel@tonic-gate 		rc = errno;
524*7c478bd9Sstevel@tonic-gate 	}
525*7c478bd9Sstevel@tonic-gate 
526*7c478bd9Sstevel@tonic-gate 	va_end(ap);
527*7c478bd9Sstevel@tonic-gate 
528*7c478bd9Sstevel@tonic-gate 	return (errno = rc);
529*7c478bd9Sstevel@tonic-gate }
530