getdevpath.3: Fix reference and sort.
[dragonfly.git] / sys / kern / lwkt_caps.c
blob514518a088e759400d9f49296d5c4488deae2189
1 /*
2 * Copyright (c) 2003,2004 The DragonFly Project. All rights reserved.
3 *
4 * This code is derived from software contributed to The DragonFly Project
5 * by Matthew Dillon <dillon@backplane.com>
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
16 * distribution.
17 * 3. Neither the name of The DragonFly Project nor the names of its
18 * contributors may be used to endorse or promote products derived
19 * from this software without specific, prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
34 * $DragonFly: src/sys/kern/lwkt_caps.c,v 1.13 2007/02/26 21:41:08 corecode Exp $
38 * This module implements the DragonFly LWKT IPC rendezvous and message
39 * passing API which operates between userland processes, between userland
40 * threads, and between userland processes and kernel threads. This API
41 * is known as the CAPS interface.
43 * Generally speaking this module abstracts the LWKT message port interface
44 * into userland Clients and Servers rendezvous through ports named
45 * by or wildcarded by (name,uid,gid). The kernel provides system calls
46 * which may be assigned to the mp_* fields in a userland-supplied
47 * kernel-managed port, and a registration interface which associates an
48 * upcall with a userland port. The kernel tracks authentication information
49 * and deals with connection failures by automatically replying to unreplied
50 * messages.
52 * From the userland perspective a client/server connection involves two
53 * message ports on the client and two message ports on the server.
56 #include <sys/param.h>
57 #include <sys/systm.h>
58 #include <sys/kernel.h>
59 #include <sys/sysproto.h>
60 #include <sys/malloc.h>
61 #include <sys/proc.h>
62 #include <sys/ucred.h>
63 #include <sys/caps.h>
64 #include <sys/sysctl.h>
65 #include <vm/vm.h>
66 #include <vm/vm_extern.h>
68 static int caps_process_msg(caps_kinfo_t caps, caps_kmsg_t msg, struct caps_sys_get_args *uap);
69 static void caps_free(caps_kinfo_t caps);
70 static void caps_free_msg(caps_kmsg_t msg);
71 static int caps_name_check(const char *name, size_t len);
72 static caps_kinfo_t caps_free_msg_mcaps(caps_kmsg_t msg);
73 static caps_kinfo_t kern_caps_sys_service(const char *name, uid_t uid,
74 gid_t gid, struct ucred *cred,
75 int flags, int *error);
76 static caps_kinfo_t kern_caps_sys_client(const char *name, uid_t uid,
77 gid_t gid, struct ucred *cred, int flags, int *error);
79 #define CAPS_HSIZE 64
80 #define CAPS_HMASK (CAPS_HSIZE - 1)
82 static caps_kinfo_t caps_hash_ary[CAPS_HSIZE];
83 static int caps_waitsvc;
85 MALLOC_DEFINE(M_CAPS, "caps", "caps IPC messaging");
87 static int caps_enabled;
88 SYSCTL_INT(_kern, OID_AUTO, caps_enabled,
89 CTLFLAG_RW, &caps_enabled, 0, "Enable CAPS");
91 /************************************************************************
92 * INLINE SUPPORT FUNCTIONS *
93 ************************************************************************/
95 static __inline
96 struct caps_kinfo **
97 caps_hash(const char *name, int len)
99 int hv = 0x7123F4B3;
101 while (--len >= 0)
102 hv = (hv << 5) ^ name[len] ^ (hv >> 23);
103 return(&caps_hash_ary[(hv ^ (hv >> 16)) & CAPS_HMASK]);
106 static __inline
107 void
108 caps_hold(caps_kinfo_t caps)
110 ++caps->ci_refs;
113 static __inline
114 void
115 caps_drop(caps_kinfo_t caps)
117 if (--caps->ci_refs == 0)
118 caps_free(caps);
121 /************************************************************************
122 * STATIC SUPPORT FUNCTIONS *
123 ************************************************************************/
125 static
126 caps_kinfo_t
127 caps_find(const char *name, int len, uid_t uid, gid_t gid)
129 caps_kinfo_t caps;
130 struct caps_kinfo **chash;
132 chash = caps_hash(name, len);
133 for (caps = *chash; caps; caps = caps->ci_hnext) {
134 if ((uid == (uid_t)-1 || uid == caps->ci_uid) &&
135 (gid == (gid_t)-1 || gid == caps->ci_gid) &&
136 len == caps->ci_namelen &&
137 bcmp(name, caps->ci_name, len) == 0
139 caps_hold(caps);
140 break;
143 return(caps);
146 static
147 caps_kinfo_t
148 caps_find_id(thread_t td, int id)
150 caps_kinfo_t caps;
152 for (caps = td->td_caps; caps; caps = caps->ci_tdnext) {
153 if (caps->ci_id == id) {
154 caps_hold(caps);
155 break;
158 return(caps);
161 static
162 caps_kinfo_t
163 caps_alloc(thread_t td, const char *name, int len, uid_t uid, gid_t gid,
164 int flags, caps_type_t type)
166 struct caps_kinfo **chash;
167 caps_kinfo_t caps;
168 caps_kinfo_t ctmp;
170 caps = kmalloc(offsetof(struct caps_kinfo, ci_name[len+1]),
171 M_CAPS, M_WAITOK|M_ZERO);
172 TAILQ_INIT(&caps->ci_msgpendq);
173 TAILQ_INIT(&caps->ci_msguserq);
174 caps->ci_uid = uid; /* -1 == not registered for uid search */
175 caps->ci_gid = gid; /* -1 == not registered for gid search */
176 caps->ci_type = type;
177 caps->ci_refs = 1; /* CAPKF_TDLIST reference */
178 caps->ci_namelen = len;
179 caps->ci_flags = flags;
180 bcopy(name, caps->ci_name, len + 1);
181 if (type == CAPT_SERVICE) {
182 chash = caps_hash(caps->ci_name, len);
183 caps->ci_hnext = *chash;
184 *chash = caps;
185 caps->ci_flags |= CAPKF_HLIST;
187 if (td->td_caps) {
188 caps->ci_id = td->td_caps->ci_id + 1;
189 if (caps->ci_id < 0) {
191 * It is virtually impossible for this case to occur.
193 caps->ci_id = 1;
194 while ((ctmp = caps_find_id(td, caps->ci_id)) != NULL) {
195 caps_drop(ctmp);
196 ++caps->ci_id;
199 } else {
200 caps->ci_id = 1;
202 caps->ci_flags |= CAPKF_TDLIST;
203 caps->ci_tdnext = td->td_caps;
204 caps->ci_td = td;
205 td->td_caps = caps;
206 return(caps);
209 static
210 caps_kmsg_t
211 caps_alloc_msg(caps_kinfo_t caps)
213 caps_kmsg_t msg;
215 msg = kmalloc(sizeof(struct caps_kmsg), M_CAPS, M_WAITOK|M_ZERO);
216 msg->km_msgid.c_id = (off_t)(uintptr_t)msg;
217 return(msg);
220 static
221 caps_kmsg_t
222 caps_find_msg(caps_kinfo_t caps, off_t msgid)
224 caps_kmsg_t msg;
226 TAILQ_FOREACH(msg, &caps->ci_msguserq, km_node) {
227 if (msg->km_msgid.c_id == msgid)
228 return(msg);
230 TAILQ_FOREACH(msg, &caps->ci_msgpendq, km_node) {
231 if (msg->km_msgid.c_id == msgid)
232 return(msg);
234 return(NULL);
237 static
238 caps_kinfo_t
239 caps_load_ccr(caps_kinfo_t caps, caps_kmsg_t msg, struct proc *p,
240 void *udata, int ubytes)
242 int i;
243 struct ucred *cr = p->p_ucred;
244 caps_kinfo_t rcaps;
247 * replace km_mcaps with new VM state, return the old km_mcaps. The
248 * caller is expected to drop the rcaps ref count on return so we do
249 * not do it ourselves.
251 rcaps = caps_free_msg_mcaps(msg); /* can be NULL */
252 caps_hold(caps);
253 msg->km_mcaps = caps;
254 xio_init_ubuf(&msg->km_xio, udata, ubytes, XIOF_READ);
256 msg->km_ccr.pid = p ? p->p_pid : -1;
257 msg->km_ccr.uid = cr->cr_ruid;
258 msg->km_ccr.euid = cr->cr_uid;
259 msg->km_ccr.gid = cr->cr_rgid;
260 msg->km_ccr.ngroups = MIN(cr->cr_ngroups, CAPS_MAXGROUPS);
261 for (i = 0; i < msg->km_ccr.ngroups; ++i)
262 msg->km_ccr.groups[i] = cr->cr_groups[i];
263 return(rcaps);
266 static void
267 caps_dequeue_msg(caps_kinfo_t caps, caps_kmsg_t msg)
269 if (msg->km_flags & CAPKMF_ONUSERQ)
270 TAILQ_REMOVE(&caps->ci_msguserq, msg, km_node);
271 if (msg->km_flags & CAPKMF_ONPENDQ)
272 TAILQ_REMOVE(&caps->ci_msgpendq, msg, km_node);
273 msg->km_flags &= ~(CAPKMF_ONPENDQ|CAPKMF_ONUSERQ);
276 static void
277 caps_put_msg(caps_kinfo_t caps, caps_kmsg_t msg, caps_msg_state_t state)
279 KKASSERT((msg->km_flags & (CAPKMF_ONUSERQ|CAPKMF_ONPENDQ)) == 0);
281 msg->km_flags |= CAPKMF_ONPENDQ;
282 msg->km_flags &= ~CAPKMF_PEEKED;
283 msg->km_state = state;
284 TAILQ_INSERT_TAIL(&caps->ci_msgpendq, msg, km_node);
287 * Instead of waking up the service for both new messages and disposals,
288 * just wakeup the service for new messages and it will process the
289 * previous disposal in the same loop, reducing the number of context
290 * switches required to run an IPC.
292 if (state != CAPMS_DISPOSE)
293 wakeup(caps);
294 caps_drop(caps);
298 * caps_free_msg_mcaps()
300 static
301 caps_kinfo_t
302 caps_free_msg_mcaps(caps_kmsg_t msg)
304 caps_kinfo_t mcaps;
306 mcaps = msg->km_mcaps; /* may be NULL */
307 msg->km_mcaps = NULL;
308 if (msg->km_xio.xio_npages)
309 xio_release(&msg->km_xio);
310 return(mcaps);
314 * caps_free_msg()
316 * Free a caps placeholder message. The message must not be on any queues.
318 static void
319 caps_free_msg(caps_kmsg_t msg)
321 caps_kinfo_t rcaps;
323 if ((rcaps = caps_free_msg_mcaps(msg)) != NULL)
324 caps_drop(rcaps);
325 kfree(msg, M_CAPS);
329 * Validate the service name
331 static int
332 caps_name_check(const char *name, size_t len)
334 size_t i;
335 char c;
337 for (i = len - 1; i >= 0; --i) {
338 c = name[i];
339 if (c >= '0' && c <= '9')
340 continue;
341 if (c >= 'a' && c <= 'z')
342 continue;
343 if (c >= 'A' && c <= 'Z')
344 continue;
345 if (c == '_' || c == '.')
346 continue;
347 return(EINVAL);
349 return(0);
353 * caps_term()
355 * Terminate portions of a caps info structure. This is used to close
356 * an end-point or to flush particular messages on an end-point.
358 * This function should not be called with CAPKF_TDLIST unless the caller
359 * has an additional hold on the caps structure.
361 static void
362 caps_term(caps_kinfo_t caps, int flags, caps_kinfo_t cflush)
364 struct caps_kinfo **scan;
365 caps_kmsg_t msg;
367 if (flags & CAPKF_TDLIST)
368 caps->ci_flags |= CAPKF_CLOSED;
370 if (flags & CAPKF_FLUSH) {
371 int mflags;
372 struct caps_kmsg_queue tmpuserq;
373 struct caps_kmsg_queue tmppendq;
374 caps_kinfo_t rcaps;
376 TAILQ_INIT(&tmpuserq);
377 TAILQ_INIT(&tmppendq);
379 while ((msg = TAILQ_FIRST(&caps->ci_msgpendq)) != NULL ||
380 (msg = TAILQ_FIRST(&caps->ci_msguserq)) != NULL
382 mflags = msg->km_flags & (CAPKMF_ONUSERQ|CAPKMF_ONPENDQ);
383 caps_dequeue_msg(caps, msg);
385 if (cflush && msg->km_mcaps != cflush) {
386 if (mflags & CAPKMF_ONUSERQ)
387 TAILQ_INSERT_TAIL(&tmpuserq, msg, km_node);
388 else
389 TAILQ_INSERT_TAIL(&tmppendq, msg, km_node);
390 } else {
392 * Dispose of the message. If the received message is a
393 * request we must reply it. If the received message is
394 * a reply we must return it for disposal. If the
395 * received message is a disposal request we simply free it.
397 switch(msg->km_state) {
398 case CAPMS_REQUEST:
399 case CAPMS_REQUEST_RETRY:
400 rcaps = caps_load_ccr(caps, msg, curproc, NULL, 0);
401 if (rcaps->ci_flags & CAPKF_CLOSED) {
403 * can't reply, if we never read the message (its on
404 * the pending queue), or if we are closed ourselves,
405 * we can just free the message. Otherwise we have
406 * to send ourselves a disposal request (multi-threaded
407 * services have to deal with disposal requests for
408 * messages that might be in progress).
410 if ((caps->ci_flags & CAPKF_CLOSED) ||
411 (mflags & CAPKMF_ONPENDQ)
413 caps_free_msg(msg);
414 caps_drop(rcaps);
415 } else {
416 caps_drop(rcaps);
417 caps_hold(caps); /* for message */
418 caps_put_msg(caps, msg, CAPMS_DISPOSE);
420 } else {
422 * auto-reply to the originator. rcaps already
423 * has a dangling hold so we do not have to hold it
424 * again.
426 caps_put_msg(rcaps, msg, CAPMS_REPLY);
428 break;
429 case CAPMS_REPLY:
430 case CAPMS_REPLY_RETRY:
431 rcaps = caps_load_ccr(caps, msg, curproc, NULL, 0);
432 if (caps == rcaps || (rcaps->ci_flags & CAPKF_CLOSED)) {
433 caps_free_msg(msg); /* degenerate disposal case */
434 caps_drop(rcaps);
435 } else {
436 caps_put_msg(rcaps, msg, CAPMS_DISPOSE);
438 break;
439 case CAPMS_DISPOSE:
440 caps_free_msg(msg);
441 break;
445 while ((msg = TAILQ_FIRST(&tmpuserq)) != NULL) {
446 TAILQ_REMOVE(&tmpuserq, msg, km_node);
447 TAILQ_INSERT_TAIL(&caps->ci_msguserq, msg, km_node);
448 msg->km_flags |= CAPKMF_ONUSERQ;
450 while ((msg = TAILQ_FIRST(&tmppendq)) != NULL) {
451 TAILQ_REMOVE(&tmppendq, msg, km_node);
452 TAILQ_INSERT_TAIL(&caps->ci_msgpendq, msg, km_node);
453 msg->km_flags |= CAPKMF_ONPENDQ;
456 if ((flags & CAPKF_HLIST) && (caps->ci_flags & CAPKF_HLIST)) {
457 for (scan = caps_hash(caps->ci_name, caps->ci_namelen);
458 *scan != caps;
459 scan = &(*scan)->ci_hnext
461 KKASSERT(*scan != NULL);
463 *scan = caps->ci_hnext;
464 caps->ci_hnext = (void *)-1;
465 caps->ci_flags &= ~CAPKF_HLIST;
467 if ((flags & CAPKF_TDLIST) && (caps->ci_flags & CAPKF_TDLIST)) {
468 for (scan = &caps->ci_td->td_caps;
469 *scan != caps;
470 scan = &(*scan)->ci_tdnext
472 KKASSERT(*scan != NULL);
474 *scan = caps->ci_tdnext;
475 caps->ci_flags &= ~CAPKF_TDLIST;
476 caps->ci_tdnext = (void *)-1;
477 caps->ci_td = NULL;
478 caps_drop(caps);
480 if ((flags & CAPKF_RCAPS) && (caps->ci_flags & CAPKF_RCAPS)) {
481 caps_kinfo_t ctmp;
483 caps->ci_flags &= ~CAPKF_RCAPS;
484 if ((ctmp = caps->ci_rcaps)) {
485 caps->ci_rcaps = NULL;
486 caps_term(ctmp, CAPKF_FLUSH, caps);
487 caps_drop(ctmp);
492 static void
493 caps_free(caps_kinfo_t caps)
495 KKASSERT(TAILQ_EMPTY(&caps->ci_msgpendq));
496 KKASSERT(TAILQ_EMPTY(&caps->ci_msguserq));
497 KKASSERT((caps->ci_flags & (CAPKF_HLIST|CAPKF_TDLIST)) == 0);
498 kfree(caps, M_CAPS);
501 /************************************************************************
502 * PROCESS SUPPORT FUNCTIONS *
503 ************************************************************************/
506 * Create dummy entries in p2 so we can return the appropriate
507 * error code. Robust userland code will check the error for a
508 * forked condition and reforge the connection.
510 void
511 caps_fork(struct thread *td1, struct thread *td2)
513 caps_kinfo_t caps1;
514 caps_kinfo_t caps2;
517 * Create dummy entries with the same id's as the originals. Note
518 * that service entries are not re-added to the hash table. The
519 * dummy entries return an ENOTCONN error allowing userland code to
520 * detect that a fork occured. Userland must reconnect to the service.
522 for (caps1 = td1->td_caps; caps1; caps1 = caps1->ci_tdnext) {
523 if (caps1->ci_flags & CAPF_NOFORK)
524 continue;
525 caps2 = caps_alloc(td2,
526 caps1->ci_name, caps1->ci_namelen,
527 caps1->ci_uid, caps1->ci_gid,
528 caps1->ci_flags & CAPF_UFLAGS, CAPT_FORKED);
529 caps2->ci_id = caps1->ci_id;
533 * Reverse the list order to maintain highest-id-first
535 caps2 = td2->td_caps;
536 td2->td_caps = NULL;
537 while (caps2) {
538 caps1 = caps2->ci_tdnext;
539 caps2->ci_tdnext = td2->td_caps;
540 td2->td_caps = caps2;
541 caps2 = caps1;
545 void
546 caps_exit(struct thread *td)
548 caps_kinfo_t caps;
550 while ((caps = td->td_caps) != NULL) {
551 caps_hold(caps);
552 caps_term(caps, CAPKF_TDLIST|CAPKF_HLIST|CAPKF_FLUSH|CAPKF_RCAPS, NULL);
553 caps_drop(caps);
557 /************************************************************************
558 * SYSTEM CALLS *
559 ************************************************************************/
562 * caps_sys_service(name, uid, gid, upcid, flags);
564 * Create an IPC service using the specified name, uid, gid, and flags.
565 * Either uid or gid can be -1, but not both. The port identifier is
566 * returned.
568 * upcid can either be an upcall or a kqueue identifier (XXX)
571 sys_caps_sys_service(struct caps_sys_service_args *uap)
573 struct ucred *cred = curproc->p_ucred;
574 char name[CAPS_MAXNAMELEN];
575 caps_kinfo_t caps;
576 size_t len;
577 int error;
579 if (caps_enabled == 0)
580 return(EOPNOTSUPP);
581 if ((error = copyinstr(uap->name, name, CAPS_MAXNAMELEN, &len)) != 0)
582 return(error);
583 if ((ssize_t)--len <= 0)
584 return(EINVAL);
585 if ((error = caps_name_check(name, len)) != 0)
586 return(error);
588 caps = kern_caps_sys_service(name, uap->uid, uap->gid, cred,
589 uap->flags & CAPF_UFLAGS, &error);
590 if (caps)
591 uap->sysmsg_result = caps->ci_id;
592 return(error);
596 * caps_sys_client(name, uid, gid, upcid, flags);
598 * Create an IPC client connected to the specified service. Either uid or gid
599 * may be -1, indicating a wildcard, but not both. The port identifier is
600 * returned.
602 * upcid can either be an upcall or a kqueue identifier (XXX)
605 sys_caps_sys_client(struct caps_sys_client_args *uap)
607 struct ucred *cred = curproc->p_ucred;
608 char name[CAPS_MAXNAMELEN];
609 caps_kinfo_t caps;
610 size_t len;
611 int error;
613 if (caps_enabled == 0)
614 return(EOPNOTSUPP);
615 if ((error = copyinstr(uap->name, name, CAPS_MAXNAMELEN, &len)) != 0)
616 return(error);
617 if ((ssize_t)--len <= 0)
618 return(EINVAL);
619 if ((error = caps_name_check(name, len)) != 0)
620 return(error);
622 caps = kern_caps_sys_client(name, uap->uid, uap->gid, cred,
623 uap->flags & CAPF_UFLAGS, &error);
624 if (caps)
625 uap->sysmsg_result = caps->ci_id;
626 return(error);
630 sys_caps_sys_close(struct caps_sys_close_args *uap)
632 caps_kinfo_t caps;
634 if ((caps = caps_find_id(curthread, uap->portid)) == NULL)
635 return(EINVAL);
636 caps_term(caps, CAPKF_TDLIST|CAPKF_HLIST|CAPKF_FLUSH|CAPKF_RCAPS, NULL);
637 caps_drop(caps);
638 return(0);
642 sys_caps_sys_setgen(struct caps_sys_setgen_args *uap)
644 caps_kinfo_t caps;
645 int error;
647 if ((caps = caps_find_id(curthread, uap->portid)) == NULL)
648 return(EINVAL);
649 if (caps->ci_type == CAPT_FORKED) {
650 error = ENOTCONN;
651 } else {
652 caps->ci_gen = uap->gen;
653 error = 0;
655 caps_drop(caps);
656 return(error);
660 sys_caps_sys_getgen(struct caps_sys_getgen_args *uap)
662 caps_kinfo_t caps;
663 int error;
665 if ((caps = caps_find_id(curthread, uap->portid)) == NULL)
666 return(EINVAL);
667 if (caps->ci_type == CAPT_FORKED) {
668 error = ENOTCONN;
669 } else if (caps->ci_rcaps == NULL) {
670 error = EINVAL;
671 } else {
672 uap->sysmsg_result64 = caps->ci_rcaps->ci_gen;
673 error = 0;
675 caps_drop(caps);
676 return(error);
680 * caps_sys_put(portid, msg, msgsize)
682 * Send an opaque message of the specified size to the specified port. This
683 * function may only be used with a client port. The message id is returned.
686 sys_caps_sys_put(struct caps_sys_put_args *uap)
688 caps_kinfo_t caps;
689 caps_kmsg_t msg;
690 struct proc *p = curproc;
691 int error;
693 if (uap->msgsize < 0)
694 return(EINVAL);
695 if ((caps = caps_find_id(curthread, uap->portid)) == NULL)
696 return(EINVAL);
697 if (caps->ci_type == CAPT_FORKED) {
698 error = ENOTCONN;
699 } else if (caps->ci_rcaps == NULL) {
700 error = EINVAL;
701 } else if (caps->ci_cmsgcount > CAPS_MAXINPROG) {
703 * If this client has queued a large number of messages return
704 * ENOBUFS. The client must process some replies before it can
705 * send new messages. The server can also throttle a client by
706 * holding its replies. XXX allow a server to refuse messages from
707 * a client.
709 error = ENOBUFS;
710 } else {
711 msg = caps_alloc_msg(caps);
712 uap->sysmsg_offset = msg->km_msgid.c_id;
715 * If the remote end is closed return ENOTCONN immediately, otherwise
716 * send it to the remote end.
718 * Note: since this is a new message, caps_load_ccr() returns a remote
719 * caps of NULL.
721 if (caps->ci_rcaps->ci_flags & CAPKF_CLOSED) {
722 error = ENOTCONN;
723 caps_free_msg(msg);
724 } else {
726 * new message, load_ccr returns NULL. hold rcaps for put_msg
728 error = 0;
729 caps_load_ccr(caps, msg, p, uap->msg, uap->msgsize);
730 caps_hold(caps->ci_rcaps);
731 ++caps->ci_cmsgcount;
732 caps_put_msg(caps->ci_rcaps, msg, CAPMS_REQUEST); /* drops rcaps */
735 caps_drop(caps);
736 return(error);
740 * caps_sys_reply(portid, msg, msgsize, msgid)
742 * Reply to the message referenced by the specified msgid, supplying opaque
743 * data back to the originator.
746 sys_caps_sys_reply(struct caps_sys_reply_args *uap)
748 caps_kinfo_t caps;
749 caps_kinfo_t rcaps;
750 caps_kmsg_t msg;
751 struct proc *p;
752 int error;
754 if (uap->msgsize < 0)
755 return(EINVAL);
756 if ((caps = caps_find_id(curthread, uap->portid)) == NULL)
757 return(EINVAL);
758 if (caps->ci_type == CAPT_FORKED) {
760 * The caps structure is just a fork placeholder, tell the caller
761 * that he has to reconnect.
763 error = ENOTCONN;
764 } else if ((msg = caps_find_msg(caps, uap->msgcid)) == NULL) {
766 * Could not find message being replied to (other side might have
767 * gone away).
769 error = EINVAL;
770 } else if ((msg->km_flags & CAPKMF_ONUSERQ) == 0) {
772 * Trying to reply to a non-replyable message
774 error = EINVAL;
775 } else {
777 * If the remote end is closed requeue to ourselves for disposal.
778 * Otherwise send the reply to the other end (the other end will
779 * return a passive DISPOSE to us when it has eaten the data)
781 error = 0;
782 caps_dequeue_msg(caps, msg);
783 p = curproc;
784 if (msg->km_mcaps->ci_flags & CAPKF_CLOSED) {
785 caps_drop(caps_load_ccr(caps, msg, p, NULL, 0));
786 caps_hold(caps); /* ref for message */
787 caps_put_msg(caps, msg, CAPMS_DISPOSE);
788 } else {
789 rcaps = caps_load_ccr(caps, msg, p, uap->msg, uap->msgsize);
790 caps_put_msg(rcaps, msg, CAPMS_REPLY);
793 caps_drop(caps);
794 return(error);
798 * caps_sys_get(portid, msg, maxsize, msgid, ccr)
800 * Retrieve the next ready message on the port, store its message id in
801 * uap->msgid and return the length of the message. If the message is too
802 * large to fit the message id, length, and creds are still returned, but
803 * the message is not dequeued (the caller is expected to call again with
804 * a larger buffer or to reply the messageid if it does not want to handle
805 * the message).
807 * EWOULDBLOCK is returned if no messages are pending. Note that 0-length
808 * messages are perfectly acceptable so 0 can be legitimately returned.
811 sys_caps_sys_get(struct caps_sys_get_args *uap)
813 caps_kinfo_t caps;
814 caps_kmsg_t msg;
815 int error;
817 if (uap->maxsize < 0)
818 return(EINVAL);
819 if ((caps = caps_find_id(curthread, uap->portid)) == NULL)
820 return(EINVAL);
821 if (caps->ci_type == CAPT_FORKED) {
822 error = ENOTCONN;
823 } else if ((msg = TAILQ_FIRST(&caps->ci_msgpendq)) == NULL) {
824 error = EWOULDBLOCK;
825 } else {
826 error = caps_process_msg(caps, msg, uap);
828 caps_drop(caps);
829 return(error);
833 * caps_sys_wait(portid, msg, maxsize, msgid, ccr)
835 * Retrieve the next ready message on the port, store its message id in
836 * uap->msgid and return the length of the message. If the message is too
837 * large to fit the message id, length, and creds are still returned, but
838 * the message is not dequeued (the caller is expected to call again with
839 * a larger buffer or to reply the messageid if it does not want to handle
840 * the message).
842 * This function blocks until interrupted or a message is received.
843 * Note that 0-length messages are perfectly acceptable so 0 can be
844 * legitimately returned.
847 sys_caps_sys_wait(struct caps_sys_wait_args *uap)
849 caps_kinfo_t caps;
850 caps_kmsg_t msg;
851 int error;
853 if (uap->maxsize < 0)
854 return(EINVAL);
855 if ((caps = caps_find_id(curthread, uap->portid)) == NULL)
856 return(EINVAL);
857 if (caps->ci_type == CAPT_FORKED) {
858 error = ENOTCONN;
859 } else {
860 error = 0;
861 while ((msg = TAILQ_FIRST(&caps->ci_msgpendq)) == NULL) {
862 if ((error = tsleep(caps, PCATCH, "caps", 0)) != 0)
863 break;
865 if (error == 0) {
866 error = caps_process_msg(caps, msg,
867 (struct caps_sys_get_args *)uap);
870 caps_drop(caps);
871 return(error);
874 static int
875 caps_process_msg(caps_kinfo_t caps, caps_kmsg_t msg, struct caps_sys_get_args *uap)
877 int error = 0;
878 int msgsize;
879 caps_kinfo_t rcaps;
881 msg->km_flags |= CAPKMF_PEEKED;
882 msgsize = msg->km_xio.xio_bytes;
883 if (msgsize <= uap->maxsize)
884 caps_dequeue_msg(caps, msg);
886 if (msg->km_xio.xio_bytes != 0) {
887 error = xio_copy_xtou(&msg->km_xio, 0, uap->msg,
888 min(msg->km_xio.xio_bytes, uap->maxsize));
889 if (error) {
890 if (msg->km_mcaps->ci_td && msg->km_mcaps->ci_td->td_proc) {
891 kprintf("xio_copy_xtou: error %d from proc %d\n",
892 error, msg->km_mcaps->ci_td->td_proc->p_pid);
894 if (msgsize > uap->maxsize)
895 caps_dequeue_msg(caps, msg);
896 msgsize = 0;
897 error = 0;
901 if (uap->msgid)
902 error = copyout(&msg->km_msgid, uap->msgid, sizeof(msg->km_msgid));
903 if (uap->ccr)
904 error = copyout(&msg->km_ccr, uap->ccr, sizeof(msg->km_ccr));
905 if (error == 0)
906 uap->sysmsg_result = msgsize;
909 * If the message was dequeued we must deal with it.
911 if (msgsize <= uap->maxsize) {
912 switch(msg->km_state) {
913 case CAPMS_REQUEST:
914 case CAPMS_REQUEST_RETRY:
915 TAILQ_INSERT_TAIL(&caps->ci_msguserq, msg, km_node);
916 msg->km_flags |= CAPKMF_ONUSERQ;
917 break;
918 case CAPMS_REPLY:
919 case CAPMS_REPLY_RETRY:
920 --caps->ci_cmsgcount;
921 rcaps = caps_load_ccr(caps, msg, curproc, NULL, 0);
922 if (caps == rcaps || (rcaps->ci_flags & CAPKF_CLOSED)) {
923 /* degenerate disposal case */
924 caps_free_msg(msg);
925 caps_drop(rcaps);
926 } else {
927 caps_put_msg(rcaps, msg, CAPMS_DISPOSE);
929 break;
930 case CAPMS_DISPOSE:
931 caps_free_msg(msg);
932 break;
935 return(error);
939 * caps_sys_abort(portid, msgcid, flags)
941 * Abort a previously sent message. You must still wait for the message
942 * to be returned after sending the abort request. This function will
943 * return the appropriate CAPS_ABORT_* code depending on what it had
944 * to do.
947 sys_caps_sys_abort(struct caps_sys_abort_args *uap)
949 uap->sysmsg_result = CAPS_ABORT_NOTIMPL;
950 return(0);
954 * KERNEL SYSCALL SEPARATION SUPPORT FUNCTIONS
957 static
958 caps_kinfo_t
959 kern_caps_sys_service(const char *name, uid_t uid, gid_t gid,
960 struct ucred *cred, int flags, int *error)
962 caps_kinfo_t caps;
963 int len;
965 len = strlen(name);
968 * Make sure we can use the uid and gid
970 if (cred) {
971 if (cred->cr_uid != 0 && uid != (uid_t)-1 && cred->cr_uid != uid) {
972 *error = EPERM;
973 return(NULL);
975 if (cred->cr_uid != 0 && gid != (gid_t)-1 && !groupmember(gid, cred)) {
976 *error = EPERM;
977 return(NULL);
982 * Handle CAPF_EXCL
984 if (flags & CAPF_EXCL) {
985 if ((caps = caps_find(name, strlen(name), uid, gid)) != NULL) {
986 caps_drop(caps);
987 *error = EEXIST;
988 return(NULL);
993 * Create the service
995 caps = caps_alloc(curthread, name, len,
996 uid, gid, flags & CAPF_UFLAGS, CAPT_SERVICE);
997 wakeup(&caps_waitsvc);
998 return(caps);
1001 static
1002 caps_kinfo_t
1003 kern_caps_sys_client(const char *name, uid_t uid, gid_t gid,
1004 struct ucred *cred, int flags, int *error)
1006 caps_kinfo_t caps, rcaps;
1007 int len;
1009 len = strlen(name);
1012 * Locate the CAPS service (rcaps ref is for caps->ci_rcaps)
1014 again:
1015 if ((rcaps = caps_find(name, len, uid, gid)) == NULL) {
1016 if (flags & CAPF_WAITSVC) {
1017 char cbuf[32];
1018 ksnprintf(cbuf, sizeof(cbuf), "C%s", name);
1019 *error = tsleep(&caps_waitsvc, PCATCH, cbuf, 0);
1020 if (*error == 0)
1021 goto again;
1022 } else {
1023 *error = ENOENT;
1025 return(NULL);
1029 * Check permissions
1031 if (cred) {
1032 *error = EACCES;
1033 if ((flags & CAPF_USER) && (rcaps->ci_flags & CAPF_USER)) {
1034 if (rcaps->ci_uid != (uid_t)-1 && rcaps->ci_uid == cred->cr_uid)
1035 *error = 0;
1037 if ((flags & CAPF_GROUP) && (rcaps->ci_flags & CAPF_GROUP)) {
1038 if (rcaps->ci_gid != (gid_t)-1 && groupmember(rcaps->ci_gid, cred))
1039 *error = 0;
1041 if ((flags & CAPF_WORLD) && (rcaps->ci_flags & CAPF_WORLD)) {
1042 *error = 0;
1044 if (*error) {
1045 caps_drop(rcaps);
1046 return(NULL);
1048 } else {
1049 *error = 0;
1053 * Allocate the client side and connect to the server
1055 caps = caps_alloc(curthread, name, len,
1056 uid, gid, flags & CAPF_UFLAGS, CAPT_CLIENT);
1057 caps->ci_rcaps = rcaps;
1058 caps->ci_flags |= CAPKF_RCAPS;
1059 return(caps);