#include <sys/types.h>
#include <sys/errno.h>
#include <sys/stropts.h>
#include <sys/debug.h>
#include <sys/ddi.h>
#include <sys/vmem.h>
#include <sys/cmn_err.h>
#include <sys/callb.h>
#include <sys/sysevent.h>
#include <sys/sysevent_impl.h>
#include <sys/sysmacros.h>
#include <sys/disp.h>
#include <sys/atomic.h>
#include <sys/door.h>
#include <sys/zone.h>
#include <sys/sdt.h>
#define EVCH_MIN_PAUSE 8
#define EVCH_MAX_PAUSE 128
#define GEVENT(ev) ((evch_gevent_t *)((char *)ev - \
offsetof(evch_gevent_t, ge_payload)))
#define EVCH_EVQ_EVCOUNT(x) ((&(x)->eq_eventq)->sq_count)
#define EVCH_EVQ_HIGHWM(x) ((&(x)->eq_eventq)->sq_highwm)
#define CH_HOLD_PEND 1
#define CH_HOLD_PEND_INDEF 2
struct evch_globals {
evch_dlist_t evch_list;
kmutex_t evch_list_lock;
};
static int evq_initcomplete = 0;
static zone_key_t evch_zone_key;
static uint32_t evch_channels_max;
static uint32_t evch_bindings_max = EVCH_MAX_BINDS_PER_CHANNEL;
static uint32_t evch_events_max;
static void evch_evq_unsub(evch_eventq_t *, evch_evqsub_t *);
static void evch_evq_destroy(evch_eventq_t *);
static void
evch_dl_init(evch_dlist_t *hp)
{
hp->dh_head.dl_prev = hp->dh_head.dl_next = &hp->dh_head;
hp->dh_count = 0;
}
static void
evch_dl_fini(evch_dlist_t *hp)
{
hp->dh_head.dl_prev = hp->dh_head.dl_next = NULL;
}
static int
evch_dl_is_init(evch_dlist_t *hp)
{
return (hp->dh_head.dl_next != NULL ? 1 : 0);
}
static void
evch_dl_add(evch_dlist_t *hp, evch_dlelem_t *el)
{
evch_dlelem_t *x = hp->dh_head.dl_prev;
evch_dlelem_t *y = &hp->dh_head;
x->dl_next = el;
y->dl_prev = el;
el->dl_next = y;
el->dl_prev = x;
hp->dh_count++;
}
static void
evch_dl_del(evch_dlist_t *hp, evch_dlelem_t *p)
{
ASSERT(hp->dh_count > 0 && p != &hp->dh_head);
p->dl_prev->dl_next = p->dl_next;
p->dl_next->dl_prev = p->dl_prev;
p->dl_prev = NULL;
p->dl_next = NULL;
hp->dh_count--;
}
static evch_dlelem_t *
evch_dl_search(evch_dlist_t *hp, int (*cmp)(evch_dlelem_t *, char *), char *s)
{
evch_dlelem_t *p;
for (p = hp->dh_head.dl_next; p != &hp->dh_head; p = p->dl_next) {
if (cmp(p, s) == 0) {
return (p);
}
}
return (NULL);
}
static int
evch_dl_getnum(evch_dlist_t *hp)
{
return (hp->dh_count);
}
static void *
evch_dl_next(evch_dlist_t *hp, void *el)
{
evch_dlelem_t *ep = (evch_dlelem_t *)el;
if (hp->dh_count == 0) {
return (NULL);
}
if (ep == NULL) {
return (hp->dh_head.dl_next);
}
if ((ep = ep->dl_next) == (evch_dlelem_t *)hp) {
return (NULL);
}
return ((void *)ep);
}
static void
evch_q_init(evch_squeue_t *q)
{
q->sq_head = NULL;
q->sq_tail = (evch_qelem_t *)q;
q->sq_count = 0;
q->sq_highwm = 0;
}
static void
evch_q_in(evch_squeue_t *q, evch_qelem_t *el)
{
q->sq_tail->q_next = el;
el->q_next = NULL;
q->sq_tail = el;
q->sq_count++;
if (q->sq_count > q->sq_highwm) {
q->sq_highwm = q->sq_count;
}
}
static evch_qelem_t *
evch_q_out(evch_squeue_t *q)
{
evch_qelem_t *el;
if ((el = q->sq_head) != NULL) {
q->sq_head = el->q_next;
q->sq_count--;
if (q->sq_head == NULL) {
q->sq_tail = (evch_qelem_t *)q;
}
}
return (el);
}
static evch_qelem_t *
evch_q_next(evch_squeue_t *q, evch_qelem_t *el)
{
if (el == NULL)
return (q->sq_head);
return (el->q_next);
}
static void *
evch_zoneinit(zoneid_t zoneid)
{
struct evch_globals *eg;
eg = kmem_zalloc(sizeof (*eg), KM_SLEEP);
evch_dl_init(&eg->evch_list);
return (eg);
}
static void
evch_zonefree(zoneid_t zoneid, void *arg)
{
struct evch_globals *eg = arg;
evch_chan_t *chp;
evch_subd_t *sdp;
mutex_enter(&eg->evch_list_lock);
while ((chp = evch_dl_next(&eg->evch_list, NULL)) != NULL) {
mutex_enter(&chp->ch_mutex);
ASSERT(chp->ch_bindings == 0);
ASSERT(evch_dl_getnum(&chp->ch_subscr) != 0 ||
chp->ch_holdpend == CH_HOLD_PEND_INDEF);
while ((sdp = evch_dl_next(&chp->ch_subscr, NULL)) != NULL) {
ASSERT(sdp->sd_active == 0);
ASSERT((sdp->sd_persist & EVCH_SUB_KEEP) != 0);
evch_evq_unsub(chp->ch_queue, sdp->sd_msub);
evch_evq_unsub(sdp->sd_queue, sdp->sd_ssub);
evch_evq_destroy(sdp->sd_queue);
evch_dl_del(&chp->ch_subscr, &sdp->sd_link);
kmem_free(sdp->sd_classname, sdp->sd_clnsize);
kmem_free(sdp->sd_ident, strlen(sdp->sd_ident) + 1);
kmem_free(sdp, sizeof (evch_subd_t));
}
ASSERT(evch_dl_getnum(&chp->ch_subscr) == 0);
mutex_exit(&chp->ch_mutex);
evch_dl_del(&eg->evch_list, &chp->ch_link);
evch_evq_destroy(chp->ch_queue);
mutex_destroy(&chp->ch_mutex);
mutex_destroy(&chp->ch_pubmx);
cv_destroy(&chp->ch_pubcv);
kmem_free(chp->ch_name, chp->ch_namelen);
kmem_free(chp, sizeof (evch_chan_t));
}
mutex_exit(&eg->evch_list_lock);
ASSERT(evch_dl_getnum(&eg->evch_list) == 0);
kmem_free(eg, sizeof (*eg));
}
static void
evch_gevent_free(evch_gevent_t *evp)
{
int32_t refcnt;
refcnt = (int32_t)atomic_dec_32_nv(&evp->ge_refcount);
if (refcnt <= 0) {
if (evp->ge_destruct != NULL) {
evp->ge_destruct((void *)&(evp->ge_payload),
evp->ge_dstcookie);
}
kmem_free(evp, evp->ge_size);
}
}
static int
evch_deliver(evch_evqsub_t *sp, evch_gevent_t *ep)
{
void *uep = &ep->ge_payload;
int res = EVQ_DELIVER;
if (sp->su_filter != NULL) {
res = sp->su_filter(uep, sp->su_fcookie);
}
if (res == EVQ_DELIVER) {
return (sp->su_callb(uep, sp->su_cbcookie));
}
return (0);
}
static void
evch_delivery_hold(evch_eventq_t *eqp, callb_cpr_t *cpip)
{
if (eqp->eq_tabortflag == 0) {
do {
if (eqp->eq_holdmode) {
cv_signal(&eqp->eq_onholdcv);
}
CALLB_CPR_SAFE_BEGIN(cpip);
cv_wait(&eqp->eq_thrsleepcv, &eqp->eq_queuemx);
CALLB_CPR_SAFE_END(cpip, &eqp->eq_queuemx);
} while (eqp->eq_holdmode);
}
}
static void
evch_delivery_thr(evch_eventq_t *eqp)
{
evch_qelem_t *qep;
callb_cpr_t cprinfo;
int res;
evch_evqsub_t *sub;
int deltime;
int repeatcount;
char thnam[32];
(void) snprintf(thnam, sizeof (thnam), "sysevent_chan-%d",
(int)eqp->eq_thrid);
CALLB_CPR_INIT(&cprinfo, &eqp->eq_queuemx, callb_generic_cpr, thnam);
mutex_enter(&eqp->eq_queuemx);
while (eqp->eq_tabortflag == 0) {
while (eqp->eq_holdmode == 0 && eqp->eq_tabortflag == 0 &&
(qep = evch_q_out(&eqp->eq_eventq)) != NULL) {
deltime = EVCH_MIN_PAUSE;
repeatcount = EVCH_MAX_TRY_DELIVERY;
eqp->eq_curevent = qep->q_objref;
sub = evch_dl_next(&eqp->eq_subscr, NULL);
while (sub != NULL) {
eqp->eq_dactive = 1;
mutex_exit(&eqp->eq_queuemx);
res = evch_deliver(sub, qep->q_objref);
mutex_enter(&eqp->eq_queuemx);
eqp->eq_dactive = 0;
cv_signal(&eqp->eq_dactivecv);
switch (res) {
case EVQ_SLEEP:
eqp->eq_holdmode = 1;
evch_delivery_hold(eqp, &cprinfo);
if (eqp->eq_tabortflag) {
break;
}
continue;
case EVQ_AGAIN:
CALLB_CPR_SAFE_BEGIN(&cprinfo);
mutex_exit(&eqp->eq_queuemx);
delay(deltime);
deltime =
deltime > EVCH_MAX_PAUSE ?
deltime : deltime << 1;
mutex_enter(&eqp->eq_queuemx);
CALLB_CPR_SAFE_END(&cprinfo,
&eqp->eq_queuemx);
if (repeatcount-- > 0) {
continue;
}
break;
}
if (eqp->eq_tabortflag) {
break;
}
sub = evch_dl_next(&eqp->eq_subscr, sub);
repeatcount = EVCH_MAX_TRY_DELIVERY;
}
eqp->eq_curevent = NULL;
evch_gevent_free((evch_gevent_t *)qep->q_objref);
kmem_free(qep, qep->q_objsize);
}
evch_delivery_hold(eqp, &cprinfo);
}
CALLB_CPR_EXIT(&cprinfo);
thread_exit();
}
static void
evch_evq_thrcreate(evch_eventq_t *eqp)
{
kthread_t *thp;
thp = thread_create(NULL, 0, evch_delivery_thr, (char *)eqp, 0, &p0,
TS_RUN, minclsyspri);
eqp->eq_thrid = thp->t_did;
}
static evch_eventq_t *
evch_evq_create()
{
evch_eventq_t *p;
p = kmem_zalloc(sizeof (evch_eventq_t), KM_SLEEP);
mutex_init(&p->eq_queuemx, NULL, MUTEX_DEFAULT, NULL);
cv_init(&p->eq_thrsleepcv, NULL, CV_DEFAULT, NULL);
evch_q_init(&p->eq_eventq);
evch_dl_init(&p->eq_subscr);
cv_init(&p->eq_dactivecv, NULL, CV_DEFAULT, NULL);
cv_init(&p->eq_onholdcv, NULL, CV_DEFAULT, NULL);
if (evq_initcomplete) {
evch_evq_thrcreate(p);
}
return (p);
}
static void
evch_evq_destroy(evch_eventq_t *eqp)
{
evch_qelem_t *qep;
ASSERT(evch_dl_getnum(&eqp->eq_subscr) == 0);
if (eqp->eq_thrid != 0) {
mutex_enter(&eqp->eq_queuemx);
eqp->eq_tabortflag = 1;
eqp->eq_holdmode = 0;
cv_signal(&eqp->eq_thrsleepcv);
mutex_exit(&eqp->eq_queuemx);
thread_join(eqp->eq_thrid);
}
while ((qep = (evch_qelem_t *)evch_q_out(&eqp->eq_eventq)) != NULL) {
evch_gevent_free((evch_gevent_t *)qep->q_objref);
kmem_free(qep, qep->q_objsize);
}
cv_destroy(&eqp->eq_onholdcv);
cv_destroy(&eqp->eq_dactivecv);
cv_destroy(&eqp->eq_thrsleepcv);
evch_dl_fini(&eqp->eq_subscr);
mutex_destroy(&eqp->eq_queuemx);
kmem_free(eqp, sizeof (evch_eventq_t));
}
static evch_evqsub_t *
evch_evq_sub(evch_eventq_t *eqp, filter_f filter, void *fcookie,
deliver_f callb, void *cbcookie)
{
evch_evqsub_t *sp = kmem_zalloc(sizeof (evch_evqsub_t), KM_SLEEP);
sp->su_filter = filter;
sp->su_fcookie = fcookie;
sp->su_callb = callb;
sp->su_cbcookie = cbcookie;
mutex_enter(&eqp->eq_queuemx);
evch_dl_add(&eqp->eq_subscr, &sp->su_link);
mutex_exit(&eqp->eq_queuemx);
return (sp);
}
static void
evch_evq_unsub(evch_eventq_t *eqp, evch_evqsub_t *sp)
{
mutex_enter(&eqp->eq_queuemx);
if (eqp->eq_dactive) {
cv_wait(&eqp->eq_dactivecv, &eqp->eq_queuemx);
}
evch_dl_del(&eqp->eq_subscr, &sp->su_link);
mutex_exit(&eqp->eq_queuemx);
kmem_free(sp, sizeof (evch_evqsub_t));
}
static int
evch_evq_pub(evch_eventq_t *eqp, void *ev, int flags)
{
size_t size;
evch_qelem_t *qep;
evch_gevent_t *evp = GEVENT(ev);
size = sizeof (evch_qelem_t);
if (flags & EVCH_TRYHARD) {
qep = kmem_alloc_tryhard(size, &size, KM_NOSLEEP);
} else {
qep = kmem_alloc(size, flags & EVCH_NOSLEEP ?
KM_NOSLEEP : KM_SLEEP);
}
if (qep == NULL) {
return (-1);
}
qep->q_objref = (void *)evp;
qep->q_objsize = size;
atomic_inc_32(&evp->ge_refcount);
mutex_enter(&eqp->eq_queuemx);
evch_q_in(&eqp->eq_eventq, qep);
cv_signal(&eqp->eq_thrsleepcv);
mutex_exit(&eqp->eq_queuemx);
return (0);
}
static void
evch_evq_stop(evch_eventq_t *eqp)
{
mutex_enter(&eqp->eq_queuemx);
eqp->eq_holdmode = 1;
if (evq_initcomplete) {
cv_signal(&eqp->eq_thrsleepcv);
cv_wait(&eqp->eq_onholdcv, &eqp->eq_queuemx);
}
mutex_exit(&eqp->eq_queuemx);
}
static void
evch_evq_continue(evch_eventq_t *eqp)
{
mutex_enter(&eqp->eq_queuemx);
eqp->eq_holdmode = 0;
cv_signal(&eqp->eq_thrsleepcv);
mutex_exit(&eqp->eq_queuemx);
}
static int
evch_evq_status(evch_eventq_t *eqp)
{
return (eqp->eq_holdmode);
}
static void
evch_evq_evadd_dest(void *ev, destr_f destructor, void *cookie)
{
evch_gevent_t *evp = GEVENT(ev);
evp->ge_destruct = destructor;
evp->ge_dstcookie = cookie;
}
static void *
evch_evq_evzalloc(size_t paylsize, int flag)
{
evch_gevent_t *evp;
size_t rsize, evsize, ge_size;
rsize = offsetof(evch_gevent_t, ge_payload) + paylsize;
if (flag & EVCH_TRYHARD) {
evp = kmem_alloc_tryhard(rsize, &evsize, KM_NOSLEEP);
ge_size = evsize;
} else {
evp = kmem_alloc(rsize, flag & EVCH_NOSLEEP ? KM_NOSLEEP :
KM_SLEEP);
ge_size = rsize;
}
if (evp) {
bzero(evp, rsize);
evp->ge_size = ge_size;
return (&evp->ge_payload);
}
return (evp);
}
static void
evch_evq_evfree(void *ev)
{
evch_gevent_free(GEVENT(ev));
}
static void *
evch_evq_evnext(evch_eventq_t *evq, void *ev)
{
if (ev == NULL) {
evq->eq_nextev = NULL;
if (evq->eq_curevent != NULL)
return (&evq->eq_curevent->ge_payload);
}
evq->eq_nextev = evch_q_next(&evq->eq_eventq, evq->eq_nextev);
if (evq->eq_nextev == NULL)
return (NULL);
return (&((evch_gevent_t *)evq->eq_nextev->q_objref)->ge_payload);
}
static int
evch_namecmp(evch_dlelem_t *ep, char *s)
{
return (strcmp(((evch_chan_t *)ep)->ch_name, s));
}
static int
evch_clsmatch(char *class, const char *pat)
{
char c;
do {
if ((c = *pat++) == '\0')
return (*class == '\0');
if (c == '*') {
while (*pat == '*')
pat++;
if (*pat == '\0')
return (1);
while (*class != '\0') {
if (evch_clsmatch(class++, pat) != 0)
return (1);
}
return (0);
}
} while (c == *class++);
return (0);
}
static int
evch_class_filter(void *ev, void *cookie)
{
const char *pat = (const char *)cookie;
if (pat == NULL || evch_clsmatch(SE_CLASS_NAME(ev), pat))
return (EVQ_DELIVER);
return (EVQ_IGNORE);
}
static int
evch_subq_deliver(void *evp, void *cookie)
{
evch_subd_t *p = (evch_subd_t *)cookie;
(void) evch_evq_pub(p->sd_queue, evp, EVCH_SLEEP);
return (EVQ_CONT);
}
static int
evch_kern_deliver(void *evp, void *cookie)
{
sysevent_impl_t *ev = (sysevent_impl_t *)evp;
evch_subd_t *sdp = (evch_subd_t *)cookie;
return (sdp->sd_callback(ev, sdp->sd_cbcookie));
}
static int
evch_door_deliver(void *evp, void *cookie)
{
int error;
size_t size;
sysevent_impl_t *ev = (sysevent_impl_t *)evp;
door_arg_t darg;
evch_subd_t *sdp = (evch_subd_t *)cookie;
int nticks = EVCH_MIN_PAUSE;
uint32_t retval;
int retry = 20;
size = sizeof (sysevent_impl_t) + SE_PAYLOAD_SZ(ev);
darg.rbuf = (char *)&retval;
darg.rsize = sizeof (retval);
darg.data_ptr = (char *)ev;
darg.data_size = size;
darg.desc_ptr = NULL;
darg.desc_num = 0;
for (;;) {
if ((error = door_ki_upcall_limited(sdp->sd_door, &darg,
NULL, SIZE_MAX, 0)) == 0) {
break;
}
switch (error) {
case EAGAIN:
delay(nticks);
nticks <<= 1;
if (nticks > EVCH_MAX_PAUSE) {
nticks = EVCH_MAX_PAUSE;
}
if (retry-- <= 0) {
cmn_err(CE_CONT, "event delivery thread: "
"door_ki_upcall error EAGAIN\n");
return (EVQ_CONT);
}
break;
case EINTR:
case EBADF:
return (EVQ_SLEEP);
default:
cmn_err(CE_CONT,
"event delivery thread: door_ki_upcall error %d\n",
error);
return (EVQ_CONT);
}
}
if (retval == EAGAIN) {
return (EVQ_AGAIN);
}
return (EVQ_CONT);
}
static int
evch_subidcmp(evch_dlelem_t *ep, char *s)
{
return (strcmp(((evch_subd_t *)ep)->sd_ident, s));
}
static int
evch_dumpflgcmp(evch_dlelem_t *ep, char *s)
{
return (((evch_subd_t *)ep)->sd_dump ? 0 : 1);
}
static void
evch_destr_event(void *ev, void *ch)
{
evch_chan_t *chp = (evch_chan_t *)ch;
mutex_enter(&chp->ch_pubmx);
chp->ch_nevents--;
cv_signal(&chp->ch_pubcv);
mutex_exit(&chp->ch_pubmx);
}
static uint32_t
evch_isqrt(uint64_t n)
{
uint64_t x = n >> 1;
uint64_t xn = x - 1;
static uint32_t lowval[] = { 0, 1, 1, 2 };
if (n < 4) {
return (lowval[n]);
}
while (xn < x) {
x = xn;
xn = (x + n / x) / 2;
}
return ((uint32_t)xn);
}
static void
evch_chinit()
{
size_t k;
k = kmem_maxavail() >> 20;
evch_channels_max = min(evch_isqrt(k), EVCH_MAX_CHANNELS);
evch_events_max = evch_channels_max << 8;
zone_key_create(&evch_zone_key, evch_zoneinit, NULL, evch_zonefree);
}
static void
evch_chinitthr()
{
struct evch_globals *eg;
evch_chan_t *chp;
evch_subd_t *sdp;
eg = zone_getspecific(evch_zone_key, global_zone);
ASSERT(eg != NULL);
for (chp = evch_dl_next(&eg->evch_list, NULL); chp != NULL;
chp = evch_dl_next(&eg->evch_list, chp)) {
for (sdp = evch_dl_next(&chp->ch_subscr, NULL); sdp;
sdp = evch_dl_next(&chp->ch_subscr, sdp)) {
evch_evq_thrcreate(sdp->sd_queue);
}
evch_evq_thrcreate(chp->ch_queue);
}
evq_initcomplete = 1;
}
static int
evch_chbind(const char *chnam, evch_bind_t **scpp, uint32_t flags)
{
struct evch_globals *eg;
evch_bind_t *bp;
evch_chan_t *p;
char *chn;
size_t namlen;
int rv;
eg = zone_getspecific(evch_zone_key, curproc->p_zone);
ASSERT(eg != NULL);
ASSERT(evch_dl_is_init(&eg->evch_list));
if ((namlen = strlen(chnam) + 1) > MAX_CHNAME_LEN) {
return (EINVAL);
}
mutex_enter(&eg->evch_list_lock);
if ((p = (evch_chan_t *)evch_dl_search(&eg->evch_list, evch_namecmp,
(char *)chnam)) == NULL) {
if (flags & EVCH_CREAT) {
if (evch_dl_getnum(&eg->evch_list) >=
evch_channels_max) {
mutex_exit(&eg->evch_list_lock);
return (ENOMEM);
}
chn = kmem_alloc(namlen, KM_SLEEP);
bcopy(chnam, chn, namlen);
p = kmem_zalloc(sizeof (evch_chan_t), KM_SLEEP);
p->ch_name = chn;
p->ch_namelen = namlen;
mutex_init(&p->ch_mutex, NULL, MUTEX_DEFAULT, NULL);
p->ch_queue = evch_evq_create();
evch_dl_init(&p->ch_subscr);
if (evq_initcomplete) {
p->ch_uid = crgetuid(curthread->t_cred);
p->ch_gid = crgetgid(curthread->t_cred);
}
cv_init(&p->ch_pubcv, NULL, CV_DEFAULT, NULL);
mutex_init(&p->ch_pubmx, NULL, MUTEX_DEFAULT, NULL);
p->ch_maxev = min(EVCH_DEFAULT_EVENTS, evch_events_max);
p->ch_maxsubscr = EVCH_MAX_SUBSCRIPTIONS;
p->ch_maxbinds = evch_bindings_max;
p->ch_ctime = gethrestime_sec();
if (flags & (EVCH_HOLD_PEND | EVCH_HOLD_PEND_INDEF)) {
if (flags & EVCH_HOLD_PEND_INDEF)
p->ch_holdpend = CH_HOLD_PEND_INDEF;
else
p->ch_holdpend = CH_HOLD_PEND;
evch_evq_stop(p->ch_queue);
}
evch_dl_add(&eg->evch_list, (evch_dlelem_t *)p);
} else {
mutex_exit(&eg->evch_list_lock);
return (ENOENT);
}
}
mutex_enter(&p->ch_mutex);
if (p->ch_bindings >= p->ch_maxbinds) {
rv = ENOMEM;
goto errorexit;
}
bp = kmem_alloc(sizeof (evch_bind_t), KM_SLEEP);
bp->bd_channel = p;
bp->bd_sublst = NULL;
p->ch_bindings++;
rv = 0;
*scpp = bp;
errorexit:
mutex_exit(&p->ch_mutex);
mutex_exit(&eg->evch_list_lock);
return (rv);
}
static void
evch_chunbind(evch_bind_t *bp)
{
struct evch_globals *eg;
evch_chan_t *chp = bp->bd_channel;
eg = zone_getspecific(evch_zone_key, curproc->p_zone);
ASSERT(eg != NULL);
mutex_enter(&eg->evch_list_lock);
mutex_enter(&chp->ch_mutex);
ASSERT(chp->ch_bindings > 0);
chp->ch_bindings--;
kmem_free(bp, sizeof (evch_bind_t));
if (chp->ch_bindings == 0 && evch_dl_getnum(&chp->ch_subscr) == 0 &&
(chp->ch_nevents == 0 || chp->ch_holdpend != CH_HOLD_PEND_INDEF)) {
mutex_exit(&chp->ch_mutex);
evch_dl_del(&eg->evch_list, &chp->ch_link);
evch_evq_destroy(chp->ch_queue);
nvlist_free(chp->ch_propnvl);
mutex_destroy(&chp->ch_mutex);
mutex_destroy(&chp->ch_pubmx);
cv_destroy(&chp->ch_pubcv);
kmem_free(chp->ch_name, chp->ch_namelen);
kmem_free(chp, sizeof (evch_chan_t));
} else
mutex_exit(&chp->ch_mutex);
mutex_exit(&eg->evch_list_lock);
}
static int
wildcard_count(const char *class)
{
int count = 0;
char c;
if (class == NULL)
return (0);
while ((c = *class++) != '\0') {
if (c == '*')
count++;
}
return (count);
}
static int
evch_chsubscribe(evch_bind_t *bp, int dtype, const char *sid, const char *class,
void *dinfo, void *cookie, int flags, pid_t pid)
{
evch_chan_t *chp = bp->bd_channel;
evch_eventq_t *eqp = chp->ch_queue;
evch_subd_t *sdp;
evch_subd_t *esp;
int (*delivfkt)();
char *clb = NULL;
int clblen = 0;
char *subid;
int subidblen;
if (flags & ~(EVCH_SUB_KEEP | EVCH_SUB_DUMP))
return (EINVAL);
if (wildcard_count(class) > EVCH_WILDCARD_MAX)
return (EINVAL);
mutex_enter(&chp->ch_mutex);
if ((esp = (evch_subd_t *)evch_dl_search(&chp->ch_subscr,
evch_subidcmp, (char *)sid)) != NULL) {
int error = 0;
if ((flags & EVCH_SUB_KEEP) && (esp->sd_active == 0)) {
ASSERT(dtype == EVCH_DELDOOR);
esp->sd_subnxt = bp->bd_sublst;
bp->bd_sublst = esp;
esp->sd_pid = pid;
esp->sd_door = (door_handle_t)dinfo;
esp->sd_active++;
evch_evq_continue(esp->sd_queue);
} else {
error = EEXIST;
}
mutex_exit(&chp->ch_mutex);
return (error);
}
if (evch_dl_getnum(&chp->ch_subscr) >= chp->ch_maxsubscr) {
mutex_exit(&chp->ch_mutex);
return (ENOMEM);
}
if (flags & EVCH_SUB_DUMP && evch_dl_search(&chp->ch_subscr,
evch_dumpflgcmp, NULL) != NULL) {
mutex_exit(&chp->ch_mutex);
return (EINVAL);
}
if (class != NULL) {
clblen = strlen(class) + 1;
clb = kmem_alloc(clblen, KM_SLEEP);
bcopy(class, clb, clblen);
}
subidblen = strlen(sid) + 1;
subid = kmem_alloc(subidblen, KM_SLEEP);
bcopy(sid, subid, subidblen);
sdp = kmem_zalloc(sizeof (evch_subd_t), KM_SLEEP);
sdp->sd_queue = evch_evq_create();
sdp->sd_persist = flags & EVCH_SUB_KEEP ? 1 : 0;
sdp->sd_dump = flags & EVCH_SUB_DUMP ? 1 : 0;
sdp->sd_type = dtype;
sdp->sd_cbcookie = cookie;
sdp->sd_ident = subid;
if (dtype == EVCH_DELKERN) {
sdp->sd_callback = (kerndlv_f)dinfo;
delivfkt = evch_kern_deliver;
} else {
sdp->sd_door = (door_handle_t)dinfo;
delivfkt = evch_door_deliver;
}
sdp->sd_ssub =
evch_evq_sub(sdp->sd_queue, NULL, NULL, delivfkt, (void *)sdp);
sdp->sd_msub = evch_evq_sub(eqp, evch_class_filter, clb,
evch_subq_deliver, (void *)sdp);
sdp->sd_classname = clb;
sdp->sd_clnsize = clblen;
sdp->sd_pid = pid;
sdp->sd_active++;
sdp->sd_subnxt = bp->bd_sublst;
bp->bd_sublst = sdp;
evch_dl_add(&chp->ch_subscr, &sdp->sd_link);
if (chp->ch_holdpend && evch_dl_getnum(&chp->ch_subscr) == 1) {
evch_evq_continue(eqp);
}
mutex_exit(&chp->ch_mutex);
return (0);
}
static void
evch_chunsubscribe(evch_bind_t *bp, const char *sid, uint32_t flags)
{
evch_subd_t *sdp;
evch_subd_t *next;
evch_subd_t *prev;
evch_chan_t *chp = bp->bd_channel;
mutex_enter(&chp->ch_mutex);
if (chp->ch_holdpend) {
evch_evq_stop(chp->ch_queue);
}
prev = NULL;
for (sdp = bp->bd_sublst; sdp; sdp = next) {
if (sid == NULL || strcmp(sid, sdp->sd_ident) == 0) {
if (flags == 0 || sdp->sd_persist == 0) {
evch_evq_unsub(chp->ch_queue, sdp->sd_msub);
evch_evq_unsub(sdp->sd_queue, sdp->sd_ssub);
evch_evq_destroy(sdp->sd_queue);
evch_dl_del(&chp->ch_subscr, &sdp->sd_link);
kmem_free(sdp->sd_classname, sdp->sd_clnsize);
if (sdp->sd_type == EVCH_DELDOOR) {
door_ki_rele(sdp->sd_door);
}
next = sdp->sd_subnxt;
if (prev) {
prev->sd_subnxt = next;
} else {
bp->bd_sublst = next;
}
kmem_free(sdp->sd_ident,
strlen(sdp->sd_ident) + 1);
kmem_free(sdp, sizeof (evch_subd_t));
} else {
evch_evq_stop(sdp->sd_queue);
if (sdp->sd_type == EVCH_DELDOOR) {
door_ki_rele(sdp->sd_door);
}
sdp->sd_active--;
ASSERT(sdp->sd_active == 0);
next = sdp->sd_subnxt;
prev = sdp;
}
if (sid != NULL) {
break;
}
} else {
next = sdp->sd_subnxt;
prev = sdp;
}
}
if (!(chp->ch_holdpend && evch_dl_getnum(&chp->ch_subscr) == 0)) {
evch_evq_continue(chp->ch_queue);
}
mutex_exit(&chp->ch_mutex);
}
static int
evch_chpublish(evch_bind_t *bp, sysevent_impl_t *ev, int flags)
{
evch_chan_t *chp = bp->bd_channel;
DTRACE_SYSEVENT2(post, evch_bind_t *, bp, sysevent_impl_t *, ev);
mutex_enter(&chp->ch_pubmx);
if (chp->ch_nevents >= chp->ch_maxev) {
if (!(flags & EVCH_QWAIT)) {
evch_evq_evfree(ev);
mutex_exit(&chp->ch_pubmx);
return (EAGAIN);
} else {
while (chp->ch_nevents >= chp->ch_maxev) {
if (cv_wait_sig(&chp->ch_pubcv,
&chp->ch_pubmx) == 0) {
evch_evq_evfree(ev);
mutex_exit(&chp->ch_pubmx);
return (EINTR);
}
}
}
}
chp->ch_nevents++;
mutex_exit(&chp->ch_pubmx);
SE_TIME(ev) = gethrtime();
SE_SEQ(ev) = log_sysevent_new_id();
evch_evq_evadd_dest(ev, evch_destr_event, (void *)chp);
return (evch_evq_pub(chp->ch_queue, ev, flags) == 0 ? 0 : EAGAIN);
}
static int
evch_chgetnames(char *buf, size_t size)
{
struct evch_globals *eg;
int len = 0;
char *addr = buf;
int max = size;
evch_chan_t *chp;
eg = zone_getspecific(evch_zone_key, curproc->p_zone);
ASSERT(eg != NULL);
mutex_enter(&eg->evch_list_lock);
for (chp = evch_dl_next(&eg->evch_list, NULL); chp != NULL;
chp = evch_dl_next(&eg->evch_list, chp)) {
len += chp->ch_namelen;
if (len >= max) {
mutex_exit(&eg->evch_list_lock);
return (-1);
}
bcopy(chp->ch_name, addr, chp->ch_namelen);
addr += chp->ch_namelen;
}
mutex_exit(&eg->evch_list_lock);
addr[0] = 0;
return (len + 1);
}
static int
evch_chgetchdata(char *chname, void *buf, size_t size)
{
struct evch_globals *eg;
char *cpaddr;
int bufmax;
int buflen;
evch_chan_t *chp;
sev_chinfo_t *p = (sev_chinfo_t *)buf;
int chdlen;
evch_subd_t *sdp;
sev_subinfo_t *subp;
int idlen;
int len;
eg = zone_getspecific(evch_zone_key, curproc->p_zone);
ASSERT(eg != NULL);
mutex_enter(&eg->evch_list_lock);
chp = (evch_chan_t *)evch_dl_search(&eg->evch_list, evch_namecmp,
chname);
if (chp == NULL) {
mutex_exit(&eg->evch_list_lock);
return (-1);
}
chdlen = offsetof(sev_chinfo_t, cd_subinfo);
if (size < chdlen) {
mutex_exit(&eg->evch_list_lock);
return (0);
}
p->cd_version = 0;
p->cd_suboffs = chdlen;
p->cd_uid = chp->ch_uid;
p->cd_gid = chp->ch_gid;
p->cd_perms = 0;
p->cd_ctime = chp->ch_ctime;
p->cd_maxev = chp->ch_maxev;
p->cd_evhwm = EVCH_EVQ_HIGHWM(chp->ch_queue);
p->cd_nevents = EVCH_EVQ_EVCOUNT(chp->ch_queue);
p->cd_maxsub = chp->ch_maxsubscr;
p->cd_nsub = evch_dl_getnum(&chp->ch_subscr);
p->cd_maxbinds = chp->ch_maxbinds;
p->cd_nbinds = chp->ch_bindings;
p->cd_holdpend = chp->ch_holdpend;
p->cd_limev = evch_events_max;
cpaddr = (char *)p + chdlen;
bufmax = size - chdlen;
buflen = 0;
for (sdp = evch_dl_next(&chp->ch_subscr, NULL); sdp != NULL;
sdp = evch_dl_next(&chp->ch_subscr, sdp)) {
idlen = strlen(sdp->sd_ident) + 1;
len = SE_ALIGN(offsetof(sev_subinfo_t, sb_strings) + idlen +
sdp->sd_clnsize);
buflen += len;
if (buflen >= bufmax) {
mutex_exit(&eg->evch_list_lock);
return (0);
}
subp = (sev_subinfo_t *)cpaddr;
subp->sb_nextoff = len;
subp->sb_stroff = offsetof(sev_subinfo_t, sb_strings);
if (sdp->sd_classname) {
bcopy(sdp->sd_classname, subp->sb_strings + idlen,
sdp->sd_clnsize);
subp->sb_clnamoff = idlen;
} else {
subp->sb_clnamoff = idlen - 1;
}
subp->sb_pid = sdp->sd_pid;
subp->sb_nevents = EVCH_EVQ_EVCOUNT(sdp->sd_queue);
subp->sb_evhwm = EVCH_EVQ_HIGHWM(sdp->sd_queue);
subp->sb_persist = sdp->sd_persist;
subp->sb_status = evch_evq_status(sdp->sd_queue);
subp->sb_active = sdp->sd_active;
subp->sb_dump = sdp->sd_dump;
bcopy(sdp->sd_ident, subp->sb_strings, idlen);
cpaddr += len;
}
mutex_exit(&eg->evch_list_lock);
return (chdlen + buflen);
}
static void
evch_chsetpropnvl(evch_bind_t *bp, nvlist_t *nvl)
{
evch_chan_t *chp = bp->bd_channel;
mutex_enter(&chp->ch_mutex);
nvlist_free(chp->ch_propnvl);
chp->ch_propnvl = nvl;
chp->ch_propnvlgen++;
mutex_exit(&chp->ch_mutex);
}
static int
evch_chgetpropnvl(evch_bind_t *bp, nvlist_t **nvlp, int64_t *genp)
{
evch_chan_t *chp = bp->bd_channel;
int rc = 0;
mutex_enter(&chp->ch_mutex);
if (chp->ch_propnvl != NULL)
rc = (nvlist_dup(chp->ch_propnvl, nvlp, 0) == 0) ? 0 : ENOMEM;
else
*nvlp = NULL;
if (genp)
*genp = chp->ch_propnvlgen;
mutex_exit(&chp->ch_mutex);
if (rc != 0)
*nvlp = NULL;
return (rc);
}
static evch_chan_t *evch_chan;
static evch_eventq_t *evch_subq;
static sysevent_impl_t *evch_curev;
static evchanq_t *
evch_chrdevent_init(evch_chan_t *chp, char *subid)
{
evch_subd_t *sdp;
void *ev;
int pmqstat;
int psqstat;
evchanq_t *snp;
compare_f compfunc;
compfunc = subid == NULL ? evch_dumpflgcmp : evch_subidcmp;
if (panicstr != NULL) {
evch_chan = chp;
evch_subq = NULL;
evch_curev = NULL;
if ((sdp = (evch_subd_t *)evch_dl_search(&chp->ch_subscr,
compfunc, subid)) != NULL) {
evch_subq = sdp->sd_queue;
}
return (NULL);
}
mutex_enter(&chp->ch_mutex);
sdp = (evch_subd_t *)evch_dl_search(&chp->ch_subscr, compfunc, subid);
pmqstat = evch_evq_status(chp->ch_queue);
if (pmqstat == 0)
evch_evq_stop(chp->ch_queue);
psqstat = 0;
if (sdp != NULL) {
psqstat = evch_evq_status(sdp->sd_queue);
if (psqstat == 0)
evch_evq_stop(sdp->sd_queue);
}
snp = kmem_alloc(sizeof (evchanq_t), KM_SLEEP);
snp->sn_queue = evch_evq_create();
evch_evq_stop(snp->sn_queue);
if (sdp != NULL) {
ev = NULL;
while ((ev = evch_evq_evnext(sdp->sd_queue, ev)) != NULL) {
(void) evch_evq_pub(snp->sn_queue, ev, EVCH_SLEEP);
}
}
ev = NULL;
while ((ev = evch_evq_evnext(chp->ch_queue, ev)) != NULL) {
(void) evch_evq_pub(snp->sn_queue, ev, EVCH_SLEEP);
}
snp->sn_nxtev = NULL;
if (sdp != NULL && psqstat == 0)
evch_evq_continue(sdp->sd_queue);
if (pmqstat == 0)
evch_evq_continue(chp->ch_queue);
mutex_exit(&chp->ch_mutex);
return (snp);
}
static void
evch_chrdevent_fini(evchanq_t *snp)
{
if (snp != NULL) {
evch_evq_destroy(snp->sn_queue);
kmem_free(snp, sizeof (evchanq_t));
}
}
static sysevent_impl_t *
evch_chgetnextev(evchanq_t *snp)
{
if (panicstr != NULL) {
if (evch_chan == NULL)
return (NULL);
if (evch_subq != NULL) {
if ((evch_curev = (sysevent_impl_t *)
evch_evq_evnext(evch_subq, evch_curev)) != NULL) {
return (evch_curev);
} else {
evch_subq = NULL;
}
}
if ((evch_curev = (sysevent_impl_t *)
evch_evq_evnext(evch_chan->ch_queue, evch_curev)) ==
NULL) {
evch_chan = NULL;
}
return (evch_curev);
}
ASSERT(snp != NULL);
snp->sn_nxtev = (sysevent_impl_t *)evch_evq_evnext(snp->sn_queue,
snp->sn_nxtev);
return (snp->sn_nxtev);
}
static sysevent_impl_t *
sysevent_evc_alloc(const char *class, const char *subclass, const char *pub,
size_t pub_sz, size_t atsz, uint32_t flag)
{
int payload_sz;
int class_sz, subclass_sz;
int aligned_class_sz, aligned_subclass_sz, aligned_pub_sz;
sysevent_impl_t *ev;
class_sz = strlen(class) + 1;
subclass_sz = strlen(subclass) + 1;
ASSERT((class_sz <= MAX_CLASS_LEN) && (subclass_sz <=
MAX_SUBCLASS_LEN) && (pub_sz <= MAX_PUB_LEN));
aligned_class_sz = SE_ALIGN(class_sz);
aligned_subclass_sz = SE_ALIGN(subclass_sz);
aligned_pub_sz = SE_ALIGN(pub_sz);
payload_sz = (aligned_class_sz - sizeof (uint64_t)) +
(aligned_subclass_sz - sizeof (uint64_t)) +
(aligned_pub_sz - sizeof (uint64_t)) - sizeof (uint64_t) +
atsz;
if ((ev = evch_evq_evzalloc(sizeof (sysevent_impl_t) +
payload_sz, flag)) == NULL) {
return (NULL);
}
SE_VERSION(ev) = SYS_EVENT_VERSION;
bcopy(class, SE_CLASS_NAME(ev), class_sz);
SE_SUBCLASS_OFF(ev) = SE_ALIGN(offsetof(sysevent_impl_t,
se_class_name)) + aligned_class_sz;
bcopy(subclass, SE_SUBCLASS_NAME(ev), subclass_sz);
SE_PUB_OFF(ev) = SE_SUBCLASS_OFF(ev) + aligned_subclass_sz;
bcopy(pub, SE_PUB_NAME(ev), pub_sz);
SE_ATTR_PTR(ev) = (uint64_t)0;
SE_PAYLOAD_SZ(ev) = payload_sz;
return (ev);
}
void
sysevent_evc_init()
{
evch_chinit();
}
void
sysevent_evc_thrinit()
{
evch_chinitthr();
}
int
sysevent_evc_bind(const char *ch_name, evchan_t **scpp, uint32_t flags)
{
ASSERT(ch_name != NULL && scpp != NULL);
ASSERT((flags & ~EVCH_B_FLAGS) == 0);
return (evch_chbind(ch_name, (evch_bind_t **)scpp, flags));
}
int
sysevent_evc_unbind(evchan_t *scp)
{
evch_bind_t *bp = (evch_bind_t *)scp;
ASSERT(scp != NULL);
evch_chunsubscribe(bp, NULL, 0);
evch_chunbind(bp);
return (0);
}
int
sysevent_evc_subscribe(evchan_t *scp, const char *sid, const char *class,
int (*callb)(sysevent_t *ev, void *cookie),
void *cookie, uint32_t flags)
{
ASSERT(scp != NULL && sid != NULL && class != NULL && callb != NULL);
ASSERT(flags == 0);
if (strlen(sid) > MAX_SUBID_LEN) {
return (EINVAL);
}
if (strcmp(class, EC_ALL) == 0) {
class = NULL;
}
return (evch_chsubscribe((evch_bind_t *)scp, EVCH_DELKERN, sid, class,
(void *)callb, cookie, 0, 0));
}
int
sysevent_evc_unsubscribe(evchan_t *scp, const char *sid)
{
ASSERT(scp != NULL && sid != NULL);
if (strcmp(sid, EVCH_ALLSUB) == 0) {
sid = NULL;
}
evch_chunsubscribe((evch_bind_t *)scp, sid, 0);
return (0);
}
int
sysevent_evc_publish(evchan_t *scp, const char *class, const char *subclass,
const char *vendor, const char *pubs, nvlist_t *attr, uint32_t flags)
{
sysevent_impl_t *evp;
char pub[MAX_PUB_LEN];
int pub_sz;
int km_flags;
size_t asz = 0;
uint64_t attr_offset;
caddr_t patt;
int err;
ASSERT(scp != NULL && class != NULL && subclass != NULL &&
vendor != NULL && pubs != NULL);
ASSERT((flags & ~(EVCH_SLEEP | EVCH_NOSLEEP | EVCH_TRYHARD |
EVCH_QWAIT)) == 0);
km_flags = flags & (EVCH_SLEEP | EVCH_NOSLEEP | EVCH_TRYHARD);
ASSERT(km_flags == EVCH_SLEEP || km_flags == EVCH_NOSLEEP ||
km_flags == EVCH_TRYHARD);
pub_sz = snprintf(pub, MAX_PUB_LEN, "%s:kern:%s", vendor, pubs) + 1;
if (pub_sz > MAX_PUB_LEN)
return (EINVAL);
if (attr != NULL) {
if ((err = nvlist_size(attr, &asz, NV_ENCODE_NATIVE)) != 0) {
return (err);
}
}
evp = sysevent_evc_alloc(class, subclass, pub, pub_sz, asz, km_flags);
if (evp == NULL) {
return (ENOMEM);
}
if (attr != NULL) {
attr_offset = SE_ATTR_OFF(evp);
patt = (caddr_t)evp + attr_offset;
err = nvlist_pack(attr, &patt, &asz, NV_ENCODE_NATIVE,
km_flags & EVCH_SLEEP ? KM_SLEEP : KM_NOSLEEP);
ASSERT(err != ENOMEM);
if (err != 0) {
return (EINVAL);
}
evp->seh_attr_off = attr_offset;
SE_FLAG(evp) = SE_PACKED_BUF;
}
return (evch_chpublish((evch_bind_t *)scp, evp, flags));
}
int
sysevent_evc_control(evchan_t *scp, int cmd, ...)
{
va_list ap;
evch_chan_t *chp;
uint32_t *chlenp;
uint32_t chlen;
uint32_t ochlen;
int rc = 0;
if (scp == NULL) {
return (EINVAL);
}
chp = ((evch_bind_t *)scp)->bd_channel;
va_start(ap, cmd);
mutex_enter(&chp->ch_mutex);
switch (cmd) {
case EVCH_GET_CHAN_LEN:
chlenp = va_arg(ap, uint32_t *);
*chlenp = chp->ch_maxev;
break;
case EVCH_SET_CHAN_LEN:
chlen = va_arg(ap, uint32_t);
ochlen = chp->ch_maxev;
chp->ch_maxev = min(chlen, evch_events_max);
if (ochlen < chp->ch_maxev) {
cv_signal(&chp->ch_pubcv);
}
break;
case EVCH_GET_CHAN_LEN_MAX:
*va_arg(ap, uint32_t *) = evch_events_max;
break;
default:
rc = EINVAL;
}
mutex_exit(&chp->ch_mutex);
va_end(ap);
return (rc);
}
int
sysevent_evc_setpropnvl(evchan_t *scp, nvlist_t *nvl)
{
nvlist_t *nvlcp = nvl;
if (nvl != NULL && nvlist_dup(nvl, &nvlcp, 0) != 0)
return (ENOMEM);
evch_chsetpropnvl((evch_bind_t *)scp, nvlcp);
return (0);
}
int
sysevent_evc_getpropnvl(evchan_t *scp, nvlist_t **nvlp)
{
return (evch_chgetpropnvl((evch_bind_t *)scp, nvlp, NULL));
}
evchanq_t *
sysevent_evc_walk_init(evchan_t *scp, char *subscr)
{
if (panicstr != NULL && scp == NULL)
return (NULL);
ASSERT(scp != NULL);
return (evch_chrdevent_init(((evch_bind_t *)scp)->bd_channel, subscr));
}
sysevent_t *
sysevent_evc_walk_step(evchanq_t *evcq)
{
return ((sysevent_t *)evch_chgetnextev(evcq));
}
void
sysevent_evc_walk_fini(evchanq_t *evcq)
{
evch_chrdevent_fini(evcq);
}
char *
sysevent_evc_event_attr(sysevent_t *ev, size_t *plsize)
{
char *attrp;
size_t aoff;
size_t asz;
aoff = SE_ATTR_OFF(ev);
attrp = (char *)ev + aoff;
asz = *plsize = SE_SIZE(ev) - aoff;
return (asz ? attrp : NULL);
}
char *
sysevent_get_class_name(sysevent_t *ev)
{
return (SE_CLASS_NAME(ev));
}
char *
sysevent_get_subclass_name(sysevent_t *ev)
{
return (SE_SUBCLASS_NAME(ev));
}
uint64_t
sysevent_get_seq(sysevent_t *ev)
{
return (SE_SEQ(ev));
}
void
sysevent_get_time(sysevent_t *ev, hrtime_t *etime)
{
*etime = SE_TIME(ev);
}
size_t
sysevent_get_size(sysevent_t *ev)
{
return ((size_t)SE_SIZE(ev));
}
char *
sysevent_get_pub(sysevent_t *ev)
{
return (SE_PUB_NAME(ev));
}
int
sysevent_get_attr_list(sysevent_t *ev, nvlist_t **nvlist)
{
int error;
caddr_t attr;
size_t attr_len;
uint64_t attr_offset;
*nvlist = NULL;
if (SE_FLAG(ev) != SE_PACKED_BUF) {
return (EINVAL);
}
attr_offset = SE_ATTR_OFF(ev);
if (SE_SIZE(ev) == attr_offset) {
return (EINVAL);
}
attr = (caddr_t)ev + attr_offset;
attr_len = SE_SIZE(ev) - attr_offset;
if ((error = nvlist_unpack(attr, attr_len, nvlist, 0)) != 0) {
error = error != ENOMEM ? EINVAL : error;
return (error);
}
return (0);
}
evchan_t *
evch_usrchanopen(const char *name, uint32_t flags, int *err)
{
evch_bind_t *bp = NULL;
*err = evch_chbind(name, &bp, flags);
return ((evchan_t *)bp);
}
void
evch_usrchanclose(evchan_t *cbp)
{
evch_chunbind((evch_bind_t *)cbp);
}
sysevent_impl_t *
evch_usrallocev(size_t evsize, uint32_t flags)
{
return ((sysevent_impl_t *)evch_evq_evzalloc(evsize, flags));
}
void
evch_usrfreeev(sysevent_impl_t *ev)
{
evch_evq_evfree((void *)ev);
}
int
evch_usrpostevent(evchan_t *bp, sysevent_impl_t *ev, uint32_t flags)
{
return (evch_chpublish((evch_bind_t *)bp, ev, flags));
}
int
evch_usrsubscribe(evchan_t *bp, const char *sid, const char *class,
int d, uint32_t flags)
{
door_handle_t dh = door_ki_lookup(d);
int rv;
if (dh == NULL) {
return (EINVAL);
}
if ((rv = evch_chsubscribe((evch_bind_t *)bp, EVCH_DELDOOR, sid, class,
(void *)dh, NULL, flags, curproc->p_pid)) != 0) {
door_ki_rele(dh);
}
return (rv);
}
void
evch_usrunsubscribe(evchan_t *bp, const char *subid, uint32_t flags)
{
evch_chunsubscribe((evch_bind_t *)bp, subid, flags);
}
int
evch_usrcontrol_set(evchan_t *bp, int cmd, uint32_t value)
{
evch_chan_t *chp = ((evch_bind_t *)bp)->bd_channel;
uid_t uid = crgetuid(curthread->t_cred);
int rc = 0;
mutex_enter(&chp->ch_mutex);
switch (cmd) {
case EVCH_SET_CHAN_LEN:
if (uid && uid != chp->ch_uid) {
rc = EACCES;
break;
}
chp->ch_maxev = min(value, evch_events_max);
break;
default:
rc = EINVAL;
}
mutex_exit(&chp->ch_mutex);
return (rc);
}
int
evch_usrcontrol_get(evchan_t *bp, int cmd, uint32_t *value)
{
evch_chan_t *chp = ((evch_bind_t *)bp)->bd_channel;
int rc = 0;
mutex_enter(&chp->ch_mutex);
switch (cmd) {
case EVCH_GET_CHAN_LEN:
*value = chp->ch_maxev;
break;
case EVCH_GET_CHAN_LEN_MAX:
*value = evch_events_max;
break;
default:
rc = EINVAL;
}
mutex_exit(&chp->ch_mutex);
return (rc);
}
int
evch_usrgetchnames(char *buf, size_t size)
{
return (evch_chgetnames(buf, size));
}
int
evch_usrgetchdata(char *chname, void *buf, size_t size)
{
return (evch_chgetchdata(chname, buf, size));
}
void
evch_usrsetpropnvl(evchan_t *bp, nvlist_t *nvl)
{
evch_chsetpropnvl((evch_bind_t *)bp, nvl);
}
int
evch_usrgetpropnvl(evchan_t *bp, nvlist_t **nvlp, int64_t *genp)
{
return (evch_chgetpropnvl((evch_bind_t *)bp, nvlp, genp));
}