kernel - Move MP lock inward, plus misc other stuff
[dragonfly.git] / sys / kern / lwkt_caps.c
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:
10  * 
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.
20  * 
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.
33  * 
34  * $DragonFly: src/sys/kern/lwkt_caps.c,v 1.13 2007/02/26 21:41:08 corecode Exp $
35  */
36
37 /*
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.
42  *
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.
51  *
52  * From the userland perspective a client/server connection involves two
53  * message ports on the client and two message ports on the server.
54  */
55
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>
67
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);
78
79 #define CAPS_HSIZE      64
80 #define CAPS_HMASK      (CAPS_HSIZE - 1)
81
82 static caps_kinfo_t     caps_hash_ary[CAPS_HSIZE];
83 static int caps_waitsvc;
84
85 MALLOC_DEFINE(M_CAPS, "caps", "caps IPC messaging");
86
87 static int caps_enabled;
88 SYSCTL_INT(_kern, OID_AUTO, caps_enabled,
89         CTLFLAG_RW, &caps_enabled, 0, "Enable CAPS");
90
91 /************************************************************************
92  *                      INLINE SUPPORT FUNCTIONS                        *
93  ************************************************************************/
94
95 static __inline
96 struct caps_kinfo **
97 caps_hash(const char *name, int len)
98 {
99     int hv = 0x7123F4B3;
100
101     while (--len >= 0)
102         hv = (hv << 5) ^ name[len] ^ (hv >> 23);
103     return(&caps_hash_ary[(hv ^ (hv >> 16)) & CAPS_HMASK]);
104 }
105
106 static __inline
107 void
108 caps_hold(caps_kinfo_t caps)
109 {
110     ++caps->ci_refs;
111 }
112
113 static __inline
114 void
115 caps_drop(caps_kinfo_t caps)
116 {
117     if (--caps->ci_refs == 0)
118         caps_free(caps);
119 }
120
121 /************************************************************************
122  *                      STATIC SUPPORT FUNCTIONS                        *
123  ************************************************************************/
124
125 static
126 caps_kinfo_t
127 caps_find(const char *name, int len, uid_t uid, gid_t gid)
128 {
129     caps_kinfo_t caps;
130     struct caps_kinfo **chash;
131
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
138         ) {
139             caps_hold(caps);
140             break;
141         }
142     }
143     return(caps);
144 }
145
146 static
147 caps_kinfo_t
148 caps_find_id(thread_t td, int id)
149 {
150    caps_kinfo_t caps;
151
152    for (caps = td->td_caps; caps; caps = caps->ci_tdnext) {
153         if (caps->ci_id == id) {
154             caps_hold(caps);
155             break;
156         }
157    }
158    return(caps);
159 }
160
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)
165 {
166     struct caps_kinfo **chash;
167     caps_kinfo_t caps;
168     caps_kinfo_t ctmp;
169
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;
186     }
187     if (td->td_caps) {
188         caps->ci_id = td->td_caps->ci_id + 1;
189         if (caps->ci_id < 0) {
190             /*
191              * It is virtually impossible for this case to occur.
192              */
193             caps->ci_id = 1;
194             while ((ctmp = caps_find_id(td, caps->ci_id)) != NULL) {
195                 caps_drop(ctmp);
196                 ++caps->ci_id;
197             }
198         }
199     } else {
200         caps->ci_id = 1;
201     }
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);
207 }
208
209 static
210 caps_kmsg_t
211 caps_alloc_msg(caps_kinfo_t caps)
212 {
213     caps_kmsg_t msg;
214
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);
218 }
219
220 static
221 caps_kmsg_t
222 caps_find_msg(caps_kinfo_t caps, off_t msgid)
223 {
224     caps_kmsg_t msg;
225
226     TAILQ_FOREACH(msg, &caps->ci_msguserq, km_node) {
227         if (msg->km_msgid.c_id == msgid)
228             return(msg);
229     }
230     TAILQ_FOREACH(msg, &caps->ci_msgpendq, km_node) {
231         if (msg->km_msgid.c_id == msgid)
232             return(msg);
233     }
234     return(NULL);
235 }
236
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)
241 {
242     int i;
243     struct ucred *cr = p->p_ucred;
244     caps_kinfo_t rcaps;
245
246     /*
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.
250      */
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);
255
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);
264 }
265
266 static void
267 caps_dequeue_msg(caps_kinfo_t caps, caps_kmsg_t msg)
268 {
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);
274 }
275
276 static void
277 caps_put_msg(caps_kinfo_t caps, caps_kmsg_t msg, caps_msg_state_t state)
278 {
279     KKASSERT((msg->km_flags & (CAPKMF_ONUSERQ|CAPKMF_ONPENDQ)) == 0);
280
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);
285
286     /*
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.
291      */
292     if (state != CAPMS_DISPOSE)
293         wakeup(caps);
294     caps_drop(caps);
295 }
296
297 /*
298  * caps_free_msg_mcaps()
299  */
300 static
301 caps_kinfo_t
302 caps_free_msg_mcaps(caps_kmsg_t msg)
303 {
304     caps_kinfo_t mcaps;
305
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);
311 }
312
313 /*
314  * caps_free_msg()
315  *
316  * Free a caps placeholder message.  The message must not be on any queues.
317  */
318 static void
319 caps_free_msg(caps_kmsg_t msg)
320 {
321     caps_kinfo_t rcaps;
322
323     if ((rcaps = caps_free_msg_mcaps(msg)) != NULL)
324         caps_drop(rcaps);
325     kfree(msg, M_CAPS);
326 }
327
328 /*
329  * Validate the service name
330  */
331 static int
332 caps_name_check(const char *name, size_t len)
333 {
334     size_t i;
335     char c;
336
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);
348     }
349     return(0);
350 }
351
352 /*
353  * caps_term()
354  *
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.
357  *
358  * This function should not be called with CAPKF_TDLIST unless the caller
359  * has an additional hold on the caps structure.
360  */
361 static void
362 caps_term(caps_kinfo_t caps, int flags, caps_kinfo_t cflush)
363 {
364     struct caps_kinfo **scan;
365     caps_kmsg_t msg;
366
367     if (flags & CAPKF_TDLIST)
368         caps->ci_flags |= CAPKF_CLOSED;
369
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;
375
376         TAILQ_INIT(&tmpuserq);
377         TAILQ_INIT(&tmppendq);
378
379         while ((msg = TAILQ_FIRST(&caps->ci_msgpendq)) != NULL ||
380                (msg = TAILQ_FIRST(&caps->ci_msguserq)) != NULL
381         ) {
382             mflags = msg->km_flags & (CAPKMF_ONUSERQ|CAPKMF_ONPENDQ);
383             caps_dequeue_msg(caps, msg);
384
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 {
391                 /*
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.
396                  */
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) {
402                         /*
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).
409                          */
410                         if ((caps->ci_flags & CAPKF_CLOSED) ||
411                             (mflags & CAPKMF_ONPENDQ)
412                         ) {
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);
419                         }
420                     } else {
421                         /*
422                          * auto-reply to the originator.  rcaps already
423                          * has a dangling hold so we do not have to hold it
424                          * again.
425                          */
426                         caps_put_msg(rcaps, msg, CAPMS_REPLY);
427                     }
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);
437                     }
438                     break;
439                 case CAPMS_DISPOSE:
440                     caps_free_msg(msg);
441                     break;
442                 }
443             }
444         }
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;
449         }
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;
454         }
455     }
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
460         ) {
461             KKASSERT(*scan != NULL);
462         }
463         *scan = caps->ci_hnext;
464         caps->ci_hnext = (void *)-1;
465         caps->ci_flags &= ~CAPKF_HLIST;
466     }
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
471         ) {
472             KKASSERT(*scan != NULL);
473         }
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);
479     }
480     if ((flags & CAPKF_RCAPS) && (caps->ci_flags & CAPKF_RCAPS)) {
481         caps_kinfo_t ctmp;
482
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);
488         }
489     }
490 }
491
492 static void
493 caps_free(caps_kinfo_t caps)
494 {
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);
499 }
500
501 /************************************************************************
502  *                      PROCESS SUPPORT FUNCTIONS                       *
503  ************************************************************************/
504
505 /*
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.
509  */
510 void
511 caps_fork(struct thread *td1, struct thread *td2)
512 {
513     caps_kinfo_t caps1;
514     caps_kinfo_t caps2;
515
516     /*
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.
521      */
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;
530     }
531
532     /*
533      * Reverse the list order to maintain highest-id-first
534      */
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;
542     }
543 }
544
545 void
546 caps_exit(struct thread *td)
547 {
548     caps_kinfo_t caps;
549
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);
554     }
555 }
556
557 /************************************************************************
558  *                              SYSTEM CALLS                            *
559  ************************************************************************/
560
561 /*
562  * caps_sys_service(name, uid, gid, upcid, flags);
563  *
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.
567  *
568  * upcid can either be an upcall or a kqueue identifier (XXX)
569  *
570  * MPALMOSTSAFE
571  */
572 int
573 sys_caps_sys_service(struct caps_sys_service_args *uap)
574 {
575     struct ucred *cred = curproc->p_ucred;
576     char name[CAPS_MAXNAMELEN];
577     caps_kinfo_t caps;
578     size_t len;
579     int error;
580
581     if (caps_enabled == 0)
582         return(EOPNOTSUPP);
583     if ((error = copyinstr(uap->name, name, CAPS_MAXNAMELEN, &len)) != 0)
584         return(error);
585     if ((ssize_t)--len <= 0)
586         return(EINVAL);
587     get_mplock();
588
589     if ((error = caps_name_check(name, len)) == 0) {
590         caps = kern_caps_sys_service(name, uap->uid, uap->gid, cred,
591                                     uap->flags & CAPF_UFLAGS, &error);
592         if (caps)
593             uap->sysmsg_result = caps->ci_id;
594     }
595     rel_mplock();
596     return(error);
597 }
598
599 /*
600  * caps_sys_client(name, uid, gid, upcid, flags);
601  *
602  * Create an IPC client connected to the specified service.  Either uid or gid
603  * may be -1, indicating a wildcard, but not both.  The port identifier is
604  * returned.
605  *
606  * upcid can either be an upcall or a kqueue identifier (XXX)
607  *
608  * MPALMOSTSAFE
609  */
610 int
611 sys_caps_sys_client(struct caps_sys_client_args *uap)
612 {
613     struct ucred *cred = curproc->p_ucred;
614     char name[CAPS_MAXNAMELEN];
615     caps_kinfo_t caps;
616     size_t len;
617     int error;
618
619     if (caps_enabled == 0)
620         return(EOPNOTSUPP);
621     if ((error = copyinstr(uap->name, name, CAPS_MAXNAMELEN, &len)) != 0)
622         return(error);
623     if ((ssize_t)--len <= 0)
624         return(EINVAL);
625     get_mplock();
626
627     if ((error = caps_name_check(name, len)) == 0) {
628         caps = kern_caps_sys_client(name, uap->uid, uap->gid, cred,
629                                     uap->flags & CAPF_UFLAGS, &error);
630         if (caps)
631             uap->sysmsg_result = caps->ci_id;
632     }
633     rel_mplock();
634     return(error);
635 }
636
637 /*
638  * MPALMOSTSAFE
639  */
640 int
641 sys_caps_sys_close(struct caps_sys_close_args *uap)
642 {
643     caps_kinfo_t caps;
644     int error;
645
646     get_mplock();
647
648     if ((caps = caps_find_id(curthread, uap->portid)) != NULL) {
649             caps_term(caps, CAPKF_TDLIST|CAPKF_HLIST|CAPKF_FLUSH|CAPKF_RCAPS,
650                       NULL);
651             caps_drop(caps);
652             error = 0;
653     } else {
654             error = EINVAL;
655     }
656     rel_mplock();
657     return(error);
658 }
659
660 /*
661  * MPALMOSTSAFE
662  */
663 int
664 sys_caps_sys_setgen(struct caps_sys_setgen_args *uap)
665 {
666     caps_kinfo_t caps;
667     int error;
668
669     get_mplock();
670
671     if ((caps = caps_find_id(curthread, uap->portid)) != NULL) {
672         if (caps->ci_type == CAPT_FORKED) {
673             error = ENOTCONN;
674         } else {
675             caps->ci_gen = uap->gen;
676             error = 0;
677         }
678         caps_drop(caps);
679     } else {
680         error = EINVAL;
681     }
682     rel_mplock();
683     return(error);
684 }
685
686 /*
687  * MPALMOSTSAFE
688  */
689 int
690 sys_caps_sys_getgen(struct caps_sys_getgen_args *uap)
691 {
692     caps_kinfo_t caps;
693     int error;
694
695     get_mplock();
696
697     if ((caps = caps_find_id(curthread, uap->portid)) != NULL) {
698         if (caps->ci_type == CAPT_FORKED) {
699             error = ENOTCONN;
700         } else if (caps->ci_rcaps == NULL) {
701             error = EINVAL;
702         } else {
703             uap->sysmsg_result64 = caps->ci_rcaps->ci_gen;
704             error = 0;
705         }
706         caps_drop(caps);
707     } else {
708         error = EINVAL;
709     }
710     rel_mplock();
711     return(error);
712 }
713
714 /*
715  * caps_sys_put(portid, msg, msgsize)
716  *
717  * Send an opaque message of the specified size to the specified port.  This
718  * function may only be used with a client port.  The message id is returned.
719  *
720  * MPALMOSTSAFE
721  */
722 int
723 sys_caps_sys_put(struct caps_sys_put_args *uap)
724 {
725     caps_kinfo_t caps;
726     caps_kmsg_t msg;
727     struct proc *p = curproc;
728     int error;
729
730     if (uap->msgsize < 0)
731         return(EINVAL);
732     get_mplock();
733
734     if ((caps = caps_find_id(curthread, uap->portid)) == NULL) {
735         error = EINVAL;
736         goto done;
737     }
738     if (caps->ci_type == CAPT_FORKED) {
739         error = ENOTCONN;
740     } else if (caps->ci_rcaps == NULL) {
741         error = EINVAL;
742     } else if (caps->ci_cmsgcount > CAPS_MAXINPROG) {
743         /*
744          * If this client has queued a large number of messages return
745          * ENOBUFS.  The client must process some replies before it can
746          * send new messages.  The server can also throttle a client by
747          * holding its replies.  XXX allow a server to refuse messages from
748          * a client.
749          */
750         error = ENOBUFS;
751     } else {
752         msg = caps_alloc_msg(caps);
753         uap->sysmsg_offset = msg->km_msgid.c_id;
754
755         /*
756          * If the remote end is closed return ENOTCONN immediately, otherwise
757          * send it to the remote end.
758          *
759          * Note: since this is a new message, caps_load_ccr() returns a remote
760          * caps of NULL.
761          */
762         if (caps->ci_rcaps->ci_flags & CAPKF_CLOSED) {
763             error = ENOTCONN;
764             caps_free_msg(msg);
765         } else {
766             /*
767              * new message, load_ccr returns NULL. hold rcaps for put_msg
768              */
769             error = 0;
770             caps_load_ccr(caps, msg, p, uap->msg, uap->msgsize);
771             caps_hold(caps->ci_rcaps);
772             ++caps->ci_cmsgcount;
773             caps_put_msg(caps->ci_rcaps, msg, CAPMS_REQUEST); /* drops rcaps */
774         }
775     }
776     caps_drop(caps);
777 done:
778     rel_mplock();
779     return(error);
780 }
781
782 /*
783  * caps_sys_reply(portid, msg, msgsize, msgid)
784  *
785  * Reply to the message referenced by the specified msgid, supplying opaque
786  * data back to the originator.
787  *
788  * MPALMOSTSAFE
789  */
790 int
791 sys_caps_sys_reply(struct caps_sys_reply_args *uap)
792 {
793     caps_kinfo_t caps;
794     caps_kinfo_t rcaps;
795     caps_kmsg_t msg;
796     struct proc *p;
797     int error;
798
799     if (uap->msgsize < 0)
800         return(EINVAL);
801     get_mplock();
802
803     if ((caps = caps_find_id(curthread, uap->portid)) == NULL) {
804         error = EINVAL;
805         goto done;
806     }
807     if (caps->ci_type == CAPT_FORKED) {
808         /*
809          * The caps structure is just a fork placeholder, tell the caller
810          * that he has to reconnect.
811          */
812         error = ENOTCONN;
813     } else if ((msg = caps_find_msg(caps, uap->msgcid)) == NULL) {
814         /*
815          * Could not find message being replied to (other side might have
816          * gone away).
817          */
818         error = EINVAL;
819     } else if ((msg->km_flags & CAPKMF_ONUSERQ) == 0) {
820         /*
821          * Trying to reply to a non-replyable message
822          */
823         error = EINVAL;
824     } else {
825         /*
826          * If the remote end is closed requeue to ourselves for disposal.
827          * Otherwise send the reply to the other end (the other end will
828          * return a passive DISPOSE to us when it has eaten the data)
829          */
830         error = 0;
831         caps_dequeue_msg(caps, msg);
832         p = curproc;
833         if (msg->km_mcaps->ci_flags & CAPKF_CLOSED) {
834             caps_drop(caps_load_ccr(caps, msg, p, NULL, 0));
835             caps_hold(caps);                    /* ref for message */
836             caps_put_msg(caps, msg, CAPMS_DISPOSE);
837         } else {
838             rcaps = caps_load_ccr(caps, msg, p, uap->msg, uap->msgsize);
839             caps_put_msg(rcaps, msg, CAPMS_REPLY);
840         }
841     }
842     caps_drop(caps);
843 done:
844     rel_mplock();
845     return(error);
846 }
847
848 /*
849  * caps_sys_get(portid, msg, maxsize, msgid, ccr)
850  *
851  * Retrieve the next ready message on the port, store its message id in
852  * uap->msgid and return the length of the message.  If the message is too
853  * large to fit the message id, length, and creds are still returned, but
854  * the message is not dequeued (the caller is expected to call again with
855  * a larger buffer or to reply the messageid if it does not want to handle
856  * the message).
857  *
858  * EWOULDBLOCK is returned if no messages are pending.  Note that 0-length
859  * messages are perfectly acceptable so 0 can be legitimately returned.
860  *
861  * MPALMOSTSAFE
862  */
863 int
864 sys_caps_sys_get(struct caps_sys_get_args *uap)
865 {
866     caps_kinfo_t caps;
867     caps_kmsg_t msg;
868     int error;
869
870     if (uap->maxsize < 0)
871         return(EINVAL);
872     get_mplock();
873
874     if ((caps = caps_find_id(curthread, uap->portid)) != NULL) {
875         if (caps->ci_type == CAPT_FORKED) {
876             error = ENOTCONN;
877         } else if ((msg = TAILQ_FIRST(&caps->ci_msgpendq)) == NULL) {
878             error = EWOULDBLOCK;
879         } else {
880             error = caps_process_msg(caps, msg, uap);
881         }
882     } else {
883         error = EINVAL;
884     }
885     caps_drop(caps);
886     rel_mplock();
887     return(error);
888 }
889
890 /*
891  * caps_sys_wait(portid, msg, maxsize, msgid, ccr)
892  *
893  * Retrieve the next ready message on the port, store its message id in
894  * uap->msgid and return the length of the message.  If the message is too
895  * large to fit the message id, length, and creds are still returned, but
896  * the message is not dequeued (the caller is expected to call again with
897  * a larger buffer or to reply the messageid if it does not want to handle
898  * the message).
899  *
900  * This function blocks until interrupted or a message is received.
901  * Note that 0-length messages are perfectly acceptable so 0 can be
902  * legitimately returned.
903  *
904  * MPALMOSTSAFE
905  */
906 int
907 sys_caps_sys_wait(struct caps_sys_wait_args *uap)
908 {
909     caps_kinfo_t caps;
910     caps_kmsg_t msg;
911     int error;
912
913     if (uap->maxsize < 0)
914         return(EINVAL);
915     get_mplock();
916
917     if ((caps = caps_find_id(curthread, uap->portid)) != NULL) {
918         if (caps->ci_type == CAPT_FORKED) {
919             error = ENOTCONN;
920         } else {
921             error = 0;
922             while ((msg = TAILQ_FIRST(&caps->ci_msgpendq)) == NULL) {
923                 if ((error = tsleep(caps, PCATCH, "caps", 0)) != 0)
924                     break;
925             }
926             if (error == 0) {
927                 error = caps_process_msg(caps, msg,
928                                     (struct caps_sys_get_args *)uap);
929             }
930         }
931     } else {
932         error = EINVAL;
933     }
934     caps_drop(caps);
935     rel_mplock();
936     return(error);
937 }
938
939 static int
940 caps_process_msg(caps_kinfo_t caps, caps_kmsg_t msg,
941                  struct caps_sys_get_args *uap)
942 {
943     int error = 0;
944     int msgsize;
945     caps_kinfo_t rcaps;
946
947     msg->km_flags |= CAPKMF_PEEKED;
948     msgsize = msg->km_xio.xio_bytes;
949     if (msgsize <= uap->maxsize)
950         caps_dequeue_msg(caps, msg);
951
952     if (msg->km_xio.xio_bytes != 0) {
953         error = xio_copy_xtou(&msg->km_xio, 0, uap->msg, 
954                             min(msg->km_xio.xio_bytes, uap->maxsize));
955         if (error) {
956             if (msg->km_mcaps->ci_td && msg->km_mcaps->ci_td->td_proc) {
957                 kprintf("xio_copy_xtou: error %d from proc %d\n", 
958                         error, msg->km_mcaps->ci_td->td_proc->p_pid);
959             }
960             if (msgsize > uap->maxsize)
961                 caps_dequeue_msg(caps, msg);
962             msgsize = 0;
963             error = 0;
964         }
965     }
966
967     if (uap->msgid)
968         error = copyout(&msg->km_msgid, uap->msgid, sizeof(msg->km_msgid));
969     if (uap->ccr)
970         error = copyout(&msg->km_ccr, uap->ccr, sizeof(msg->km_ccr));
971     if (error == 0)
972         uap->sysmsg_result = msgsize;
973
974     /*
975      * If the message was dequeued we must deal with it.
976      */
977     if (msgsize <= uap->maxsize) {
978         switch(msg->km_state) {
979         case CAPMS_REQUEST:
980         case CAPMS_REQUEST_RETRY:
981             TAILQ_INSERT_TAIL(&caps->ci_msguserq, msg, km_node);
982             msg->km_flags |= CAPKMF_ONUSERQ;
983             break;
984         case CAPMS_REPLY:
985         case CAPMS_REPLY_RETRY:
986             --caps->ci_cmsgcount;
987             rcaps = caps_load_ccr(caps, msg, curproc, NULL, 0);
988             if (caps == rcaps || (rcaps->ci_flags & CAPKF_CLOSED)) {
989                 /* degenerate disposal case */
990                 caps_free_msg(msg);
991                 caps_drop(rcaps);
992             } else {
993                 caps_put_msg(rcaps, msg, CAPMS_DISPOSE);
994             }
995             break;
996         case CAPMS_DISPOSE:
997             caps_free_msg(msg);
998             break;
999         }
1000     }
1001     return(error);
1002 }
1003
1004 /*
1005  * caps_sys_abort(portid, msgcid, flags)
1006  *
1007  *      Abort a previously sent message.  You must still wait for the message
1008  *      to be returned after sending the abort request.  This function will
1009  *      return the appropriate CAPS_ABORT_* code depending on what it had
1010  *      to do.
1011  *
1012  * MPALMOSTSAFE
1013  */
1014 int
1015 sys_caps_sys_abort(struct caps_sys_abort_args *uap)
1016 {
1017     uap->sysmsg_result = CAPS_ABORT_NOTIMPL;
1018     return(0);
1019 }
1020
1021 /*
1022  * KERNEL SYSCALL SEPARATION SUPPORT FUNCTIONS
1023  */
1024
1025 static
1026 caps_kinfo_t
1027 kern_caps_sys_service(const char *name, uid_t uid, gid_t gid,
1028                         struct ucred *cred, int flags, int *error)
1029 {
1030     caps_kinfo_t caps;
1031     int len;
1032
1033     len = strlen(name);
1034
1035     /*
1036      * Make sure we can use the uid and gid
1037      */
1038     if (cred) {
1039         if (cred->cr_uid != 0 && uid != (uid_t)-1 && cred->cr_uid != uid) {
1040             *error = EPERM;
1041             return(NULL);
1042         }
1043         if (cred->cr_uid != 0 && gid != (gid_t)-1 && !groupmember(gid, cred)) {
1044             *error = EPERM;
1045             return(NULL);
1046         }
1047     }
1048
1049     /*
1050      * Handle CAPF_EXCL
1051      */
1052     if (flags & CAPF_EXCL) {
1053         if ((caps = caps_find(name, strlen(name), uid, gid)) != NULL) {
1054             caps_drop(caps);
1055             *error = EEXIST;
1056             return(NULL);
1057         }
1058     }
1059
1060     /*
1061      * Create the service
1062      */
1063     caps = caps_alloc(curthread, name, len, 
1064                         uid, gid, flags & CAPF_UFLAGS, CAPT_SERVICE);
1065     wakeup(&caps_waitsvc);
1066     return(caps);
1067 }
1068
1069 static
1070 caps_kinfo_t
1071 kern_caps_sys_client(const char *name, uid_t uid, gid_t gid,
1072                         struct ucred *cred, int flags, int *error)
1073 {
1074     caps_kinfo_t caps, rcaps;
1075     int len;
1076
1077     len = strlen(name);
1078
1079     /*
1080      * Locate the CAPS service (rcaps ref is for caps->ci_rcaps)
1081      */
1082 again:
1083     if ((rcaps = caps_find(name, len, uid, gid)) == NULL) {
1084         if (flags & CAPF_WAITSVC) {
1085             char cbuf[32];
1086             ksnprintf(cbuf, sizeof(cbuf), "C%s", name);
1087             *error = tsleep(&caps_waitsvc, PCATCH, cbuf, 0);
1088             if (*error == 0)
1089                 goto again;
1090         } else {
1091             *error = ENOENT;
1092         }
1093         return(NULL);
1094     }
1095
1096     /*
1097      * Check permissions
1098      */
1099     if (cred) {
1100         *error = EACCES;
1101         if ((flags & CAPF_USER) && (rcaps->ci_flags & CAPF_USER)) {
1102             if (rcaps->ci_uid != (uid_t)-1 && rcaps->ci_uid == cred->cr_uid)
1103                 *error = 0;
1104         }
1105         if ((flags & CAPF_GROUP) && (rcaps->ci_flags & CAPF_GROUP)) {
1106             if (rcaps->ci_gid != (gid_t)-1 && groupmember(rcaps->ci_gid, cred))
1107                 *error = 0;
1108         }
1109         if ((flags & CAPF_WORLD) && (rcaps->ci_flags & CAPF_WORLD)) {
1110             *error = 0;
1111         }
1112         if (*error) {
1113             caps_drop(rcaps);
1114             return(NULL);
1115         }
1116     } else {
1117         *error = 0;
1118     }
1119
1120     /*
1121      * Allocate the client side and connect to the server
1122      */
1123     caps = caps_alloc(curthread, name, len, 
1124                         uid, gid, flags & CAPF_UFLAGS, CAPT_CLIENT);
1125     caps->ci_rcaps = rcaps;
1126     caps->ci_flags |= CAPKF_RCAPS;
1127     return(caps);
1128 }
1129