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

Cache object: e73396d74aae093987f9734e9c931f29


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