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

Cache object: 617bba02704ab4249e30460be2a832a1


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