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

Cache object: ef0180616ce912c621610b3b64304dbc


[ 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.