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