root/kern/sysv_msg.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. msginit
  2. msg_freehdr
  3. sys_msgctl
  4. msgctl1
  5. sys_msgget
  6. sys_msgsnd
  7. sys_msgrcv

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

/* [<][>][^][v][top][bottom][index][help] */