The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/kern/sysv_msg.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * Implementation of SVID messages
    3  *
    4  * Author:  Daniel Boulet
    5  *
    6  * Copyright 1993 Daniel Boulet and RTMX Inc.
    7  *
    8  * This system call was implemented by Daniel Boulet under contract from RTMX.
    9  *
   10  * Redistribution and use in source forms, with and without modification,
   11  * are permitted provided that this entire comment appears intact.
   12  *
   13  * Redistribution in binary form may occur without any restrictions.
   14  * Obviously, it would be nice if you gave credit where credit is due
   15  * but requiring it would be too onerous.
   16  *
   17  * This software is provided ``AS IS'' without any warranties of any kind.
   18  */
   19 
   20 #include <sys/cdefs.h>
   21 __FBSDID("$FreeBSD$");
   22 
   23 #include "opt_sysvipc.h"
   24 
   25 #include <sys/param.h>
   26 #include <sys/systm.h>
   27 #include <sys/sysproto.h>
   28 #include <sys/kernel.h>
   29 #include <sys/proc.h>
   30 #include <sys/lock.h>
   31 #include <sys/mutex.h>
   32 #include <sys/module.h>
   33 #include <sys/msg.h>
   34 #include <sys/syscall.h>
   35 #include <sys/syscallsubr.h>
   36 #include <sys/sysent.h>
   37 #include <sys/sysctl.h>
   38 #include <sys/malloc.h>
   39 #include <sys/jail.h>
   40 
   41 static MALLOC_DEFINE(M_MSG, "msg", "SVID compatible message queues");
   42 
   43 static void msginit(void);
   44 static int msgunload(void);
   45 static int sysvmsg_modload(struct module *, int, void *);
   46 
   47 #ifdef MSG_DEBUG
   48 #define DPRINTF(a)      printf a
   49 #else
   50 #define DPRINTF(a)
   51 #endif
   52 
   53 static void msg_freehdr(struct msg *msghdr);
   54 
   55 /* XXX casting to (sy_call_t *) is bogus, as usual. */
   56 static sy_call_t *msgcalls[] = {
   57         (sy_call_t *)msgctl, (sy_call_t *)msgget,
   58         (sy_call_t *)msgsnd, (sy_call_t *)msgrcv
   59 };
   60 
   61 #ifndef MSGSSZ
   62 #define MSGSSZ  8               /* Each segment must be 2^N long */
   63 #endif
   64 #ifndef MSGSEG
   65 #define MSGSEG  2048            /* must be less than 32767 */
   66 #endif
   67 #define MSGMAX  (MSGSSZ*MSGSEG)
   68 #ifndef MSGMNB
   69 #define MSGMNB  2048            /* max # of bytes in a queue */
   70 #endif
   71 #ifndef MSGMNI
   72 #define MSGMNI  40
   73 #endif
   74 #ifndef MSGTQL
   75 #define MSGTQL  40
   76 #endif
   77 
   78 /*
   79  * Based on the configuration parameters described in an SVR2 (yes, two)
   80  * config(1m) man page.
   81  *
   82  * Each message is broken up and stored in segments that are msgssz bytes
   83  * long.  For efficiency reasons, this should be a power of two.  Also,
   84  * it doesn't make sense if it is less than 8 or greater than about 256.
   85  * Consequently, msginit in kern/sysv_msg.c checks that msgssz is a power of
   86  * two between 8 and 1024 inclusive (and panic's if it isn't).
   87  */
   88 struct msginfo msginfo = {
   89                 MSGMAX,         /* max chars in a message */
   90                 MSGMNI,         /* # of message queue identifiers */
   91                 MSGMNB,         /* max chars in a queue */
   92                 MSGTQL,         /* max messages in system */
   93                 MSGSSZ,         /* size of a message segment */
   94                                 /* (must be small power of 2 greater than 4) */
   95                 MSGSEG          /* number of message segments */
   96 };
   97 
   98 /*
   99  * macros to convert between msqid_ds's and msqid's.
  100  * (specific to this implementation)
  101  */
  102 #define MSQID(ix,ds)    ((ix) & 0xffff | (((ds).msg_perm.seq << 16) & 0xffff0000))
  103 #define MSQID_IX(id)    ((id) & 0xffff)
  104 #define MSQID_SEQ(id)   (((id) >> 16) & 0xffff)
  105 
  106 /*
  107  * The rest of this file is specific to this particular implementation.
  108  */
  109 
  110 struct msgmap {
  111         short   next;           /* next segment in buffer */
  112                                 /* -1 -> available */
  113                                 /* 0..(MSGSEG-1) -> index of next segment */
  114 };
  115 
  116 #define MSG_LOCKED      01000   /* Is this msqid_ds locked? */
  117 
  118 static int nfree_msgmaps;       /* # of free map entries */
  119 static short free_msgmaps;      /* head of linked list of free map entries */
  120 static struct msg *free_msghdrs;/* list of free msg headers */
  121 static char *msgpool;           /* MSGMAX byte long msg buffer pool */
  122 static struct msgmap *msgmaps;  /* MSGSEG msgmap structures */
  123 static struct msg *msghdrs;     /* MSGTQL msg headers */
  124 static struct msqid_kernel *msqids;     /* MSGMNI msqid_kernel struct's */
  125 static struct mtx msq_mtx;      /* global mutex for message queues. */
  126 
  127 static void
  128 msginit()
  129 {
  130         register int i;
  131 
  132         TUNABLE_INT_FETCH("kern.ipc.msgseg", &msginfo.msgseg);
  133         TUNABLE_INT_FETCH("kern.ipc.msgssz", &msginfo.msgssz);
  134         msginfo.msgmax = msginfo.msgseg * msginfo.msgssz;
  135         TUNABLE_INT_FETCH("kern.ipc.msgmni", &msginfo.msgmni);
  136         TUNABLE_INT_FETCH("kern.ipc.msgmnb", &msginfo.msgmnb);
  137         TUNABLE_INT_FETCH("kern.ipc.msgtql", &msginfo.msgtql);
  138 
  139         msgpool = malloc(msginfo.msgmax, M_MSG, M_WAITOK);
  140         if (msgpool == NULL)
  141                 panic("msgpool is NULL");
  142         msgmaps = malloc(sizeof(struct msgmap) * msginfo.msgseg, M_MSG, M_WAITOK);
  143         if (msgmaps == NULL)
  144                 panic("msgmaps is NULL");
  145         msghdrs = malloc(sizeof(struct msg) * msginfo.msgtql, M_MSG, M_WAITOK);
  146         if (msghdrs == NULL)
  147                 panic("msghdrs is NULL");
  148         msqids = malloc(sizeof(struct msqid_kernel) * msginfo.msgmni, M_MSG,
  149             M_WAITOK);
  150         if (msqids == NULL)
  151                 panic("msqids is NULL");
  152 
  153         /*
  154          * msginfo.msgssz should be a power of two for efficiency reasons.
  155          * It is also pretty silly if msginfo.msgssz is less than 8
  156          * or greater than about 256 so ...
  157          */
  158 
  159         i = 8;
  160         while (i < 1024 && i != msginfo.msgssz)
  161                 i <<= 1;
  162         if (i != msginfo.msgssz) {
  163                 DPRINTF(("msginfo.msgssz=%d (0x%x)\n", msginfo.msgssz,
  164                     msginfo.msgssz));
  165                 panic("msginfo.msgssz not a small power of 2");
  166         }
  167 
  168         if (msginfo.msgseg > 32767) {
  169                 DPRINTF(("msginfo.msgseg=%d\n", msginfo.msgseg));
  170                 panic("msginfo.msgseg > 32767");
  171         }
  172 
  173         if (msgmaps == NULL)
  174                 panic("msgmaps is NULL");
  175 
  176         for (i = 0; i < msginfo.msgseg; i++) {
  177                 if (i > 0)
  178                         msgmaps[i-1].next = i;
  179                 msgmaps[i].next = -1;   /* implies entry is available */
  180         }
  181         free_msgmaps = 0;
  182         nfree_msgmaps = msginfo.msgseg;
  183 
  184         if (msghdrs == NULL)
  185                 panic("msghdrs is NULL");
  186 
  187         for (i = 0; i < msginfo.msgtql; i++) {
  188                 msghdrs[i].msg_type = 0;
  189                 if (i > 0)
  190                         msghdrs[i-1].msg_next = &msghdrs[i];
  191                 msghdrs[i].msg_next = NULL;
  192         }
  193         free_msghdrs = &msghdrs[0];
  194 
  195         if (msqids == NULL)
  196                 panic("msqids is NULL");
  197 
  198         for (i = 0; i < msginfo.msgmni; i++) {
  199                 msqids[i].u.msg_qbytes = 0;     /* implies entry is available */
  200                 msqids[i].u.msg_perm.seq = 0;   /* reset to a known value */
  201                 msqids[i].u.msg_perm.mode = 0;
  202         }
  203         mtx_init(&msq_mtx, "msq", NULL, MTX_DEF);
  204 }
  205 
  206 static int
  207 msgunload()
  208 {
  209         struct msqid_kernel *msqkptr;
  210         int msqid;
  211 
  212         for (msqid = 0; msqid < msginfo.msgmni; msqid++) {
  213                 /*
  214                  * Look for an unallocated and unlocked msqid_ds.
  215                  * msqid_ds's can be locked by msgsnd or msgrcv while
  216                  * they are copying the message in/out.  We can't
  217                  * re-use the entry until they release it.
  218                  */
  219                 msqkptr = &msqids[msqid];
  220                 if (msqkptr->u.msg_qbytes != 0 ||
  221                     (msqkptr->u.msg_perm.mode & MSG_LOCKED) != 0)
  222                         break;
  223         }
  224         if (msqid != msginfo.msgmni)
  225                 return (EBUSY);
  226 
  227         free(msgpool, M_MSG);
  228         free(msgmaps, M_MSG);
  229         free(msghdrs, M_MSG);
  230         free(msqids, M_MSG);
  231         mtx_destroy(&msq_mtx);
  232         return (0);
  233 }
  234 
  235 
  236 static int
  237 sysvmsg_modload(struct module *module, int cmd, void *arg)
  238 {
  239         int error = 0;
  240 
  241         switch (cmd) {
  242         case MOD_LOAD:
  243                 msginit();
  244                 break;
  245         case MOD_UNLOAD:
  246                 error = msgunload();
  247                 break;
  248         case MOD_SHUTDOWN:
  249                 break;
  250         default:
  251                 error = EINVAL;
  252                 break;
  253         }
  254         return (error);
  255 }
  256 
  257 static moduledata_t sysvmsg_mod = {
  258         "sysvmsg",
  259         &sysvmsg_modload,
  260         NULL
  261 };
  262 
  263 SYSCALL_MODULE_HELPER(msgsys);
  264 SYSCALL_MODULE_HELPER(msgctl);
  265 SYSCALL_MODULE_HELPER(msgget);
  266 SYSCALL_MODULE_HELPER(msgsnd);
  267 SYSCALL_MODULE_HELPER(msgrcv);
  268 
  269 DECLARE_MODULE(sysvmsg, sysvmsg_mod,
  270         SI_SUB_SYSV_MSG, SI_ORDER_FIRST);
  271 MODULE_VERSION(sysvmsg, 1);
  272 
  273 /*
  274  * Entry point for all MSG calls
  275  *
  276  * MPSAFE
  277  */
  278 int
  279 msgsys(td, uap)
  280         struct thread *td;
  281         /* XXX actually varargs. */
  282         struct msgsys_args /* {
  283                 int     which;
  284                 int     a2;
  285                 int     a3;
  286                 int     a4;
  287                 int     a5;
  288                 int     a6;
  289         } */ *uap;
  290 {
  291         int error;
  292 
  293         if (!jail_sysvipc_allowed && jailed(td->td_ucred))
  294                 return (ENOSYS);
  295         if (uap->which < 0 ||
  296             uap->which >= sizeof(msgcalls)/sizeof(msgcalls[0]))
  297                 return (EINVAL);
  298         error = (*msgcalls[uap->which])(td, &uap->a2);
  299         return (error);
  300 }
  301 
  302 static void
  303 msg_freehdr(msghdr)
  304         struct msg *msghdr;
  305 {
  306         while (msghdr->msg_ts > 0) {
  307                 short next;
  308                 if (msghdr->msg_spot < 0 || msghdr->msg_spot >= msginfo.msgseg)
  309                         panic("msghdr->msg_spot out of range");
  310                 next = msgmaps[msghdr->msg_spot].next;
  311                 msgmaps[msghdr->msg_spot].next = free_msgmaps;
  312                 free_msgmaps = msghdr->msg_spot;
  313                 nfree_msgmaps++;
  314                 msghdr->msg_spot = next;
  315                 if (msghdr->msg_ts >= msginfo.msgssz)
  316                         msghdr->msg_ts -= msginfo.msgssz;
  317                 else
  318                         msghdr->msg_ts = 0;
  319         }
  320         if (msghdr->msg_spot != -1)
  321                 panic("msghdr->msg_spot != -1");
  322         msghdr->msg_next = free_msghdrs;
  323         free_msghdrs = msghdr;
  324 }
  325 
  326 #ifndef _SYS_SYSPROTO_H_
  327 struct msgctl_args {
  328         int     msqid;
  329         int     cmd;
  330         struct  msqid_ds *buf;
  331 };
  332 #endif
  333 
  334 /*
  335  * MPSAFE
  336  */
  337 int
  338 msgctl(td, uap)
  339         struct thread *td;
  340         register struct msgctl_args *uap;
  341 {
  342         int msqid = uap->msqid;
  343         int cmd = uap->cmd;
  344         struct msqid_ds msqbuf;
  345         int error;
  346 
  347         DPRINTF(("call to msgctl(%d, %d, 0x%x)\n", msqid, cmd, uap->buf));
  348         if (cmd == IPC_SET &&
  349             (error = copyin(uap->buf, &msqbuf, sizeof(msqbuf))) != 0)
  350                 return (error);
  351         error = kern_msgctl(td, msqid, cmd, &msqbuf);
  352         if (cmd == IPC_STAT && error == 0)
  353                 error = copyout(&msqbuf, uap->buf, sizeof(struct msqid_ds));
  354         return (error);
  355 }
  356 
  357 int
  358 kern_msgctl(td, msqid, cmd, msqbuf)
  359         struct thread *td;
  360         int msqid;
  361         int cmd;
  362         struct msqid_ds *msqbuf;
  363 {
  364         int rval, error, msqix;
  365         register struct msqid_kernel *msqkptr;
  366 
  367         if (!jail_sysvipc_allowed && jailed(td->td_ucred))
  368                 return (ENOSYS);
  369 
  370         msqix = IPCID_TO_IX(msqid);
  371 
  372         if (msqix < 0 || msqix >= msginfo.msgmni) {
  373                 DPRINTF(("msqid (%d) out of range (0<=msqid<%d)\n", msqix,
  374                     msginfo.msgmni));
  375                 return (EINVAL);
  376         }
  377 
  378         msqkptr = &msqids[msqix];
  379 
  380         mtx_lock(&msq_mtx);
  381         if (msqkptr->u.msg_qbytes == 0) {
  382                 DPRINTF(("no such msqid\n"));
  383                 error = EINVAL;
  384                 goto done2;
  385         }
  386         if (msqkptr->u.msg_perm.seq != IPCID_TO_SEQ(msqid)) {
  387                 DPRINTF(("wrong sequence number\n"));
  388                 error = EINVAL;
  389                 goto done2;
  390         }
  391 
  392         error = 0;
  393         rval = 0;
  394 
  395         switch (cmd) {
  396 
  397         case IPC_RMID:
  398         {
  399                 struct msg *msghdr;
  400                 if ((error = ipcperm(td, &msqkptr->u.msg_perm, IPC_M)))
  401                         goto done2;
  402 
  403                 /* Free the message headers */
  404                 msghdr = msqkptr->u.msg_first;
  405                 while (msghdr != NULL) {
  406                         struct msg *msghdr_tmp;
  407 
  408                         /* Free the segments of each message */
  409                         msqkptr->u.msg_cbytes -= msghdr->msg_ts;
  410                         msqkptr->u.msg_qnum--;
  411                         msghdr_tmp = msghdr;
  412                         msghdr = msghdr->msg_next;
  413                         msg_freehdr(msghdr_tmp);
  414                 }
  415 
  416                 if (msqkptr->u.msg_cbytes != 0)
  417                         panic("msg_cbytes is screwed up");
  418                 if (msqkptr->u.msg_qnum != 0)
  419                         panic("msg_qnum is screwed up");
  420 
  421                 msqkptr->u.msg_qbytes = 0;      /* Mark it as free */
  422 
  423                 wakeup(msqkptr);
  424         }
  425 
  426                 break;
  427 
  428         case IPC_SET:
  429                 if ((error = ipcperm(td, &msqkptr->u.msg_perm, IPC_M)))
  430                         goto done2;
  431                 if (msqbuf->msg_qbytes > msqkptr->u.msg_qbytes) {
  432                         error = suser(td);
  433                         if (error)
  434                                 goto done2;
  435                 }
  436                 if (msqbuf->msg_qbytes > msginfo.msgmnb) {
  437                         DPRINTF(("can't increase msg_qbytes beyond %d"
  438                             "(truncating)\n", msginfo.msgmnb));
  439                         msqbuf->msg_qbytes = msginfo.msgmnb;    /* silently restrict qbytes to system limit */
  440                 }
  441                 if (msqbuf->msg_qbytes == 0) {
  442                         DPRINTF(("can't reduce msg_qbytes to 0\n"));
  443                         error = EINVAL;         /* non-standard errno! */
  444                         goto done2;
  445                 }
  446                 msqkptr->u.msg_perm.uid = msqbuf->msg_perm.uid; /* change the owner */
  447                 msqkptr->u.msg_perm.gid = msqbuf->msg_perm.gid; /* change the owner */
  448                 msqkptr->u.msg_perm.mode = (msqkptr->u.msg_perm.mode & ~0777) |
  449                     (msqbuf->msg_perm.mode & 0777);
  450                 msqkptr->u.msg_qbytes = msqbuf->msg_qbytes;
  451                 msqkptr->u.msg_ctime = time_second;
  452                 break;
  453 
  454         case IPC_STAT:
  455                 if ((error = ipcperm(td, &msqkptr->u.msg_perm, IPC_R))) {
  456                         DPRINTF(("requester doesn't have read access\n"));
  457                         goto done2;
  458                 }
  459                 *msqbuf = msqkptr->u;
  460                 break;
  461 
  462         default:
  463                 DPRINTF(("invalid command %d\n", cmd));
  464                 error = EINVAL;
  465                 goto done2;
  466         }
  467 
  468         if (error == 0)
  469                 td->td_retval[0] = rval;
  470 done2:
  471         mtx_unlock(&msq_mtx);
  472         return (error);
  473 }
  474 
  475 #ifndef _SYS_SYSPROTO_H_
  476 struct msgget_args {
  477         key_t   key;
  478         int     msgflg;
  479 };
  480 #endif
  481 
  482 /*
  483  * MPSAFE
  484  */
  485 int
  486 msgget(td, uap)
  487         struct thread *td;
  488         register struct msgget_args *uap;
  489 {
  490         int msqid, error = 0;
  491         int key = uap->key;
  492         int msgflg = uap->msgflg;
  493         struct ucred *cred = td->td_ucred;
  494         register struct msqid_kernel *msqkptr = NULL;
  495 
  496         DPRINTF(("msgget(0x%x, 0%o)\n", key, msgflg));
  497 
  498         if (!jail_sysvipc_allowed && jailed(td->td_ucred))
  499                 return (ENOSYS);
  500 
  501         mtx_lock(&msq_mtx);
  502         if (key != IPC_PRIVATE) {
  503                 for (msqid = 0; msqid < msginfo.msgmni; msqid++) {
  504                         msqkptr = &msqids[msqid];
  505                         if (msqkptr->u.msg_qbytes != 0 &&
  506                             msqkptr->u.msg_perm.key == key)
  507                                 break;
  508                 }
  509                 if (msqid < msginfo.msgmni) {
  510                         DPRINTF(("found public key\n"));
  511                         if ((msgflg & IPC_CREAT) && (msgflg & IPC_EXCL)) {
  512                                 DPRINTF(("not exclusive\n"));
  513                                 error = EEXIST;
  514                                 goto done2;
  515                         }
  516                         if ((error = ipcperm(td, &msqkptr->u.msg_perm,
  517                             msgflg & 0700))) {
  518                                 DPRINTF(("requester doesn't have 0%o access\n",
  519                                     msgflg & 0700));
  520                                 goto done2;
  521                         }
  522                         goto found;
  523                 }
  524         }
  525 
  526         DPRINTF(("need to allocate the msqid_ds\n"));
  527         if (key == IPC_PRIVATE || (msgflg & IPC_CREAT)) {
  528                 for (msqid = 0; msqid < msginfo.msgmni; msqid++) {
  529                         /*
  530                          * Look for an unallocated and unlocked msqid_ds.
  531                          * msqid_ds's can be locked by msgsnd or msgrcv while
  532                          * they are copying the message in/out.  We can't
  533                          * re-use the entry until they release it.
  534                          */
  535                         msqkptr = &msqids[msqid];
  536                         if (msqkptr->u.msg_qbytes == 0 &&
  537                             (msqkptr->u.msg_perm.mode & MSG_LOCKED) == 0)
  538                                 break;
  539                 }
  540                 if (msqid == msginfo.msgmni) {
  541                         DPRINTF(("no more msqid_ds's available\n"));
  542                         error = ENOSPC;
  543                         goto done2;
  544                 }
  545                 DPRINTF(("msqid %d is available\n", msqid));
  546                 msqkptr->u.msg_perm.key = key;
  547                 msqkptr->u.msg_perm.cuid = cred->cr_uid;
  548                 msqkptr->u.msg_perm.uid = cred->cr_uid;
  549                 msqkptr->u.msg_perm.cgid = cred->cr_gid;
  550                 msqkptr->u.msg_perm.gid = cred->cr_gid;
  551                 msqkptr->u.msg_perm.mode = (msgflg & 0777);
  552                 /* Make sure that the returned msqid is unique */
  553                 msqkptr->u.msg_perm.seq = (msqkptr->u.msg_perm.seq + 1) & 0x7fff;
  554                 msqkptr->u.msg_first = NULL;
  555                 msqkptr->u.msg_last = NULL;
  556                 msqkptr->u.msg_cbytes = 0;
  557                 msqkptr->u.msg_qnum = 0;
  558                 msqkptr->u.msg_qbytes = msginfo.msgmnb;
  559                 msqkptr->u.msg_lspid = 0;
  560                 msqkptr->u.msg_lrpid = 0;
  561                 msqkptr->u.msg_stime = 0;
  562                 msqkptr->u.msg_rtime = 0;
  563                 msqkptr->u.msg_ctime = time_second;
  564         } else {
  565                 DPRINTF(("didn't find it and wasn't asked to create it\n"));
  566                 error = ENOENT;
  567                 goto done2;
  568         }
  569 
  570 found:
  571         /* Construct the unique msqid */
  572         td->td_retval[0] = IXSEQ_TO_IPCID(msqid, msqkptr->u.msg_perm);
  573 done2:
  574         mtx_unlock(&msq_mtx);
  575         return (error);
  576 }
  577 
  578 #ifndef _SYS_SYSPROTO_H_
  579 struct msgsnd_args {
  580         int     msqid;
  581         const void      *msgp;
  582         size_t  msgsz;
  583         int     msgflg;
  584 };
  585 #endif
  586 
  587 /*
  588  * MPSAFE
  589  */
  590 int
  591 msgsnd(td, uap)
  592         struct thread *td;
  593         register struct msgsnd_args *uap;
  594 {
  595         int msqid = uap->msqid;
  596         const void *user_msgp = uap->msgp;
  597         size_t msgsz = uap->msgsz;
  598         int msgflg = uap->msgflg;
  599         int segs_needed, error = 0;
  600         register struct msqid_kernel *msqkptr;
  601         register struct msg *msghdr;
  602         short next;
  603 
  604         DPRINTF(("call to msgsnd(%d, 0x%x, %d, %d)\n", msqid, user_msgp, msgsz,
  605             msgflg));
  606         if (!jail_sysvipc_allowed && jailed(td->td_ucred))
  607                 return (ENOSYS);
  608 
  609         mtx_lock(&msq_mtx);
  610         msqid = IPCID_TO_IX(msqid);
  611 
  612         if (msqid < 0 || msqid >= msginfo.msgmni) {
  613                 DPRINTF(("msqid (%d) out of range (0<=msqid<%d)\n", msqid,
  614                     msginfo.msgmni));
  615                 error = EINVAL;
  616                 goto done2;
  617         }
  618 
  619         msqkptr = &msqids[msqid];
  620         if (msqkptr->u.msg_qbytes == 0) {
  621                 DPRINTF(("no such message queue id\n"));
  622                 error = EINVAL;
  623                 goto done2;
  624         }
  625         if (msqkptr->u.msg_perm.seq != IPCID_TO_SEQ(uap->msqid)) {
  626                 DPRINTF(("wrong sequence number\n"));
  627                 error = EINVAL;
  628                 goto done2;
  629         }
  630 
  631         if ((error = ipcperm(td, &msqkptr->u.msg_perm, IPC_W))) {
  632                 DPRINTF(("requester doesn't have write access\n"));
  633                 goto done2;
  634         }
  635 
  636         segs_needed = (msgsz + msginfo.msgssz - 1) / msginfo.msgssz;
  637         DPRINTF(("msgsz=%d, msgssz=%d, segs_needed=%d\n", msgsz, msginfo.msgssz,
  638             segs_needed));
  639         for (;;) {
  640                 int need_more_resources = 0;
  641 
  642                 /*
  643                  * check msgsz
  644                  * (inside this loop in case msg_qbytes changes while we sleep)
  645                  */
  646 
  647                 if (msgsz > msqkptr->u.msg_qbytes) {
  648                         DPRINTF(("msgsz > msqkptr->u.msg_qbytes\n"));
  649                         error = EINVAL;
  650                         goto done2;
  651                 }
  652 
  653                 if (msqkptr->u.msg_perm.mode & MSG_LOCKED) {
  654                         DPRINTF(("msqid is locked\n"));
  655                         need_more_resources = 1;
  656                 }
  657                 if (msgsz + msqkptr->u.msg_cbytes > msqkptr->u.msg_qbytes) {
  658                         DPRINTF(("msgsz + msg_cbytes > msg_qbytes\n"));
  659                         need_more_resources = 1;
  660                 }
  661                 if (segs_needed > nfree_msgmaps) {
  662                         DPRINTF(("segs_needed > nfree_msgmaps\n"));
  663                         need_more_resources = 1;
  664                 }
  665                 if (free_msghdrs == NULL) {
  666                         DPRINTF(("no more msghdrs\n"));
  667                         need_more_resources = 1;
  668                 }
  669 
  670                 if (need_more_resources) {
  671                         int we_own_it;
  672 
  673                         if ((msgflg & IPC_NOWAIT) != 0) {
  674                                 DPRINTF(("need more resources but caller "
  675                                     "doesn't want to wait\n"));
  676                                 error = EAGAIN;
  677                                 goto done2;
  678                         }
  679 
  680                         if ((msqkptr->u.msg_perm.mode & MSG_LOCKED) != 0) {
  681                                 DPRINTF(("we don't own the msqid_ds\n"));
  682                                 we_own_it = 0;
  683                         } else {
  684                                 /* Force later arrivals to wait for our
  685                                    request */
  686                                 DPRINTF(("we own the msqid_ds\n"));
  687                                 msqkptr->u.msg_perm.mode |= MSG_LOCKED;
  688                                 we_own_it = 1;
  689                         }
  690                         DPRINTF(("goodnight\n"));
  691                         error = msleep(msqkptr, &msq_mtx, (PZERO - 4) | PCATCH,
  692                             "msgwait", 0);
  693                         DPRINTF(("good morning, error=%d\n", error));
  694                         if (we_own_it)
  695                                 msqkptr->u.msg_perm.mode &= ~MSG_LOCKED;
  696                         if (error != 0) {
  697                                 DPRINTF(("msgsnd:  interrupted system call\n"));
  698                                 error = EINTR;
  699                                 goto done2;
  700                         }
  701 
  702                         /*
  703                          * Make sure that the msq queue still exists
  704                          */
  705 
  706                         if (msqkptr->u.msg_qbytes == 0) {
  707                                 DPRINTF(("msqid deleted\n"));
  708                                 error = EIDRM;
  709                                 goto done2;
  710                         }
  711 
  712                 } else {
  713                         DPRINTF(("got all the resources that we need\n"));
  714                         break;
  715                 }
  716         }
  717 
  718         /*
  719          * We have the resources that we need.
  720          * Make sure!
  721          */
  722 
  723         if (msqkptr->u.msg_perm.mode & MSG_LOCKED)
  724                 panic("msg_perm.mode & MSG_LOCKED");
  725         if (segs_needed > nfree_msgmaps)
  726                 panic("segs_needed > nfree_msgmaps");
  727         if (msgsz + msqkptr->u.msg_cbytes > msqkptr->u.msg_qbytes)
  728                 panic("msgsz + msg_cbytes > msg_qbytes");
  729         if (free_msghdrs == NULL)
  730                 panic("no more msghdrs");
  731 
  732         /*
  733          * Re-lock the msqid_ds in case we page-fault when copying in the
  734          * message
  735          */
  736 
  737         if ((msqkptr->u.msg_perm.mode & MSG_LOCKED) != 0)
  738                 panic("msqid_ds is already locked");
  739         msqkptr->u.msg_perm.mode |= MSG_LOCKED;
  740 
  741         /*
  742          * Allocate a message header
  743          */
  744 
  745         msghdr = free_msghdrs;
  746         free_msghdrs = msghdr->msg_next;
  747         msghdr->msg_spot = -1;
  748         msghdr->msg_ts = msgsz;
  749 
  750         /*
  751          * Allocate space for the message
  752          */
  753 
  754         while (segs_needed > 0) {
  755                 if (nfree_msgmaps <= 0)
  756                         panic("not enough msgmaps");
  757                 if (free_msgmaps == -1)
  758                         panic("nil free_msgmaps");
  759                 next = free_msgmaps;
  760                 if (next <= -1)
  761                         panic("next too low #1");
  762                 if (next >= msginfo.msgseg)
  763                         panic("next out of range #1");
  764                 DPRINTF(("allocating segment %d to message\n", next));
  765                 free_msgmaps = msgmaps[next].next;
  766                 nfree_msgmaps--;
  767                 msgmaps[next].next = msghdr->msg_spot;
  768                 msghdr->msg_spot = next;
  769                 segs_needed--;
  770         }
  771 
  772         /*
  773          * Copy in the message type
  774          */
  775 
  776         mtx_unlock(&msq_mtx);
  777         if ((error = copyin(user_msgp, &msghdr->msg_type,
  778             sizeof(msghdr->msg_type))) != 0) {
  779                 mtx_lock(&msq_mtx);
  780                 DPRINTF(("error %d copying the message type\n", error));
  781                 msg_freehdr(msghdr);
  782                 msqkptr->u.msg_perm.mode &= ~MSG_LOCKED;
  783                 wakeup(msqkptr);
  784                 goto done2;
  785         }
  786         mtx_lock(&msq_mtx);
  787         user_msgp = (const char *)user_msgp + sizeof(msghdr->msg_type);
  788 
  789         /*
  790          * Validate the message type
  791          */
  792 
  793         if (msghdr->msg_type < 1) {
  794                 msg_freehdr(msghdr);
  795                 msqkptr->u.msg_perm.mode &= ~MSG_LOCKED;
  796                 wakeup(msqkptr);
  797                 DPRINTF(("mtype (%d) < 1\n", msghdr->msg_type));
  798                 error = EINVAL;
  799                 goto done2;
  800         }
  801 
  802         /*
  803          * Copy in the message body
  804          */
  805 
  806         next = msghdr->msg_spot;
  807         while (msgsz > 0) {
  808                 size_t tlen;
  809                 if (msgsz > msginfo.msgssz)
  810                         tlen = msginfo.msgssz;
  811                 else
  812                         tlen = msgsz;
  813                 if (next <= -1)
  814                         panic("next too low #2");
  815                 if (next >= msginfo.msgseg)
  816                         panic("next out of range #2");
  817                 mtx_unlock(&msq_mtx);
  818                 if ((error = copyin(user_msgp, &msgpool[next * msginfo.msgssz],
  819                     tlen)) != 0) {
  820                         mtx_lock(&msq_mtx);
  821                         DPRINTF(("error %d copying in message segment\n",
  822                             error));
  823                         msg_freehdr(msghdr);
  824                         msqkptr->u.msg_perm.mode &= ~MSG_LOCKED;
  825                         wakeup(msqkptr);
  826                         goto done2;
  827                 }
  828                 mtx_lock(&msq_mtx);
  829                 msgsz -= tlen;
  830                 user_msgp = (const char *)user_msgp + tlen;
  831                 next = msgmaps[next].next;
  832         }
  833         if (next != -1)
  834                 panic("didn't use all the msg segments");
  835 
  836         /*
  837          * We've got the message.  Unlock the msqid_ds.
  838          */
  839 
  840         msqkptr->u.msg_perm.mode &= ~MSG_LOCKED;
  841 
  842         /*
  843          * Make sure that the msqid_ds is still allocated.
  844          */
  845 
  846         if (msqkptr->u.msg_qbytes == 0) {
  847                 msg_freehdr(msghdr);
  848                 wakeup(msqkptr);
  849                 error = EIDRM;
  850                 goto done2;
  851         }
  852 
  853         /*
  854          * Put the message into the queue
  855          */
  856         if (msqkptr->u.msg_first == NULL) {
  857                 msqkptr->u.msg_first = msghdr;
  858                 msqkptr->u.msg_last = msghdr;
  859         } else {
  860                 msqkptr->u.msg_last->msg_next = msghdr;
  861                 msqkptr->u.msg_last = msghdr;
  862         }
  863         msqkptr->u.msg_last->msg_next = NULL;
  864 
  865         msqkptr->u.msg_cbytes += msghdr->msg_ts;
  866         msqkptr->u.msg_qnum++;
  867         msqkptr->u.msg_lspid = td->td_proc->p_pid;
  868         msqkptr->u.msg_stime = time_second;
  869 
  870         wakeup(msqkptr);
  871         td->td_retval[0] = 0;
  872 done2:
  873         mtx_unlock(&msq_mtx);
  874         return (error);
  875 }
  876 
  877 #ifndef _SYS_SYSPROTO_H_
  878 struct msgrcv_args {
  879         int     msqid;
  880         void    *msgp;
  881         size_t  msgsz;
  882         long    msgtyp;
  883         int     msgflg;
  884 };
  885 #endif
  886 
  887 /*
  888  * MPSAFE
  889  */
  890 int
  891 msgrcv(td, uap)
  892         struct thread *td;
  893         register struct msgrcv_args *uap;
  894 {
  895         int msqid = uap->msqid;
  896         void *user_msgp = uap->msgp;
  897         size_t msgsz = uap->msgsz;
  898         long msgtyp = uap->msgtyp;
  899         int msgflg = uap->msgflg;
  900         size_t len;
  901         register struct msqid_kernel *msqkptr;
  902         register struct msg *msghdr;
  903         int error = 0;
  904         short next;
  905 
  906         DPRINTF(("call to msgrcv(%d, 0x%x, %d, %ld, %d)\n", msqid, user_msgp,
  907             msgsz, msgtyp, msgflg));
  908 
  909         if (!jail_sysvipc_allowed && jailed(td->td_ucred))
  910                 return (ENOSYS);
  911 
  912         msqid = IPCID_TO_IX(msqid);
  913 
  914         if (msqid < 0 || msqid >= msginfo.msgmni) {
  915                 DPRINTF(("msqid (%d) out of range (0<=msqid<%d)\n", msqid,
  916                     msginfo.msgmni));
  917                 return (EINVAL);
  918         }
  919 
  920         msqkptr = &msqids[msqid];
  921         mtx_lock(&msq_mtx);
  922         if (msqkptr->u.msg_qbytes == 0) {
  923                 DPRINTF(("no such message queue id\n"));
  924                 error = EINVAL;
  925                 goto done2;
  926         }
  927         if (msqkptr->u.msg_perm.seq != IPCID_TO_SEQ(uap->msqid)) {
  928                 DPRINTF(("wrong sequence number\n"));
  929                 error = EINVAL;
  930                 goto done2;
  931         }
  932 
  933         if ((error = ipcperm(td, &msqkptr->u.msg_perm, IPC_R))) {
  934                 DPRINTF(("requester doesn't have read access\n"));
  935                 goto done2;
  936         }
  937 
  938         msghdr = NULL;
  939         while (msghdr == NULL) {
  940                 if (msgtyp == 0) {
  941                         msghdr = msqkptr->u.msg_first;
  942                         if (msghdr != NULL) {
  943                                 if (msgsz < msghdr->msg_ts &&
  944                                     (msgflg & MSG_NOERROR) == 0) {
  945                                         DPRINTF(("first message on the queue "
  946                                             "is too big (want %d, got %d)\n",
  947                                             msgsz, msghdr->msg_ts));
  948                                         error = E2BIG;
  949                                         goto done2;
  950                                 }
  951                                 if (msqkptr->u.msg_first == msqkptr->u.msg_last) {
  952                                         msqkptr->u.msg_first = NULL;
  953                                         msqkptr->u.msg_last = NULL;
  954                                 } else {
  955                                         msqkptr->u.msg_first = msghdr->msg_next;
  956                                         if (msqkptr->u.msg_first == NULL)
  957                                                 panic("msg_first/last screwed up #1");
  958                                 }
  959                         }
  960                 } else {
  961                         struct msg *previous;
  962                         struct msg **prev;
  963 
  964                         previous = NULL;
  965                         prev = &(msqkptr->u.msg_first);
  966                         while ((msghdr = *prev) != NULL) {
  967                                 /*
  968                                  * Is this message's type an exact match or is
  969                                  * this message's type less than or equal to
  970                                  * the absolute value of a negative msgtyp?
  971                                  * Note that the second half of this test can
  972                                  * NEVER be true if msgtyp is positive since
  973                                  * msg_type is always positive!
  974                                  */
  975 
  976                                 if (msgtyp == msghdr->msg_type ||
  977                                     msghdr->msg_type <= -msgtyp) {
  978                                         DPRINTF(("found message type %d, "
  979                                             "requested %d\n",
  980                                             msghdr->msg_type, msgtyp));
  981                                         if (msgsz < msghdr->msg_ts &&
  982                                             (msgflg & MSG_NOERROR) == 0) {
  983                                                 DPRINTF(("requested message "
  984                                                     "on the queue is too big "
  985                                                     "(want %d, got %d)\n",
  986                                                     msgsz, msghdr->msg_ts));
  987                                                 error = E2BIG;
  988                                                 goto done2;
  989                                         }
  990                                         *prev = msghdr->msg_next;
  991                                         if (msghdr == msqkptr->u.msg_last) {
  992                                                 if (previous == NULL) {
  993                                                         if (prev !=
  994                                                             &msqkptr->u.msg_first)
  995                                                                 panic("msg_first/last screwed up #2");
  996                                                         msqkptr->u.msg_first =
  997                                                             NULL;
  998                                                         msqkptr->u.msg_last =
  999                                                             NULL;
 1000                                                 } else {
 1001                                                         if (prev ==
 1002                                                             &msqkptr->u.msg_first)
 1003                                                                 panic("msg_first/last screwed up #3");
 1004                                                         msqkptr->u.msg_last =
 1005                                                             previous;
 1006                                                 }
 1007                                         }
 1008                                         break;
 1009                                 }
 1010                                 previous = msghdr;
 1011                                 prev = &(msghdr->msg_next);
 1012                         }
 1013                 }
 1014 
 1015                 /*
 1016                  * We've either extracted the msghdr for the appropriate
 1017                  * message or there isn't one.
 1018                  * If there is one then bail out of this loop.
 1019                  */
 1020 
 1021                 if (msghdr != NULL)
 1022                         break;
 1023 
 1024                 /*
 1025                  * Hmph!  No message found.  Does the user want to wait?
 1026                  */
 1027 
 1028                 if ((msgflg & IPC_NOWAIT) != 0) {
 1029                         DPRINTF(("no appropriate message found (msgtyp=%d)\n",
 1030                             msgtyp));
 1031                         /* The SVID says to return ENOMSG. */
 1032                         error = ENOMSG;
 1033                         goto done2;
 1034                 }
 1035 
 1036                 /*
 1037                  * Wait for something to happen
 1038                  */
 1039 
 1040                 DPRINTF(("msgrcv:  goodnight\n"));
 1041                 error = msleep(msqkptr, &msq_mtx, (PZERO - 4) | PCATCH,
 1042                     "msgwait", 0);
 1043                 DPRINTF(("msgrcv:  good morning (error=%d)\n", error));
 1044 
 1045                 if (error != 0) {
 1046                         DPRINTF(("msgsnd:  interrupted system call\n"));
 1047                         error = EINTR;
 1048                         goto done2;
 1049                 }
 1050 
 1051                 /*
 1052                  * Make sure that the msq queue still exists
 1053                  */
 1054 
 1055                 if (msqkptr->u.msg_qbytes == 0 ||
 1056                     msqkptr->u.msg_perm.seq != IPCID_TO_SEQ(uap->msqid)) {
 1057                         DPRINTF(("msqid deleted\n"));
 1058                         error = EIDRM;
 1059                         goto done2;
 1060                 }
 1061         }
 1062 
 1063         /*
 1064          * Return the message to the user.
 1065          *
 1066          * First, do the bookkeeping (before we risk being interrupted).
 1067          */
 1068 
 1069         msqkptr->u.msg_cbytes -= msghdr->msg_ts;
 1070         msqkptr->u.msg_qnum--;
 1071         msqkptr->u.msg_lrpid = td->td_proc->p_pid;
 1072         msqkptr->u.msg_rtime = time_second;
 1073 
 1074         /*
 1075          * Make msgsz the actual amount that we'll be returning.
 1076          * Note that this effectively truncates the message if it is too long
 1077          * (since msgsz is never increased).
 1078          */
 1079 
 1080         DPRINTF(("found a message, msgsz=%d, msg_ts=%d\n", msgsz,
 1081             msghdr->msg_ts));
 1082         if (msgsz > msghdr->msg_ts)
 1083                 msgsz = msghdr->msg_ts;
 1084 
 1085         /*
 1086          * Return the type to the user.
 1087          */
 1088 
 1089         mtx_unlock(&msq_mtx);
 1090         error = copyout(&(msghdr->msg_type), user_msgp,
 1091             sizeof(msghdr->msg_type));
 1092         mtx_lock(&msq_mtx);
 1093         if (error != 0) {
 1094                 DPRINTF(("error (%d) copying out message type\n", error));
 1095                 msg_freehdr(msghdr);
 1096                 wakeup(msqkptr);
 1097                 goto done2;
 1098         }
 1099         user_msgp = (char *)user_msgp + sizeof(msghdr->msg_type);
 1100 
 1101         /*
 1102          * Return the segments to the user
 1103          */
 1104 
 1105         next = msghdr->msg_spot;
 1106         for (len = 0; len < msgsz; len += msginfo.msgssz) {
 1107                 size_t tlen;
 1108 
 1109                 if (msgsz - len > msginfo.msgssz)
 1110                         tlen = msginfo.msgssz;
 1111                 else
 1112                         tlen = msgsz - len;
 1113                 if (next <= -1)
 1114                         panic("next too low #3");
 1115                 if (next >= msginfo.msgseg)
 1116                         panic("next out of range #3");
 1117                 mtx_unlock(&msq_mtx);
 1118                 error = copyout(&msgpool[next * msginfo.msgssz],
 1119                     user_msgp, tlen);
 1120                 mtx_lock(&msq_mtx);
 1121                 if (error != 0) {
 1122                         DPRINTF(("error (%d) copying out message segment\n",
 1123                             error));
 1124                         msg_freehdr(msghdr);
 1125                         wakeup(msqkptr);
 1126                         goto done2;
 1127                 }
 1128                 user_msgp = (char *)user_msgp + tlen;
 1129                 next = msgmaps[next].next;
 1130         }
 1131 
 1132         /*
 1133          * Done, return the actual number of bytes copied out.
 1134          */
 1135 
 1136         msg_freehdr(msghdr);
 1137         wakeup(msqkptr);
 1138         td->td_retval[0] = msgsz;
 1139 done2:
 1140         mtx_unlock(&msq_mtx);
 1141         return (error);
 1142 }
 1143 
 1144 static int
 1145 sysctl_msqids(SYSCTL_HANDLER_ARGS)
 1146 {
 1147 
 1148         return (SYSCTL_OUT(req, msqids,
 1149             sizeof(struct msqid_kernel) * msginfo.msgmni));
 1150 }
 1151 
 1152 SYSCTL_DECL(_kern_ipc);
 1153 SYSCTL_INT(_kern_ipc, OID_AUTO, msgmax, CTLFLAG_RD, &msginfo.msgmax, 0,
 1154     "Maximum message size");
 1155 SYSCTL_INT(_kern_ipc, OID_AUTO, msgmni, CTLFLAG_RDTUN, &msginfo.msgmni, 0,
 1156     "Number of message queue identifiers");
 1157 SYSCTL_INT(_kern_ipc, OID_AUTO, msgmnb, CTLFLAG_RDTUN, &msginfo.msgmnb, 0,
 1158     "Maximum number of bytes in a queue");
 1159 SYSCTL_INT(_kern_ipc, OID_AUTO, msgtql, CTLFLAG_RDTUN, &msginfo.msgtql, 0,
 1160     "Maximum number of messages in the system");
 1161 SYSCTL_INT(_kern_ipc, OID_AUTO, msgssz, CTLFLAG_RDTUN, &msginfo.msgssz, 0,
 1162     "Size of a message segment");
 1163 SYSCTL_INT(_kern_ipc, OID_AUTO, msgseg, CTLFLAG_RDTUN, &msginfo.msgseg, 0,
 1164     "Number of message segments");
 1165 SYSCTL_PROC(_kern_ipc, OID_AUTO, msqids, CTLFLAG_RD,
 1166     NULL, 0, sysctl_msqids, "", "Message queue IDs");

Cache object: c274c4fa3733006102c786e7f9aa5dc7


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.