[BACK]Return to sysv_msg.c CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / src / sys / kern

Annotation of src/sys/kern/sysv_msg.c, Revision 1.41

1.41    ! cube        1: /*     $NetBSD: sysv_msg.c,v 1.40 2005/12/07 06:14:13 thorpej Exp $    */
1.26      thorpej     2:
                      3: /*-
                      4:  * Copyright (c) 1999 The NetBSD Foundation, Inc.
                      5:  * All rights reserved.
                      6:  *
                      7:  * This code is derived from software contributed to The NetBSD Foundation
                      8:  * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
                      9:  * NASA Ames Research Center.
                     10:  *
                     11:  * Redistribution and use in source and binary forms, with or without
                     12:  * modification, are permitted provided that the following conditions
                     13:  * are met:
                     14:  * 1. Redistributions of source code must retain the above copyright
                     15:  *    notice, this list of conditions and the following disclaimer.
                     16:  * 2. Redistributions in binary form must reproduce the above copyright
                     17:  *    notice, this list of conditions and the following disclaimer in the
                     18:  *    documentation and/or other materials provided with the distribution.
                     19:  * 3. All advertising materials mentioning features or use of this software
                     20:  *    must display the following acknowledgement:
                     21:  *     This product includes software developed by the NetBSD
                     22:  *     Foundation, Inc. and its contributors.
                     23:  * 4. Neither the name of The NetBSD Foundation nor the names of its
                     24:  *    contributors may be used to endorse or promote products derived
                     25:  *    from this software without specific prior written permission.
                     26:  *
                     27:  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
                     28:  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
                     29:  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
                     30:  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
                     31:  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
                     32:  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
                     33:  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
                     34:  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
                     35:  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
                     36:  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
                     37:  * POSSIBILITY OF SUCH DAMAGE.
                     38:  */
1.9       cgd        39:
1.1       cgd        40: /*
                     41:  * Implementation of SVID messages
                     42:  *
1.26      thorpej    43:  * Author: Daniel Boulet
1.1       cgd        44:  *
                     45:  * Copyright 1993 Daniel Boulet and RTMX Inc.
                     46:  *
                     47:  * This system call was implemented by Daniel Boulet under contract from RTMX.
                     48:  *
                     49:  * Redistribution and use in source forms, with and without modification,
                     50:  * are permitted provided that this entire comment appears intact.
                     51:  *
                     52:  * Redistribution in binary form may occur without any restrictions.
                     53:  * Obviously, it would be nice if you gave credit where credit is due
                     54:  * but requiring it would be too onerous.
                     55:  *
                     56:  * This software is provided ``AS IS'' without any warranties of any kind.
                     57:  */
1.33      lukem      58:
                     59: #include <sys/cdefs.h>
1.41    ! cube       60: __KERNEL_RCSID(0, "$NetBSD: sysv_msg.c,v 1.40 2005/12/07 06:14:13 thorpej Exp $");
1.23      tron       61:
1.24      tron       62: #define SYSVMSG
1.1       cgd        63:
1.2       mycroft    64: #include <sys/param.h>
                     65: #include <sys/kernel.h>
                     66: #include <sys/msg.h>
1.29      simonb     67: #include <sys/sysctl.h>
                     68: #include <sys/mount.h>         /* XXX for <sys/syscallargs.h> */
1.35      thorpej    69: #include <sys/sa.h>
1.10      cgd        70: #include <sys/syscallargs.h>
1.18      christos   71:
1.1       cgd        72: #define MSG_DEBUG
                     73: #undef MSG_DEBUG_OK
                     74:
1.20      christos   75: #ifdef MSG_DEBUG_OK
1.21      christos   76: #define MSG_PRINTF(a)  printf a
1.20      christos   77: #else
                     78: #define MSG_PRINTF(a)
                     79: #endif
                     80:
1.36      jdolecek   81: static int     nfree_msgmaps;          /* # of free map entries */
                     82: static short   free_msgmaps;   /* head of linked list of free map entries */
                     83: static struct  __msg *free_msghdrs;    /* list of free msg headers */
                     84: static char    *msgpool;               /* MSGMAX byte long msg buffer pool */
                     85: static struct  msgmap *msgmaps;        /* MSGSEG msgmap structures */
                     86: static struct __msg *msghdrs;          /* MSGTQL msg headers */
                     87: struct msqid_ds *msqids;               /* MSGMNI msqid_ds struct's */
1.1       cgd        88:
1.37      junyoung   89: static void msg_freehdr(struct __msg *);
1.18      christos   90:
                     91: void
1.40      thorpej    92: msginit(void)
1.1       cgd        93: {
1.36      jdolecek   94:        int i, sz;
                     95:        vaddr_t v;
1.1       cgd        96:
1.3       mycroft    97:        /*
                     98:         * msginfo.msgssz should be a power of two for efficiency reasons.
                     99:         * It is also pretty silly if msginfo.msgssz is less than 8
                    100:         * or greater than about 256 so ...
                    101:         */
1.1       cgd       102:
1.3       mycroft   103:        i = 8;
                    104:        while (i < 1024 && i != msginfo.msgssz)
                    105:                i <<= 1;
                    106:        if (i != msginfo.msgssz) {
1.20      christos  107:                MSG_PRINTF(("msginfo.msgssz=%d (0x%x)\n", msginfo.msgssz,
                    108:                    msginfo.msgssz));
1.3       mycroft   109:                panic("msginfo.msgssz not a small power of 2");
                    110:        }
                    111:
                    112:        if (msginfo.msgseg > 32767) {
1.20      christos  113:                MSG_PRINTF(("msginfo.msgseg=%d\n", msginfo.msgseg));
1.3       mycroft   114:                panic("msginfo.msgseg > 32767");
                    115:        }
                    116:
1.36      jdolecek  117:        /* Allocate pageable memory for our structures */
                    118:        sz = msginfo.msgmax
                    119:                + msginfo.msgseg * sizeof(struct msgmap)
                    120:                + msginfo.msgtql * sizeof(struct __msg)
                    121:                + msginfo.msgmni * sizeof(struct msqid_ds);
1.39      yamt      122:        v = uvm_km_alloc(kernel_map, round_page(sz), 0,
                    123:            UVM_KMF_WIRED|UVM_KMF_ZERO);
                    124:        if (v == 0)
1.36      jdolecek  125:                panic("sysv_msg: cannot allocate memory");
                    126:        msgpool = (void *)v;
                    127:        msgmaps = (void *) (msgpool + msginfo.msgmax);
                    128:        msghdrs = (void *) (msgmaps + msginfo.msgseg);
                    129:        msqids = (void *) (msghdrs + msginfo.msgtql);
1.3       mycroft   130:
                    131:        for (i = 0; i < msginfo.msgseg; i++) {
                    132:                if (i > 0)
                    133:                        msgmaps[i-1].next = i;
                    134:                msgmaps[i].next = -1;   /* implies entry is available */
                    135:        }
                    136:        free_msgmaps = 0;
                    137:        nfree_msgmaps = msginfo.msgseg;
                    138:
                    139:        if (msghdrs == NULL)
                    140:                panic("msghdrs is NULL");
                    141:
                    142:        for (i = 0; i < msginfo.msgtql; i++) {
                    143:                msghdrs[i].msg_type = 0;
                    144:                if (i > 0)
                    145:                        msghdrs[i-1].msg_next = &msghdrs[i];
                    146:                msghdrs[i].msg_next = NULL;
                    147:        }
                    148:        free_msghdrs = &msghdrs[0];
                    149:
                    150:        if (msqids == NULL)
                    151:                panic("msqids is NULL");
                    152:
1.4       mycroft   153:        for (i = 0; i < msginfo.msgmni; i++) {
1.3       mycroft   154:                msqids[i].msg_qbytes = 0;       /* implies entry is available */
1.26      thorpej   155:                msqids[i].msg_perm._seq = 0;    /* reset to a known value */
1.3       mycroft   156:        }
1.1       cgd       157: }
                    158:
1.3       mycroft   159: static void
1.40      thorpej   160: msg_freehdr(struct __msg *msghdr)
1.1       cgd       161: {
1.3       mycroft   162:        while (msghdr->msg_ts > 0) {
                    163:                short next;
                    164:                if (msghdr->msg_spot < 0 || msghdr->msg_spot >= msginfo.msgseg)
                    165:                        panic("msghdr->msg_spot out of range");
                    166:                next = msgmaps[msghdr->msg_spot].next;
                    167:                msgmaps[msghdr->msg_spot].next = free_msgmaps;
                    168:                free_msgmaps = msghdr->msg_spot;
1.5       mycroft   169:                nfree_msgmaps++;
1.3       mycroft   170:                msghdr->msg_spot = next;
                    171:                if (msghdr->msg_ts >= msginfo.msgssz)
                    172:                        msghdr->msg_ts -= msginfo.msgssz;
                    173:                else
                    174:                        msghdr->msg_ts = 0;
                    175:        }
                    176:        if (msghdr->msg_spot != -1)
                    177:                panic("msghdr->msg_spot != -1");
                    178:        msghdr->msg_next = free_msghdrs;
                    179:        free_msghdrs = msghdr;
1.1       cgd       180: }
                    181:
                    182: int
1.40      thorpej   183: sys___msgctl13(struct lwp *l, void *v, register_t *retval)
1.16      thorpej   184: {
1.26      thorpej   185:        struct sys___msgctl13_args /* {
1.10      cgd       186:                syscallarg(int) msqid;
                    187:                syscallarg(int) cmd;
                    188:                syscallarg(struct msqid_ds *) buf;
1.16      thorpej   189:        } */ *uap = v;
1.35      thorpej   190:        struct proc *p = l->l_proc;
1.26      thorpej   191:        struct msqid_ds msqbuf;
                    192:        int cmd, error;
                    193:
                    194:        cmd = SCARG(uap, cmd);
                    195:
                    196:        if (cmd == IPC_SET) {
                    197:                error = copyin(SCARG(uap, buf), &msqbuf, sizeof(msqbuf));
                    198:                if (error)
                    199:                        return (error);
                    200:        }
                    201:
                    202:        error = msgctl1(p, SCARG(uap, msqid), cmd,
                    203:            (cmd == IPC_SET || cmd == IPC_STAT) ? &msqbuf : NULL);
                    204:
                    205:        if (error == 0 && cmd == IPC_STAT)
                    206:                error = copyout(&msqbuf, SCARG(uap, buf), sizeof(msqbuf));
                    207:
                    208:        return (error);
                    209: }
                    210:
                    211: int
1.40      thorpej   212: msgctl1(struct proc *p, int msqid, int cmd, struct msqid_ds *msqbuf)
1.26      thorpej   213: {
1.3       mycroft   214:        struct ucred *cred = p->p_ucred;
1.26      thorpej   215:        struct msqid_ds *msqptr;
                    216:        int error = 0, ix;
1.1       cgd       217:
1.26      thorpej   218:        MSG_PRINTF(("call to msgctl1(%d, %d)\n", msqid, cmd));
1.1       cgd       219:
1.26      thorpej   220:        ix = IPCID_TO_IX(msqid);
1.1       cgd       221:
1.26      thorpej   222:        if (ix < 0 || ix >= msginfo.msgmni) {
                    223:                MSG_PRINTF(("msqid (%d) out of range (0<=msqid<%d)\n", ix,
1.20      christos  224:                    msginfo.msgmni));
1.26      thorpej   225:                return (EINVAL);
1.3       mycroft   226:        }
1.1       cgd       227:
1.26      thorpej   228:        msqptr = &msqids[ix];
1.1       cgd       229:
1.3       mycroft   230:        if (msqptr->msg_qbytes == 0) {
1.20      christos  231:                MSG_PRINTF(("no such msqid\n"));
1.26      thorpej   232:                return (EINVAL);
1.3       mycroft   233:        }
1.26      thorpej   234:        if (msqptr->msg_perm._seq != IPCID_TO_SEQ(msqid)) {
1.20      christos  235:                MSG_PRINTF(("wrong sequence number\n"));
1.26      thorpej   236:                return (EINVAL);
1.3       mycroft   237:        }
1.1       cgd       238:
1.3       mycroft   239:        switch (cmd) {
                    240:        case IPC_RMID:
1.1       cgd       241:        {
1.26      thorpej   242:                struct __msg *msghdr;
                    243:                if ((error = ipcperm(cred, &msqptr->msg_perm, IPC_M)) != 0)
                    244:                        return (error);
1.3       mycroft   245:                /* Free the message headers */
1.26      thorpej   246:                msghdr = msqptr->_msg_first;
1.3       mycroft   247:                while (msghdr != NULL) {
1.26      thorpej   248:                        struct __msg *msghdr_tmp;
1.3       mycroft   249:
                    250:                        /* Free the segments of each message */
1.26      thorpej   251:                        msqptr->_msg_cbytes -= msghdr->msg_ts;
1.5       mycroft   252:                        msqptr->msg_qnum--;
1.3       mycroft   253:                        msghdr_tmp = msghdr;
                    254:                        msghdr = msghdr->msg_next;
                    255:                        msg_freehdr(msghdr_tmp);
                    256:                }
1.1       cgd       257:
1.26      thorpej   258:                if (msqptr->_msg_cbytes != 0)
1.3       mycroft   259:                        panic("msg_cbytes is screwed up");
                    260:                if (msqptr->msg_qnum != 0)
                    261:                        panic("msg_qnum is screwed up");
1.1       cgd       262:
1.3       mycroft   263:                msqptr->msg_qbytes = 0; /* Mark it as free */
1.1       cgd       264:
1.26      thorpej   265:                wakeup(msqptr);
1.1       cgd       266:        }
1.3       mycroft   267:                break;
1.1       cgd       268:
1.3       mycroft   269:        case IPC_SET:
1.26      thorpej   270:                if ((error = ipcperm(cred, &msqptr->msg_perm, IPC_M)))
                    271:                        return (error);
                    272:                if (msqbuf->msg_qbytes > msqptr->msg_qbytes && cred->cr_uid != 0)
                    273:                        return (EPERM);
                    274:                if (msqbuf->msg_qbytes > msginfo.msgmnb) {
                    275:                        MSG_PRINTF(("can't increase msg_qbytes beyond %d "
                    276:                            "(truncating)\n", msginfo.msgmnb));
                    277:                        /* silently restrict qbytes to system limit */
                    278:                        msqbuf->msg_qbytes = msginfo.msgmnb;
1.3       mycroft   279:                }
1.26      thorpej   280:                if (msqbuf->msg_qbytes == 0) {
1.20      christos  281:                        MSG_PRINTF(("can't reduce msg_qbytes to 0\n"));
1.26      thorpej   282:                        return (EINVAL);        /* XXX non-standard errno! */
1.3       mycroft   283:                }
1.26      thorpej   284:                msqptr->msg_perm.uid = msqbuf->msg_perm.uid;
                    285:                msqptr->msg_perm.gid = msqbuf->msg_perm.gid;
1.3       mycroft   286:                msqptr->msg_perm.mode = (msqptr->msg_perm.mode & ~0777) |
1.26      thorpej   287:                    (msqbuf->msg_perm.mode & 0777);
                    288:                msqptr->msg_qbytes = msqbuf->msg_qbytes;
1.3       mycroft   289:                msqptr->msg_ctime = time.tv_sec;
                    290:                break;
1.1       cgd       291:
1.3       mycroft   292:        case IPC_STAT:
1.26      thorpej   293:                if ((error = ipcperm(cred, &msqptr->msg_perm, IPC_R))) {
1.20      christos  294:                        MSG_PRINTF(("requester doesn't have read access\n"));
1.26      thorpej   295:                        return (error);
1.3       mycroft   296:                }
1.26      thorpej   297:                memcpy(msqbuf, msqptr, sizeof(struct msqid_ds));
1.3       mycroft   298:                break;
1.1       cgd       299:
1.3       mycroft   300:        default:
1.20      christos  301:                MSG_PRINTF(("invalid command %d\n", cmd));
1.26      thorpej   302:                return (EINVAL);
1.3       mycroft   303:        }
                    304:
1.26      thorpej   305:        return (error);
1.1       cgd       306: }
                    307:
                    308: int
1.40      thorpej   309: sys_msgget(struct lwp *l, void *v, register_t *retval)
1.16      thorpej   310: {
1.26      thorpej   311:        struct sys_msgget_args /* {
1.10      cgd       312:                syscallarg(key_t) key;
                    313:                syscallarg(int) msgflg;
1.16      thorpej   314:        } */ *uap = v;
1.35      thorpej   315:        struct proc *p = l->l_proc;
1.26      thorpej   316:        int msqid, error;
1.10      cgd       317:        int key = SCARG(uap, key);
                    318:        int msgflg = SCARG(uap, msgflg);
1.3       mycroft   319:        struct ucred *cred = p->p_ucred;
1.26      thorpej   320:        struct msqid_ds *msqptr = NULL;
1.1       cgd       321:
1.20      christos  322:        MSG_PRINTF(("msgget(0x%x, 0%o)\n", key, msgflg));
1.1       cgd       323:
1.5       mycroft   324:        if (key != IPC_PRIVATE) {
                    325:                for (msqid = 0; msqid < msginfo.msgmni; msqid++) {
1.3       mycroft   326:                        msqptr = &msqids[msqid];
                    327:                        if (msqptr->msg_qbytes != 0 &&
1.26      thorpej   328:                            msqptr->msg_perm._key == key)
1.3       mycroft   329:                                break;
                    330:                }
                    331:                if (msqid < msginfo.msgmni) {
1.20      christos  332:                        MSG_PRINTF(("found public key\n"));
1.3       mycroft   333:                        if ((msgflg & IPC_CREAT) && (msgflg & IPC_EXCL)) {
1.20      christos  334:                                MSG_PRINTF(("not exclusive\n"));
1.3       mycroft   335:                                return(EEXIST);
                    336:                        }
1.26      thorpej   337:                        if ((error = ipcperm(cred, &msqptr->msg_perm,
                    338:                            msgflg & 0700 ))) {
1.20      christos  339:                                MSG_PRINTF(("requester doesn't have 0%o access\n",
                    340:                                    msgflg & 0700));
1.26      thorpej   341:                                return (error);
1.3       mycroft   342:                        }
1.5       mycroft   343:                        goto found;
1.3       mycroft   344:                }
1.1       cgd       345:        }
                    346:
1.20      christos  347:        MSG_PRINTF(("need to allocate the msqid_ds\n"));
1.5       mycroft   348:        if (key == IPC_PRIVATE || (msgflg & IPC_CREAT)) {
                    349:                for (msqid = 0; msqid < msginfo.msgmni; msqid++) {
                    350:                        /*
                    351:                         * Look for an unallocated and unlocked msqid_ds.
                    352:                         * msqid_ds's can be locked by msgsnd or msgrcv while
                    353:                         * they are copying the message in/out.  We can't
                    354:                         * re-use the entry until they release it.
                    355:                         */
                    356:                        msqptr = &msqids[msqid];
                    357:                        if (msqptr->msg_qbytes == 0 &&
                    358:                            (msqptr->msg_perm.mode & MSG_LOCKED) == 0)
                    359:                                break;
                    360:                }
                    361:                if (msqid == msginfo.msgmni) {
1.20      christos  362:                        MSG_PRINTF(("no more msqid_ds's available\n"));
1.38      perry     363:                        return (ENOSPC);
1.5       mycroft   364:                }
1.20      christos  365:                MSG_PRINTF(("msqid %d is available\n", msqid));
1.26      thorpej   366:                msqptr->msg_perm._key = key;
1.5       mycroft   367:                msqptr->msg_perm.cuid = cred->cr_uid;
                    368:                msqptr->msg_perm.uid = cred->cr_uid;
                    369:                msqptr->msg_perm.cgid = cred->cr_gid;
                    370:                msqptr->msg_perm.gid = cred->cr_gid;
                    371:                msqptr->msg_perm.mode = (msgflg & 0777);
                    372:                /* Make sure that the returned msqid is unique */
1.26      thorpej   373:                msqptr->msg_perm._seq++;
                    374:                msqptr->_msg_first = NULL;
                    375:                msqptr->_msg_last = NULL;
                    376:                msqptr->_msg_cbytes = 0;
1.5       mycroft   377:                msqptr->msg_qnum = 0;
                    378:                msqptr->msg_qbytes = msginfo.msgmnb;
                    379:                msqptr->msg_lspid = 0;
                    380:                msqptr->msg_lrpid = 0;
                    381:                msqptr->msg_stime = 0;
                    382:                msqptr->msg_rtime = 0;
                    383:                msqptr->msg_ctime = time.tv_sec;
                    384:        } else {
1.20      christos  385:                MSG_PRINTF(("didn't find it and wasn't asked to create it\n"));
1.26      thorpej   386:                return (ENOENT);
1.1       cgd       387:        }
                    388:
1.26      thorpej   389:  found:
1.3       mycroft   390:        /* Construct the unique msqid */
                    391:        *retval = IXSEQ_TO_IPCID(msqid, msqptr->msg_perm);
1.26      thorpej   392:        return (0);
1.1       cgd       393: }
                    394:
                    395: int
1.40      thorpej   396: sys_msgsnd(struct lwp *l, void *v, register_t *retval)
1.16      thorpej   397: {
1.26      thorpej   398:        struct sys_msgsnd_args /* {
1.10      cgd       399:                syscallarg(int) msqid;
1.22      kleink    400:                syscallarg(const void *) msgp;
1.10      cgd       401:                syscallarg(size_t) msgsz;
                    402:                syscallarg(int) msgflg;
1.16      thorpej   403:        } */ *uap = v;
1.41    ! cube      404:
        !           405:        return msgsnd1(l->l_proc, SCARG(uap, msqid), SCARG(uap, msgp),
        !           406:            SCARG(uap, msgsz), SCARG(uap, msgflg), sizeof(long), copyin);
        !           407: }
        !           408:
        !           409: int
        !           410: msgsnd1(struct proc *p, int msqidr, const char *user_msgp, size_t msgsz,
        !           411:     int msgflg, size_t typesz, copyin_t fetch_type)
        !           412: {
        !           413:        int segs_needed, error, msqid;
1.3       mycroft   414:        struct ucred *cred = p->p_ucred;
1.26      thorpej   415:        struct msqid_ds *msqptr;
                    416:        struct __msg *msghdr;
1.3       mycroft   417:        short next;
1.1       cgd       418:
1.34      nathanw   419:        MSG_PRINTF(("call to msgsnd(%d, %p, %lld, %d)\n", msqid, user_msgp,
                    420:            (long long)msgsz, msgflg));
1.1       cgd       421:
1.41    ! cube      422:        msqid = IPCID_TO_IX(msqidr);
1.1       cgd       423:
1.3       mycroft   424:        if (msqid < 0 || msqid >= msginfo.msgmni) {
1.20      christos  425:                MSG_PRINTF(("msqid (%d) out of range (0<=msqid<%d)\n", msqid,
                    426:                    msginfo.msgmni));
1.26      thorpej   427:                return (EINVAL);
1.3       mycroft   428:        }
1.1       cgd       429:
1.3       mycroft   430:        msqptr = &msqids[msqid];
                    431:        if (msqptr->msg_qbytes == 0) {
1.20      christos  432:                MSG_PRINTF(("no such message queue id\n"));
1.26      thorpej   433:                return (EINVAL);
1.3       mycroft   434:        }
1.41    ! cube      435:        if (msqptr->msg_perm._seq != IPCID_TO_SEQ(msqidr)) {
1.20      christos  436:                MSG_PRINTF(("wrong sequence number\n"));
1.26      thorpej   437:                return (EINVAL);
1.3       mycroft   438:        }
1.1       cgd       439:
1.26      thorpej   440:        if ((error = ipcperm(cred, &msqptr->msg_perm, IPC_W))) {
1.20      christos  441:                MSG_PRINTF(("requester doesn't have write access\n"));
1.26      thorpej   442:                return (error);
1.3       mycroft   443:        }
1.1       cgd       444:
1.3       mycroft   445:        segs_needed = (msgsz + msginfo.msgssz - 1) / msginfo.msgssz;
1.34      nathanw   446:        MSG_PRINTF(("msgsz=%lld, msgssz=%d, segs_needed=%d\n",
                    447:            (long long)msgsz, msginfo.msgssz, segs_needed));
1.3       mycroft   448:        for (;;) {
                    449:                int need_more_resources = 0;
1.1       cgd       450:
1.3       mycroft   451:                /*
1.18      christos  452:                 * check msgsz [cannot be negative since it is unsigned]
1.3       mycroft   453:                 * (inside this loop in case msg_qbytes changes while we sleep)
                    454:                 */
1.1       cgd       455:
1.18      christos  456:                if (msgsz > msqptr->msg_qbytes) {
1.20      christos  457:                        MSG_PRINTF(("msgsz > msqptr->msg_qbytes\n"));
1.26      thorpej   458:                        return (EINVAL);
1.3       mycroft   459:                }
1.1       cgd       460:
1.3       mycroft   461:                if (msqptr->msg_perm.mode & MSG_LOCKED) {
1.20      christos  462:                        MSG_PRINTF(("msqid is locked\n"));
1.3       mycroft   463:                        need_more_resources = 1;
                    464:                }
1.26      thorpej   465:                if (msgsz + msqptr->_msg_cbytes > msqptr->msg_qbytes) {
1.20      christos  466:                        MSG_PRINTF(("msgsz + msg_cbytes > msg_qbytes\n"));
1.3       mycroft   467:                        need_more_resources = 1;
                    468:                }
                    469:                if (segs_needed > nfree_msgmaps) {
1.20      christos  470:                        MSG_PRINTF(("segs_needed > nfree_msgmaps\n"));
1.3       mycroft   471:                        need_more_resources = 1;
                    472:                }
                    473:                if (free_msghdrs == NULL) {
1.20      christos  474:                        MSG_PRINTF(("no more msghdrs\n"));
1.3       mycroft   475:                        need_more_resources = 1;
                    476:                }
1.1       cgd       477:
1.3       mycroft   478:                if (need_more_resources) {
                    479:                        int we_own_it;
1.1       cgd       480:
1.3       mycroft   481:                        if ((msgflg & IPC_NOWAIT) != 0) {
1.26      thorpej   482:                                MSG_PRINTF(("need more resources but caller "
                    483:                                    "doesn't want to wait\n"));
                    484:                                return (EAGAIN);
1.3       mycroft   485:                        }
1.1       cgd       486:
1.3       mycroft   487:                        if ((msqptr->msg_perm.mode & MSG_LOCKED) != 0) {
1.20      christos  488:                                MSG_PRINTF(("we don't own the msqid_ds\n"));
1.3       mycroft   489:                                we_own_it = 0;
                    490:                        } else {
                    491:                                /* Force later arrivals to wait for our
                    492:                                   request */
1.20      christos  493:                                MSG_PRINTF(("we own the msqid_ds\n"));
1.3       mycroft   494:                                msqptr->msg_perm.mode |= MSG_LOCKED;
                    495:                                we_own_it = 1;
                    496:                        }
1.20      christos  497:                        MSG_PRINTF(("goodnight\n"));
1.26      thorpej   498:                        error = tsleep(msqptr, (PZERO - 4) | PCATCH,
1.3       mycroft   499:                            "msgwait", 0);
1.26      thorpej   500:                        MSG_PRINTF(("good morning, error=%d\n", error));
1.3       mycroft   501:                        if (we_own_it)
                    502:                                msqptr->msg_perm.mode &= ~MSG_LOCKED;
1.26      thorpej   503:                        if (error != 0) {
                    504:                                MSG_PRINTF(("msgsnd: interrupted system "
                    505:                                    "call\n"));
                    506:                                return (EINTR);
1.3       mycroft   507:                        }
1.1       cgd       508:
1.3       mycroft   509:                        /*
                    510:                         * Make sure that the msq queue still exists
                    511:                         */
1.1       cgd       512:
1.3       mycroft   513:                        if (msqptr->msg_qbytes == 0) {
1.20      christos  514:                                MSG_PRINTF(("msqid deleted\n"));
1.26      thorpej   515:                                return (EIDRM);
1.3       mycroft   516:                        }
                    517:                } else {
1.20      christos  518:                        MSG_PRINTF(("got all the resources that we need\n"));
1.3       mycroft   519:                        break;
                    520:                }
1.1       cgd       521:        }
                    522:
1.3       mycroft   523:        /*
                    524:         * We have the resources that we need.
                    525:         * Make sure!
                    526:         */
1.1       cgd       527:
1.3       mycroft   528:        if (msqptr->msg_perm.mode & MSG_LOCKED)
                    529:                panic("msg_perm.mode & MSG_LOCKED");
                    530:        if (segs_needed > nfree_msgmaps)
                    531:                panic("segs_needed > nfree_msgmaps");
1.26      thorpej   532:        if (msgsz + msqptr->_msg_cbytes > msqptr->msg_qbytes)
1.3       mycroft   533:                panic("msgsz + msg_cbytes > msg_qbytes");
                    534:        if (free_msghdrs == NULL)
                    535:                panic("no more msghdrs");
1.1       cgd       536:
1.3       mycroft   537:        /*
                    538:         * Re-lock the msqid_ds in case we page-fault when copying in the
                    539:         * message
                    540:         */
1.1       cgd       541:
1.3       mycroft   542:        if ((msqptr->msg_perm.mode & MSG_LOCKED) != 0)
                    543:                panic("msqid_ds is already locked");
                    544:        msqptr->msg_perm.mode |= MSG_LOCKED;
1.1       cgd       545:
1.3       mycroft   546:        /*
                    547:         * Allocate a message header
                    548:         */
1.1       cgd       549:
1.3       mycroft   550:        msghdr = free_msghdrs;
                    551:        free_msghdrs = msghdr->msg_next;
                    552:        msghdr->msg_spot = -1;
                    553:        msghdr->msg_ts = msgsz;
1.1       cgd       554:
1.3       mycroft   555:        /*
                    556:         * Allocate space for the message
                    557:         */
1.1       cgd       558:
1.3       mycroft   559:        while (segs_needed > 0) {
                    560:                if (nfree_msgmaps <= 0)
                    561:                        panic("not enough msgmaps");
                    562:                if (free_msgmaps == -1)
                    563:                        panic("nil free_msgmaps");
                    564:                next = free_msgmaps;
                    565:                if (next <= -1)
                    566:                        panic("next too low #1");
                    567:                if (next >= msginfo.msgseg)
                    568:                        panic("next out of range #1");
1.20      christos  569:                MSG_PRINTF(("allocating segment %d to message\n", next));
1.3       mycroft   570:                free_msgmaps = msgmaps[next].next;
1.5       mycroft   571:                nfree_msgmaps--;
1.3       mycroft   572:                msgmaps[next].next = msghdr->msg_spot;
                    573:                msghdr->msg_spot = next;
1.5       mycroft   574:                segs_needed--;
1.1       cgd       575:        }
                    576:
1.3       mycroft   577:        /*
                    578:         * Copy in the message type
                    579:         */
1.1       cgd       580:
1.41    ! cube      581:        if ((error = (*fetch_type)(user_msgp, &msghdr->msg_type,
        !           582:            typesz)) != 0) {
1.26      thorpej   583:                MSG_PRINTF(("error %d copying the message type\n", error));
1.3       mycroft   584:                msg_freehdr(msghdr);
                    585:                msqptr->msg_perm.mode &= ~MSG_LOCKED;
1.26      thorpej   586:                wakeup(msqptr);
                    587:                return (error);
1.3       mycroft   588:        }
1.41    ! cube      589:        user_msgp += typesz;
1.1       cgd       590:
1.3       mycroft   591:        /*
                    592:         * Validate the message type
                    593:         */
1.1       cgd       594:
1.3       mycroft   595:        if (msghdr->msg_type < 1) {
                    596:                msg_freehdr(msghdr);
                    597:                msqptr->msg_perm.mode &= ~MSG_LOCKED;
1.26      thorpej   598:                wakeup(msqptr);
1.34      nathanw   599:                MSG_PRINTF(("mtype (%ld) < 1\n", msghdr->msg_type));
1.26      thorpej   600:                return (EINVAL);
1.3       mycroft   601:        }
1.1       cgd       602:
1.3       mycroft   603:        /*
                    604:         * Copy in the message body
                    605:         */
                    606:
                    607:        next = msghdr->msg_spot;
                    608:        while (msgsz > 0) {
                    609:                size_t tlen;
                    610:                if (msgsz > msginfo.msgssz)
                    611:                        tlen = msginfo.msgssz;
                    612:                else
                    613:                        tlen = msgsz;
                    614:                if (next <= -1)
                    615:                        panic("next too low #2");
                    616:                if (next >= msginfo.msgseg)
                    617:                        panic("next out of range #2");
1.26      thorpej   618:                if ((error = copyin(user_msgp, &msgpool[next * msginfo.msgssz],
1.3       mycroft   619:                    tlen)) != 0) {
1.26      thorpej   620:                        MSG_PRINTF(("error %d copying in message segment\n",
                    621:                            error));
1.3       mycroft   622:                        msg_freehdr(msghdr);
                    623:                        msqptr->msg_perm.mode &= ~MSG_LOCKED;
1.26      thorpej   624:                        wakeup(msqptr);
                    625:                        return (error);
1.3       mycroft   626:                }
                    627:                msgsz -= tlen;
                    628:                user_msgp += tlen;
                    629:                next = msgmaps[next].next;
1.1       cgd       630:        }
1.3       mycroft   631:        if (next != -1)
                    632:                panic("didn't use all the msg segments");
1.1       cgd       633:
1.3       mycroft   634:        /*
                    635:         * We've got the message.  Unlock the msqid_ds.
                    636:         */
1.1       cgd       637:
1.3       mycroft   638:        msqptr->msg_perm.mode &= ~MSG_LOCKED;
1.1       cgd       639:
1.3       mycroft   640:        /*
                    641:         * Make sure that the msqid_ds is still allocated.
                    642:         */
1.1       cgd       643:
1.3       mycroft   644:        if (msqptr->msg_qbytes == 0) {
                    645:                msg_freehdr(msghdr);
1.26      thorpej   646:                wakeup(msqptr);
                    647:                return (EIDRM);
1.3       mycroft   648:        }
                    649:
                    650:        /*
                    651:         * Put the message into the queue
                    652:         */
1.1       cgd       653:
1.26      thorpej   654:        if (msqptr->_msg_first == NULL) {
                    655:                msqptr->_msg_first = msghdr;
                    656:                msqptr->_msg_last = msghdr;
1.3       mycroft   657:        } else {
1.26      thorpej   658:                msqptr->_msg_last->msg_next = msghdr;
                    659:                msqptr->_msg_last = msghdr;
1.3       mycroft   660:        }
1.26      thorpej   661:        msqptr->_msg_last->msg_next = NULL;
1.3       mycroft   662:
1.26      thorpej   663:        msqptr->_msg_cbytes += msghdr->msg_ts;
1.5       mycroft   664:        msqptr->msg_qnum++;
1.3       mycroft   665:        msqptr->msg_lspid = p->p_pid;
                    666:        msqptr->msg_stime = time.tv_sec;
                    667:
1.26      thorpej   668:        wakeup(msqptr);
                    669:        return (0);
1.1       cgd       670: }
                    671:
                    672: int
1.40      thorpej   673: sys_msgrcv(struct lwp *l, void *v, register_t *retval)
1.16      thorpej   674: {
1.27      augustss  675:        struct sys_msgrcv_args /* {
1.10      cgd       676:                syscallarg(int) msqid;
                    677:                syscallarg(void *) msgp;
                    678:                syscallarg(size_t) msgsz;
                    679:                syscallarg(long) msgtyp;
                    680:                syscallarg(int) msgflg;
1.16      thorpej   681:        } */ *uap = v;
1.41    ! cube      682:
        !           683:        return msgrcv1(l->l_proc, SCARG(uap, msqid), SCARG(uap, msgp),
        !           684:            SCARG(uap, msgsz), SCARG(uap, msgtyp), SCARG(uap, msgflg),
        !           685:            sizeof(long), copyout, retval);
        !           686: }
        !           687:
        !           688: int
        !           689: msgrcv1(struct proc *p, int msqidr, char *user_msgp, size_t msgsz, long msgtyp,
        !           690:     int msgflg, size_t typesz, copyout_t put_type, register_t *retval)
        !           691: {
1.3       mycroft   692:        size_t len;
                    693:        struct ucred *cred = p->p_ucred;
1.27      augustss  694:        struct msqid_ds *msqptr;
                    695:        struct __msg *msghdr;
1.41    ! cube      696:        int error, msqid;
1.3       mycroft   697:        short next;
1.1       cgd       698:
1.34      nathanw   699:        MSG_PRINTF(("call to msgrcv(%d, %p, %lld, %ld, %d)\n", msqid,
                    700:            user_msgp, (long long)msgsz, msgtyp, msgflg));
1.1       cgd       701:
1.41    ! cube      702:        msqid = IPCID_TO_IX(msqidr);
1.1       cgd       703:
1.3       mycroft   704:        if (msqid < 0 || msqid >= msginfo.msgmni) {
1.20      christos  705:                MSG_PRINTF(("msqid (%d) out of range (0<=msqid<%d)\n", msqid,
                    706:                    msginfo.msgmni));
1.26      thorpej   707:                return (EINVAL);
1.3       mycroft   708:        }
1.1       cgd       709:
1.3       mycroft   710:        msqptr = &msqids[msqid];
                    711:        if (msqptr->msg_qbytes == 0) {
1.20      christos  712:                MSG_PRINTF(("no such message queue id\n"));
1.26      thorpej   713:                return (EINVAL);
1.3       mycroft   714:        }
1.41    ! cube      715:        if (msqptr->msg_perm._seq != IPCID_TO_SEQ(msqidr)) {
1.20      christos  716:                MSG_PRINTF(("wrong sequence number\n"));
1.26      thorpej   717:                return (EINVAL);
1.3       mycroft   718:        }
1.1       cgd       719:
1.26      thorpej   720:        if ((error = ipcperm(cred, &msqptr->msg_perm, IPC_R))) {
1.20      christos  721:                MSG_PRINTF(("requester doesn't have read access\n"));
1.26      thorpej   722:                return (error);
1.3       mycroft   723:        }
1.1       cgd       724:
1.18      christos  725: #if 0
                    726:        /* cannot happen, msgsz is unsigned */
1.3       mycroft   727:        if (msgsz < 0) {
1.20      christos  728:                MSG_PRINTF(("msgsz < 0\n"));
1.26      thorpej   729:                return (EINVAL);
1.3       mycroft   730:        }
1.18      christos  731: #endif
1.1       cgd       732:
1.3       mycroft   733:        msghdr = NULL;
                    734:        while (msghdr == NULL) {
                    735:                if (msgtyp == 0) {
1.26      thorpej   736:                        msghdr = msqptr->_msg_first;
1.3       mycroft   737:                        if (msghdr != NULL) {
                    738:                                if (msgsz < msghdr->msg_ts &&
                    739:                                    (msgflg & MSG_NOERROR) == 0) {
1.26      thorpej   740:                                        MSG_PRINTF(("first message on the "
                    741:                                            "queue is too big "
1.34      nathanw   742:                                            "(want %lld, got %d)\n",
                    743:                                            (long long)msgsz, msghdr->msg_ts));
1.26      thorpej   744:                                        return (E2BIG);
1.3       mycroft   745:                                }
1.26      thorpej   746:                                if (msqptr->_msg_first == msqptr->_msg_last) {
                    747:                                        msqptr->_msg_first = NULL;
                    748:                                        msqptr->_msg_last = NULL;
1.3       mycroft   749:                                } else {
1.26      thorpej   750:                                        msqptr->_msg_first = msghdr->msg_next;
                    751:                                        if (msqptr->_msg_first == NULL)
                    752:                                                panic("msg_first/last screwed "
                    753:                                                    "up #1");
1.3       mycroft   754:                                }
                    755:                        }
                    756:                } else {
1.26      thorpej   757:                        struct __msg *previous;
                    758:                        struct __msg **prev;
1.1       cgd       759:
1.26      thorpej   760:                        for (previous = NULL, prev = &msqptr->_msg_first;
1.12      mycroft   761:                             (msghdr = *prev) != NULL;
                    762:                             previous = msghdr, prev = &msghdr->msg_next) {
1.3       mycroft   763:                                /*
                    764:                                 * Is this message's type an exact match or is
                    765:                                 * this message's type less than or equal to
                    766:                                 * the absolute value of a negative msgtyp?
                    767:                                 * Note that the second half of this test can
                    768:                                 * NEVER be true if msgtyp is positive since
                    769:                                 * msg_type is always positive!
                    770:                                 */
                    771:
                    772:                                if (msgtyp == msghdr->msg_type ||
                    773:                                    msghdr->msg_type <= -msgtyp) {
1.34      nathanw   774:                                        MSG_PRINTF(("found message type %ld, "
                    775:                                            "requested %ld\n",
1.20      christos  776:                                            msghdr->msg_type, msgtyp));
1.3       mycroft   777:                                        if (msgsz < msghdr->msg_ts &&
                    778:                                            (msgflg & MSG_NOERROR) == 0) {
1.26      thorpej   779:                                                MSG_PRINTF(("requested message "
                    780:                                                    "on the queue is too big "
1.34      nathanw   781:                                                    "(want %lld, got %d)\n",
                    782:                                                    (long long)msgsz,
                    783:                                                    msghdr->msg_ts));
1.26      thorpej   784:                                                return (E2BIG);
1.3       mycroft   785:                                        }
                    786:                                        *prev = msghdr->msg_next;
1.26      thorpej   787:                                        if (msghdr == msqptr->_msg_last) {
1.3       mycroft   788:                                                if (previous == NULL) {
                    789:                                                        if (prev !=
1.26      thorpej   790:                                                            &msqptr->_msg_first)
1.3       mycroft   791:                                                                panic("msg_first/last screwed up #2");
1.26      thorpej   792:                                                        msqptr->_msg_first =
1.3       mycroft   793:                                                            NULL;
1.26      thorpej   794:                                                        msqptr->_msg_last =
1.3       mycroft   795:                                                            NULL;
                    796:                                                } else {
                    797:                                                        if (prev ==
1.26      thorpej   798:                                                            &msqptr->_msg_first)
1.3       mycroft   799:                                                                panic("msg_first/last screwed up #3");
1.26      thorpej   800:                                                        msqptr->_msg_last =
1.3       mycroft   801:                                                            previous;
                    802:                                                }
                    803:                                        }
                    804:                                        break;
                    805:                                }
                    806:                        }
1.1       cgd       807:                }
                    808:
1.3       mycroft   809:                /*
                    810:                 * We've either extracted the msghdr for the appropriate
                    811:                 * message or there isn't one.
                    812:                 * If there is one then bail out of this loop.
                    813:                 */
1.1       cgd       814:
1.3       mycroft   815:                if (msghdr != NULL)
                    816:                        break;
1.1       cgd       817:
                    818:                /*
1.3       mycroft   819:                 * Hmph!  No message found.  Does the user want to wait?
1.1       cgd       820:                 */
                    821:
1.3       mycroft   822:                if ((msgflg & IPC_NOWAIT) != 0) {
1.34      nathanw   823:                        MSG_PRINTF(("no appropriate message found (msgtyp=%ld)\n",
1.20      christos  824:                            msgtyp));
1.3       mycroft   825:                        /* The SVID says to return ENOMSG. */
1.1       cgd       826: #ifdef ENOMSG
1.26      thorpej   827:                        return (ENOMSG);
1.1       cgd       828: #else
1.3       mycroft   829:                        /* Unfortunately, BSD doesn't define that code yet! */
1.26      thorpej   830:                        return (EAGAIN);
1.1       cgd       831: #endif
1.3       mycroft   832:                }
1.1       cgd       833:
1.3       mycroft   834:                /*
                    835:                 * Wait for something to happen
                    836:                 */
1.1       cgd       837:
1.20      christos  838:                MSG_PRINTF(("msgrcv:  goodnight\n"));
1.26      thorpej   839:                error = tsleep(msqptr, (PZERO - 4) | PCATCH, "msgwait",
1.3       mycroft   840:                    0);
1.26      thorpej   841:                MSG_PRINTF(("msgrcv: good morning (error=%d)\n", error));
1.1       cgd       842:
1.26      thorpej   843:                if (error != 0) {
                    844:                        MSG_PRINTF(("msgsnd: interrupted system call\n"));
                    845:                        return (EINTR);
1.3       mycroft   846:                }
1.1       cgd       847:
1.3       mycroft   848:                /*
                    849:                 * Make sure that the msq queue still exists
                    850:                 */
1.1       cgd       851:
1.3       mycroft   852:                if (msqptr->msg_qbytes == 0 ||
1.41    ! cube      853:                    msqptr->msg_perm._seq != IPCID_TO_SEQ(msqidr)) {
1.20      christos  854:                        MSG_PRINTF(("msqid deleted\n"));
1.26      thorpej   855:                        return (EIDRM);
1.3       mycroft   856:                }
1.1       cgd       857:        }
                    858:
1.3       mycroft   859:        /*
                    860:         * Return the message to the user.
                    861:         *
                    862:         * First, do the bookkeeping (before we risk being interrupted).
                    863:         */
1.1       cgd       864:
1.26      thorpej   865:        msqptr->_msg_cbytes -= msghdr->msg_ts;
1.5       mycroft   866:        msqptr->msg_qnum--;
1.3       mycroft   867:        msqptr->msg_lrpid = p->p_pid;
                    868:        msqptr->msg_rtime = time.tv_sec;
1.1       cgd       869:
1.3       mycroft   870:        /*
                    871:         * Make msgsz the actual amount that we'll be returning.
                    872:         * Note that this effectively truncates the message if it is too long
                    873:         * (since msgsz is never increased).
                    874:         */
1.1       cgd       875:
1.34      nathanw   876:        MSG_PRINTF(("found a message, msgsz=%lld, msg_ts=%d\n",
                    877:            (long long)msgsz, msghdr->msg_ts));
1.3       mycroft   878:        if (msgsz > msghdr->msg_ts)
                    879:                msgsz = msghdr->msg_ts;
1.1       cgd       880:
1.3       mycroft   881:        /*
                    882:         * Return the type to the user.
                    883:         */
1.1       cgd       884:
1.41    ! cube      885:        error = (*put_type)(&msghdr->msg_type, user_msgp, typesz);
1.26      thorpej   886:        if (error != 0) {
                    887:                MSG_PRINTF(("error (%d) copying out message type\n", error));
1.3       mycroft   888:                msg_freehdr(msghdr);
1.26      thorpej   889:                wakeup(msqptr);
                    890:                return (error);
1.3       mycroft   891:        }
1.41    ! cube      892:        user_msgp += typesz;
1.3       mycroft   893:
                    894:        /*
                    895:         * Return the segments to the user
                    896:         */
1.1       cgd       897:
1.3       mycroft   898:        next = msghdr->msg_spot;
                    899:        for (len = 0; len < msgsz; len += msginfo.msgssz) {
                    900:                size_t tlen;
                    901:
1.25      mrg       902:                if (msgsz - len > msginfo.msgssz)
1.3       mycroft   903:                        tlen = msginfo.msgssz;
                    904:                else
1.25      mrg       905:                        tlen = msgsz - len;
1.3       mycroft   906:                if (next <= -1)
                    907:                        panic("next too low #3");
                    908:                if (next >= msginfo.msgseg)
                    909:                        panic("next out of range #3");
1.26      thorpej   910:                error = copyout(&msgpool[next * msginfo.msgssz],
1.3       mycroft   911:                    user_msgp, tlen);
1.26      thorpej   912:                if (error != 0) {
1.20      christos  913:                        MSG_PRINTF(("error (%d) copying out message segment\n",
1.26      thorpej   914:                            error));
1.3       mycroft   915:                        msg_freehdr(msghdr);
1.26      thorpej   916:                        wakeup(msqptr);
                    917:                        return (error);
1.3       mycroft   918:                }
                    919:                user_msgp += tlen;
                    920:                next = msgmaps[next].next;
1.1       cgd       921:        }
                    922:
1.3       mycroft   923:        /*
                    924:         * Done, return the actual number of bytes copied out.
                    925:         */
1.1       cgd       926:
1.3       mycroft   927:        msg_freehdr(msghdr);
1.26      thorpej   928:        wakeup(msqptr);
1.3       mycroft   929:        *retval = msgsz;
1.26      thorpej   930:        return (0);
1.1       cgd       931: }

CVSweb <webmaster@jp.NetBSD.org>