sysv_msg.c revision 45921
140123Sdes/* $Id: sysv_msg.c,v 1.19 1999/01/30 12:21:48 phk Exp $ */ 247661Sbrian 3709Swollman/* 437Srgrimes * Implementation of SVID messages 537Srgrimes * 637Srgrimes * Author: Daniel Boulet 737Srgrimes * 837Srgrimes * Copyright 1993 Daniel Boulet and RTMX Inc. 937Srgrimes * 108460Sjkh * This system call was implemented by Daniel Boulet under contract from RTMX. 1143951Sjkh * 128460Sjkh * Redistribution and use in source forms, with and without modification, 138460Sjkh * are permitted provided that this entire comment appears intact. 1437Srgrimes * 1537Srgrimes * Redistribution in binary form may occur without any restrictions. 1637Srgrimes * Obviously, it would be nice if you gave credit where credit is due 1737Srgrimes * but requiring it would be too onerous. 1837Srgrimes * 1937Srgrimes * This software is provided ``AS IS'' without any warranties of any kind. 2037Srgrimes */ 2137Srgrimes 2220684Sjoerg#include <sys/param.h> 2337Srgrimes#include <sys/systm.h> 2437Srgrimes#include <sys/sysproto.h> 2543179Sdillon#include <sys/kernel.h> 2643803Sdillon#include <sys/proc.h> 2743179Sdillon#include <sys/msg.h> 2843803Sdillon#include <sys/sysent.h> 2943375Sdillon 3043375Sdillonstatic void msginit __P((void *)); 3143803SdillonSYSINIT(sysv_msg, SI_SUB_SYSV_MSG, SI_ORDER_FIRST, msginit, NULL) 3243179Sdillon 3343179Sdillon#define MSG_DEBUG 3443179Sdillon#undef MSG_DEBUG_OK 3543219Speter 3643219Speter#ifndef _SYS_SYSPROTO_H_ 3743849Sjkhstruct msgctl_args; 3843849Sjkhint msgctl __P((struct proc *p, struct msgctl_args *uap)); 3943849Sjkhstruct msgget_args; 4043219Speterint msgget __P((struct proc *p, struct msgget_args *uap)); 4143219Speterstruct msgsnd_args; 4243219Speterint msgsnd __P((struct proc *p, struct msgsnd_args *uap)); 4315568Sasamistruct msgrcv_args; 4443803Sdillonint msgrcv __P((struct proc *p, struct msgrcv_args *uap)); 4515568Sasami#endif 4615568Sasamistatic void msg_freehdr __P((struct msg *msghdr)); 4715568Sasami 4845239Sgrog/* XXX casting to (sy_call_t *) is bogus, as usual. */ 4945239Sgrogstatic sy_call_t *msgcalls[] = { 5045239Sgrog (sy_call_t *)msgctl, (sy_call_t *)msgget, 5143454Sgrog (sy_call_t *)msgsnd, (sy_call_t *)msgrcv 5242741Sgrog}; 5342741Sgrog 5443803Sdillonstatic int nfree_msgmaps; /* # of free map entries */ 553843Sdgstatic short free_msgmaps; /* head of linked list of free map entries */ 5643803Sdillonstatic struct msg *free_msghdrs; /* list of free msg headers */ 5737Srgrimeschar *msgpool; /* MSGMAX byte long msg buffer pool */ 5837Srgrimesstruct msgmap *msgmaps; /* MSGSEG msgmap structures */ 5937Srgrimesstruct msg *msghdrs; /* MSGTQL msg headers */ 6037Srgrimesstruct msqid_ds *msqids; /* MSGMNI msqid_ds struct's */ 6137Srgrimes 6237Srgrimesvoid 6337Srgrimesmsginit(dummy) 6437Srgrimes void *dummy; 6537Srgrimes{ 6637Srgrimes register int i; 6737Srgrimes 6837Srgrimes /* 6937Srgrimes * msginfo.msgssz should be a power of two for efficiency reasons. 7037Srgrimes * It is also pretty silly if msginfo.msgssz is less than 8 7137Srgrimes * or greater than about 256 so ... 7237Srgrimes */ 7337Srgrimes 7437Srgrimes i = 8; 7537Srgrimes while (i < 1024 && i != msginfo.msgssz) 7637Srgrimes i <<= 1; 7737Srgrimes if (i != msginfo.msgssz) { 7837Srgrimes printf("msginfo.msgssz=%d (0x%x)\n", msginfo.msgssz, 7937Srgrimes msginfo.msgssz); 8037Srgrimes panic("msginfo.msgssz not a small power of 2"); 8137Srgrimes } 8237Srgrimes 8337Srgrimes if (msginfo.msgseg > 32767) { 8437Srgrimes printf("msginfo.msgseg=%d\n", msginfo.msgseg); 8537Srgrimes panic("msginfo.msgseg > 32767"); 8637Srgrimes } 872164Sdg 882164Sdg if (msgmaps == NULL) 8937Srgrimes panic("msgmaps is NULL"); 9037Srgrimes 9145222Scracauer for (i = 0; i < msginfo.msgseg; i++) { 9237Srgrimes if (i > 0) 9337Srgrimes msgmaps[i-1].next = i; 9443197Sdillon msgmaps[i].next = -1; /* implies entry is available */ 9543197Sdillon } 9643197Sdillon free_msgmaps = 0; 971692Sphk nfree_msgmaps = msginfo.msgseg; 9843803Sdillon 9943197Sdillon if (msghdrs == NULL) 10043803Sdillon panic("msghdrs is NULL"); 1018530Sdg 10243803Sdillon for (i = 0; i < msginfo.msgtql; i++) { 10343803Sdillon msghdrs[i].msg_type = 0; 10443803Sdillon if (i > 0) 10543803Sdillon msghdrs[i-1].msg_next = &msghdrs[i]; 10643197Sdillon msghdrs[i].msg_next = NULL; 10743803Sdillon } 10843197Sdillon free_msghdrs = &msghdrs[0]; 10943803Sdillon 11043197Sdillon if (msqids == NULL) 11143803Sdillon panic("msqids is NULL"); 11243803Sdillon 1138530Sdg for (i = 0; i < msginfo.msgmni; i++) { 11443803Sdillon msqids[i].msg_qbytes = 0; /* implies entry is available */ 11543803Sdillon msqids[i].msg_perm.seq = 0; /* reset to a known value */ 11643803Sdillon } 11743803Sdillon} 11837Srgrimes 11943197Sdillon/* 12043197Sdillon * Entry point for all MSG calls 12143197Sdillon */ 12243803Sdillonint 12343803Sdillonmsgsys(p, uap) 12443803Sdillon struct proc *p; 12543803Sdillon /* XXX actually varargs. */ 12643197Sdillon struct msgsys_args /* { 12743197Sdillon u_int which; 1284091Sache int a2; 129872Sache int a3; 13038237Sbrian int a4; 13139384Sbrian int a5; 13239384Sbrian int a6; 13339384Sbrian } */ *uap; 13439384Sbrian{ 13539384Sbrian 13639384Sbrian if (uap->which >= sizeof(msgcalls)/sizeof(msgcalls[0])) 13739384Sbrian return (EINVAL); 13839384Sbrian return ((*msgcalls[uap->which])(p, &uap->a2)); 13939384Sbrian} 14039384Sbrian 14138237Sbrianstatic void 14226450Sachemsg_freehdr(msghdr) 14338237Sbrian struct msg *msghdr; 14439384Sbrian{ 14539384Sbrian while (msghdr->msg_ts > 0) { 14639384Sbrian short next; 14739384Sbrian if (msghdr->msg_spot < 0 || msghdr->msg_spot >= msginfo.msgseg) 14838237Sbrian panic("msghdr->msg_spot out of range"); 14921197Sphk next = msgmaps[msghdr->msg_spot].next; 15017767Sjkh msgmaps[msghdr->msg_spot].next = free_msgmaps; 15119363Sjoerg free_msgmaps = msghdr->msg_spot; 15217767Sjkh nfree_msgmaps++; 15325184Sjkh msghdr->msg_spot = next; 15417767Sjkh if (msghdr->msg_ts >= msginfo.msgssz) 15517767Sjkh msghdr->msg_ts -= msginfo.msgssz; 15645096Simp else 15745096Simp msghdr->msg_ts = 0; 15845096Simp } 15945096Simp if (msghdr->msg_spot != -1) 16045096Simp panic("msghdr->msg_spot != -1"); 1611675Sache msghdr->msg_next = free_msghdrs; 1627219Sjkh free_msghdrs = msghdr; 1637293Sjkh} 1641675Sache 1651675Sache#ifndef _SYS_SYSPROTO_H_ 16614596Snatestruct msgctl_args { 16714624Snate int msqid; 16814624Snate int cmd; 16914596Snate struct msqid_ds *buf; 17014596Snate}; 17125184Sjkh#endif 17225184Sjkh 17325184Sjkhint 17425184Sjkhmsgctl(p, uap) 1757460Sjkh struct proc *p; 1767460Sjkh register struct msgctl_args *uap; 17738456Sphk{ 17829319Sbrian int msqid = uap->msqid; 17938456Sphk int cmd = uap->cmd; 1807487Srgrimes struct msqid_ds *user_msqptr = uap->buf; 1817487Srgrimes struct ucred *cred = p->p_ucred; 18220828Sjoerg int rval, eval; 18344752Sdes struct msqid_ds msqbuf; 1847487Srgrimes register struct msqid_ds *msqptr; 1857487Srgrimes 18638237Sbrian#ifdef MSG_DEBUG_OK 18738237Sbrian printf("call to msgctl(%d, %d, 0x%x)\n", msqid, cmd, user_msqptr); 18838237Sbrian#endif 18931192Ssteve 19031192Ssteve msqid = IPCID_TO_IX(msqid); 19131192Ssteve 19231192Ssteve if (msqid < 0 || msqid >= msginfo.msgmni) { 19331192Ssteve#ifdef MSG_DEBUG_OK 19431192Ssteve printf("msqid (%d) out of range (0<=msqid<%d)\n", msqid, 19531192Ssteve msginfo.msgmni); 19631192Ssteve#endif 19731192Ssteve return(EINVAL); 19831192Ssteve } 19931192Ssteve 20031192Ssteve msqptr = &msqids[msqid]; 20131192Ssteve 20231192Ssteve if (msqptr->msg_qbytes == 0) { 20347661Sbrian#ifdef MSG_DEBUG_OK 20447661Sbrian printf("no such msqid\n"); 20531192Ssteve#endif 20631192Ssteve return(EINVAL); 20731192Ssteve } 20838915Scracauer if (msqptr->msg_perm.seq != IPCID_TO_SEQ(uap->msqid)) { 20938915Scracauer#ifdef MSG_DEBUG_OK 21038915Scracauer printf("wrong sequence number\n"); 21138915Scracauer#endif 21242897Sjkh return(EINVAL); 21342897Sjkh } 2147487Srgrimes 21525339Sjkh eval = 0; 2167487Srgrimes rval = 0; 2177487Srgrimes 2187259Sjkh switch (cmd) { 21925412Sjkh 22024463Spst case IPC_RMID: 22124463Spst { 22224463Spst struct msg *msghdr; 22324463Spst if ((eval = ipcperm(cred, &msqptr->msg_perm, IPC_M))) 22424463Spst return(eval); 22524463Spst /* Free the message headers */ 22624463Spst msghdr = msqptr->msg_first; 22724463Spst while (msghdr != NULL) { 22825339Sjkh struct msg *msghdr_tmp; 22919226Sjoerg 23025339Sjkh /* Free the segments of each message */ 23125339Sjkh msqptr->msg_cbytes -= msghdr->msg_ts; 23232340Sjoerg msqptr->msg_qnum--; 23332340Sjoerg msghdr_tmp = msghdr; 23432340Sjoerg msghdr = msghdr->msg_next; 23532340Sjoerg msg_freehdr(msghdr_tmp); 23632340Sjoerg } 23732340Sjoerg 23832340Sjoerg if (msqptr->msg_cbytes != 0) 23932340Sjoerg panic("msg_cbytes is screwed up"); 24032340Sjoerg if (msqptr->msg_qnum != 0) 24125184Sjkh panic("msg_qnum is screwed up"); 24239384Sbrian 243857Sdg msqptr->msg_qbytes = 0; /* Mark it as free */ 24437Srgrimes 24544818Sbillf wakeup((caddr_t)msqptr); 24644818Sbillf } 24744818Sbillf 24844818Sbillf break; 24944818Sbillf 25044818Sbillf case IPC_SET: 25144818Sbillf if ((eval = ipcperm(cred, &msqptr->msg_perm, IPC_M))) 25244818Sbillf return(eval); 25344818Sbillf if ((eval = copyin(user_msqptr, &msqbuf, sizeof(msqbuf))) != 0) 25444818Sbillf return(eval); 25544818Sbillf if (msqbuf.msg_qbytes > msqptr->msg_qbytes) { 25644818Sbillf eval = suser(cred, &p->p_acflag); 25744818Sbillf if (eval) 2587238Sache return(eval); 2597238Sache } 26025184Sjkh if (msqbuf.msg_qbytes > msginfo.msgmnb) { 26139384Sbrian#ifdef MSG_DEBUG_OK 2627477Sache printf("can't increase msg_qbytes beyond %d (truncating)\n", 2637477Sache msginfo.msgmnb); 2647238Sache#endif 2657487Srgrimes msqbuf.msg_qbytes = msginfo.msgmnb; /* silently restrict qbytes to system limit */ 2667487Srgrimes } 2677487Srgrimes if (msqbuf.msg_qbytes == 0) { 2687487Srgrimes#ifdef MSG_DEBUG_OK 2697487Srgrimes printf("can't reduce msg_qbytes to 0\n"); 2707487Srgrimes#endif 2717487Srgrimes return(EINVAL); /* non-standard errno! */ 2727487Srgrimes } 2737487Srgrimes msqptr->msg_perm.uid = msqbuf.msg_perm.uid; /* change the owner */ 2747238Sache msqptr->msg_perm.gid = msqbuf.msg_perm.gid; /* change the owner */ 2757238Sache msqptr->msg_perm.mode = (msqptr->msg_perm.mode & ~0777) | 27625184Sjkh (msqbuf.msg_perm.mode & 0777); 27711992Sache msqptr->msg_qbytes = msqbuf.msg_qbytes; 27811992Sache msqptr->msg_ctime = time_second; 27925412Sjkh break; 28011992Sache 28111992Sache case IPC_STAT: 2827238Sache if ((eval = ipcperm(cred, &msqptr->msg_perm, IPC_R))) { 2837238Sache#ifdef MSG_DEBUG_OK 28427365Sjkh printf("requester doesn't have read access\n"); 28527365Sjkh#endif 28638512Sgpalmer return(eval); 28739329Sjdp } 28839329Sjdp eval = copyout((caddr_t)msqptr, user_msqptr, 28939329Sjdp sizeof(struct msqid_ds)); 29039329Sjdp break; 29139329Sjdp 29239329Sjdp default: 29339329Sjdp#ifdef MSG_DEBUG_OK 29439329Sjdp printf("invalid command %d\n", cmd); 29539329Sjdp#endif 29639329Sjdp return(EINVAL); 29739329Sjdp } 29841648Sjb 29941676Sjb if (eval == 0) 30043951Sjkh p->p_retval[0] = rval; 30141648Sjb return(eval); 30241648Sjb} 30341648Sjb 30441648Sjb#ifndef _SYS_SYSPROTO_H_ 30541648Sjbstruct msgget_args { 30641648Sjb key_t key; 30741648Sjb int msgflg; 30841648Sjb}; 30941648Sjb#endif 31041648Sjb 31138512Sgpalmerint 3127296Sjkhmsgget(p, uap) 31317210Spst struct proc *p; 31417210Spst register struct msgget_args *uap; 31525339Sjkh{ 31629343Sbrian int msqid, eval; 31726727Spst int key = uap->key; 31826727Spst int msgflg = uap->msgflg; 31917210Spst struct ucred *cred = p->p_ucred; 32029343Sbrian register struct msqid_ds *msqptr = NULL; 32126727Spst 32226727Spst#ifdef MSG_DEBUG_OK 32326727Spst printf("msgget(0x%x, 0%o)\n", key, msgflg); 32425184Sjkh#endif 32544668Sjfitz 32617210Spst if (key != IPC_PRIVATE) { 32717210Spst for (msqid = 0; msqid < msginfo.msgmni; msqid++) { 32825184Sjkh msqptr = &msqids[msqid]; 32925530Sjkh if (msqptr->msg_qbytes != 0 && 33017161Spst msqptr->msg_perm.key == key) 33117161Spst break; 33242498Sn_hibma } 33342498Sn_hibma if (msqid < msginfo.msgmni) { 33442498Sn_hibma#ifdef MSG_DEBUG_OK 33542498Sn_hibma printf("found public key\n"); 33617161Spst#endif 33717161Spst if ((msgflg & IPC_CREAT) && (msgflg & IPC_EXCL)) { 3387487Srgrimes#ifdef MSG_DEBUG_OK 3397487Srgrimes printf("not exclusive\n"); 34025469Sandreas#endif 34125469Sandreas return(EEXIST); 34225469Sandreas } 3437487Srgrimes if ((eval = ipcperm(cred, &msqptr->msg_perm, msgflg & 0700 ))) { 34416671Spst#ifdef MSG_DEBUG_OK 34519314Speter printf("requester doesn't have 0%o access\n", 34619314Speter msgflg & 0700); 34716671Spst#endif 34819314Speter return(eval); 34919314Speter } 35019314Speter goto found; 35119314Speter } 35219314Speter } 35319314Speter 35419314Speter#ifdef MSG_DEBUG_OK 35519314Speter printf("need to allocate the msqid_ds\n"); 35619314Speter#endif 35719314Speter if (key == IPC_PRIVATE || (msgflg & IPC_CREAT)) { 35819314Speter for (msqid = 0; msqid < msginfo.msgmni; msqid++) { 35916671Spst /* 36019314Speter * Look for an unallocated and unlocked msqid_ds. 36119314Speter * msqid_ds's can be locked by msgsnd or msgrcv while 36219314Speter * they are copying the message in/out. We can't 36319314Speter * re-use the entry until they release it. 36419314Speter */ 36519314Speter msqptr = &msqids[msqid]; 36619314Speter if (msqptr->msg_qbytes == 0 && 36719314Speter (msqptr->msg_perm.mode & MSG_LOCKED) == 0) 36819314Speter break; 36919314Speter } 37019314Speter if (msqid == msginfo.msgmni) { 37119314Speter#ifdef MSG_DEBUG_OK 37219314Speter printf("no more msqid_ds's available\n"); 37319314Speter#endif 37419314Speter return(ENOSPC); 37519314Speter } 37619314Speter#ifdef MSG_DEBUG_OK 37719314Speter printf("msqid %d is available\n", msqid); 37819314Speter#endif 37919314Speter msqptr->msg_perm.key = key; 38019314Speter msqptr->msg_perm.cuid = cred->cr_uid; 38119314Speter msqptr->msg_perm.uid = cred->cr_uid; 38216671Spst msqptr->msg_perm.cgid = cred->cr_gid; 38316671Spst msqptr->msg_perm.gid = cred->cr_gid; 38437899Snectar msqptr->msg_perm.mode = (msgflg & 0777); 38537899Snectar /* Make sure that the returned msqid is unique */ 38637899Snectar msqptr->msg_perm.seq++; 38737899Snectar msqptr->msg_first = NULL; 38837899Snectar msqptr->msg_last = NULL; 38913071Sjkh msqptr->msg_cbytes = 0; 39013071Sjkh msqptr->msg_qnum = 0; 39127365Sjkh msqptr->msg_qbytes = msginfo.msgmnb; 39213071Sjkh msqptr->msg_lspid = 0; 39313071Sjkh msqptr->msg_lrpid = 0; 39445244Scracauer msqptr->msg_stime = 0; 39545244Scracauer msqptr->msg_rtime = 0; 39613071Sjkh msqptr->msg_ctime = time_second; 39710873Sjkh } else { 39816391Sjkh#ifdef MSG_DEBUG_OK 3997259Sjkh printf("didn't find it and wasn't asked to create it\n"); 40037Srgrimes#endif 40141704Sdillon return(ENOENT); 40241704Sdillon } 40341704Sdillon 40441704Sdillonfound: 40541704Sdillon /* Construct the unique msqid */ 40641704Sdillon p->p_retval[0] = IXSEQ_TO_IPCID(msqid, msqptr->msg_perm); 40741704Sdillon return(0); 40841704Sdillon} 40941704Sdillon 41041704Sdillon#ifndef _SYS_SYSPROTO_H_ 41139162Ssosstruct msgsnd_args { 41239162Ssos int msqid; 41339162Ssos void *msgp; 41441704Sdillon size_t msgsz; 41543951Sjkh int msgflg; 41643951Sjkh}; 41743951Sjkh#endif 41841704Sdillon 41941704Sdillonint 42041704Sdillonmsgsnd(p, uap) 42143951Sjkh struct proc *p; 42243951Sjkh register struct msgsnd_args *uap; 42341704Sdillon{ 42443951Sjkh int msqid = uap->msqid; 42541704Sdillon void *user_msgp = uap->msgp; 42643951Sjkh size_t msgsz = uap->msgsz; 42743197Sdillon int msgflg = uap->msgflg; 42843797Sdillon int segs_needed, eval; 42943197Sdillon struct ucred *cred = p->p_ucred; 43043797Sdillon register struct msqid_ds *msqptr; 43143197Sdillon register struct msg *msghdr; 43241704Sdillon short next; 43341704Sdillon 43437106Sjkoshy#ifdef MSG_DEBUG_OK 43537106Sjkoshy printf("call to msgsnd(%d, 0x%x, %d, %d)\n", msqid, user_msgp, msgsz, 43639030Sjraynard msgflg); 43737106Sjkoshy#endif 43837106Sjkoshy 43937106Sjkoshy msqid = IPCID_TO_IX(msqid); 44037106Sjkoshy 44137106Sjkoshy if (msqid < 0 || msqid >= msginfo.msgmni) { 44237Srgrimes#ifdef MSG_DEBUG_OK 44337Srgrimes printf("msqid (%d) out of range (0<=msqid<%d)\n", msqid, 444 msginfo.msgmni); 445#endif 446 return(EINVAL); 447 } 448 449 msqptr = &msqids[msqid]; 450 if (msqptr->msg_qbytes == 0) { 451#ifdef MSG_DEBUG_OK 452 printf("no such message queue id\n"); 453#endif 454 return(EINVAL); 455 } 456 if (msqptr->msg_perm.seq != IPCID_TO_SEQ(uap->msqid)) { 457#ifdef MSG_DEBUG_OK 458 printf("wrong sequence number\n"); 459#endif 460 return(EINVAL); 461 } 462 463 if ((eval = ipcperm(cred, &msqptr->msg_perm, IPC_W))) { 464#ifdef MSG_DEBUG_OK 465 printf("requester doesn't have write access\n"); 466#endif 467 return(eval); 468 } 469 470 segs_needed = (msgsz + msginfo.msgssz - 1) / msginfo.msgssz; 471#ifdef MSG_DEBUG_OK 472 printf("msgsz=%d, msgssz=%d, segs_needed=%d\n", msgsz, msginfo.msgssz, 473 segs_needed); 474#endif 475 for (;;) { 476 int need_more_resources = 0; 477 478 /* 479 * check msgsz 480 * (inside this loop in case msg_qbytes changes while we sleep) 481 */ 482 483 if (msgsz > msqptr->msg_qbytes) { 484#ifdef MSG_DEBUG_OK 485 printf("msgsz > msqptr->msg_qbytes\n"); 486#endif 487 return(EINVAL); 488 } 489 490 if (msqptr->msg_perm.mode & MSG_LOCKED) { 491#ifdef MSG_DEBUG_OK 492 printf("msqid is locked\n"); 493#endif 494 need_more_resources = 1; 495 } 496 if (msgsz + msqptr->msg_cbytes > msqptr->msg_qbytes) { 497#ifdef MSG_DEBUG_OK 498 printf("msgsz + msg_cbytes > msg_qbytes\n"); 499#endif 500 need_more_resources = 1; 501 } 502 if (segs_needed > nfree_msgmaps) { 503#ifdef MSG_DEBUG_OK 504 printf("segs_needed > nfree_msgmaps\n"); 505#endif 506 need_more_resources = 1; 507 } 508 if (free_msghdrs == NULL) { 509#ifdef MSG_DEBUG_OK 510 printf("no more msghdrs\n"); 511#endif 512 need_more_resources = 1; 513 } 514 515 if (need_more_resources) { 516 int we_own_it; 517 518 if ((msgflg & IPC_NOWAIT) != 0) { 519#ifdef MSG_DEBUG_OK 520 printf("need more resources but caller doesn't want to wait\n"); 521#endif 522 return(EAGAIN); 523 } 524 525 if ((msqptr->msg_perm.mode & MSG_LOCKED) != 0) { 526#ifdef MSG_DEBUG_OK 527 printf("we don't own the msqid_ds\n"); 528#endif 529 we_own_it = 0; 530 } else { 531 /* Force later arrivals to wait for our 532 request */ 533#ifdef MSG_DEBUG_OK 534 printf("we own the msqid_ds\n"); 535#endif 536 msqptr->msg_perm.mode |= MSG_LOCKED; 537 we_own_it = 1; 538 } 539#ifdef MSG_DEBUG_OK 540 printf("goodnight\n"); 541#endif 542 eval = tsleep((caddr_t)msqptr, (PZERO - 4) | PCATCH, 543 "msgwait", 0); 544#ifdef MSG_DEBUG_OK 545 printf("good morning, eval=%d\n", eval); 546#endif 547 if (we_own_it) 548 msqptr->msg_perm.mode &= ~MSG_LOCKED; 549 if (eval != 0) { 550#ifdef MSG_DEBUG_OK 551 printf("msgsnd: interrupted system call\n"); 552#endif 553 return(EINTR); 554 } 555 556 /* 557 * Make sure that the msq queue still exists 558 */ 559 560 if (msqptr->msg_qbytes == 0) { 561#ifdef MSG_DEBUG_OK 562 printf("msqid deleted\n"); 563#endif 564 /* The SVID says to return EIDRM. */ 565#ifdef EIDRM 566 return(EIDRM); 567#else 568 /* Unfortunately, BSD doesn't define that code 569 yet! */ 570 return(EINVAL); 571#endif 572 } 573 574 } else { 575#ifdef MSG_DEBUG_OK 576 printf("got all the resources that we need\n"); 577#endif 578 break; 579 } 580 } 581 582 /* 583 * We have the resources that we need. 584 * Make sure! 585 */ 586 587 if (msqptr->msg_perm.mode & MSG_LOCKED) 588 panic("msg_perm.mode & MSG_LOCKED"); 589 if (segs_needed > nfree_msgmaps) 590 panic("segs_needed > nfree_msgmaps"); 591 if (msgsz + msqptr->msg_cbytes > msqptr->msg_qbytes) 592 panic("msgsz + msg_cbytes > msg_qbytes"); 593 if (free_msghdrs == NULL) 594 panic("no more msghdrs"); 595 596 /* 597 * Re-lock the msqid_ds in case we page-fault when copying in the 598 * message 599 */ 600 601 if ((msqptr->msg_perm.mode & MSG_LOCKED) != 0) 602 panic("msqid_ds is already locked"); 603 msqptr->msg_perm.mode |= MSG_LOCKED; 604 605 /* 606 * Allocate a message header 607 */ 608 609 msghdr = free_msghdrs; 610 free_msghdrs = msghdr->msg_next; 611 msghdr->msg_spot = -1; 612 msghdr->msg_ts = msgsz; 613 614 /* 615 * Allocate space for the message 616 */ 617 618 while (segs_needed > 0) { 619 if (nfree_msgmaps <= 0) 620 panic("not enough msgmaps"); 621 if (free_msgmaps == -1) 622 panic("nil free_msgmaps"); 623 next = free_msgmaps; 624 if (next <= -1) 625 panic("next too low #1"); 626 if (next >= msginfo.msgseg) 627 panic("next out of range #1"); 628#ifdef MSG_DEBUG_OK 629 printf("allocating segment %d to message\n", next); 630#endif 631 free_msgmaps = msgmaps[next].next; 632 nfree_msgmaps--; 633 msgmaps[next].next = msghdr->msg_spot; 634 msghdr->msg_spot = next; 635 segs_needed--; 636 } 637 638 /* 639 * Copy in the message type 640 */ 641 642 if ((eval = copyin(user_msgp, &msghdr->msg_type, 643 sizeof(msghdr->msg_type))) != 0) { 644#ifdef MSG_DEBUG_OK 645 printf("error %d copying the message type\n", eval); 646#endif 647 msg_freehdr(msghdr); 648 msqptr->msg_perm.mode &= ~MSG_LOCKED; 649 wakeup((caddr_t)msqptr); 650 return(eval); 651 } 652 user_msgp = (char *)user_msgp + sizeof(msghdr->msg_type); 653 654 /* 655 * Validate the message type 656 */ 657 658 if (msghdr->msg_type < 1) { 659 msg_freehdr(msghdr); 660 msqptr->msg_perm.mode &= ~MSG_LOCKED; 661 wakeup((caddr_t)msqptr); 662#ifdef MSG_DEBUG_OK 663 printf("mtype (%d) < 1\n", msghdr->msg_type); 664#endif 665 return(EINVAL); 666 } 667 668 /* 669 * Copy in the message body 670 */ 671 672 next = msghdr->msg_spot; 673 while (msgsz > 0) { 674 size_t tlen; 675 if (msgsz > msginfo.msgssz) 676 tlen = msginfo.msgssz; 677 else 678 tlen = msgsz; 679 if (next <= -1) 680 panic("next too low #2"); 681 if (next >= msginfo.msgseg) 682 panic("next out of range #2"); 683 if ((eval = copyin(user_msgp, &msgpool[next * msginfo.msgssz], 684 tlen)) != 0) { 685#ifdef MSG_DEBUG_OK 686 printf("error %d copying in message segment\n", eval); 687#endif 688 msg_freehdr(msghdr); 689 msqptr->msg_perm.mode &= ~MSG_LOCKED; 690 wakeup((caddr_t)msqptr); 691 return(eval); 692 } 693 msgsz -= tlen; 694 user_msgp = (char *)user_msgp + tlen; 695 next = msgmaps[next].next; 696 } 697 if (next != -1) 698 panic("didn't use all the msg segments"); 699 700 /* 701 * We've got the message. Unlock the msqid_ds. 702 */ 703 704 msqptr->msg_perm.mode &= ~MSG_LOCKED; 705 706 /* 707 * Make sure that the msqid_ds is still allocated. 708 */ 709 710 if (msqptr->msg_qbytes == 0) { 711 msg_freehdr(msghdr); 712 wakeup((caddr_t)msqptr); 713 /* The SVID says to return EIDRM. */ 714#ifdef EIDRM 715 return(EIDRM); 716#else 717 /* Unfortunately, BSD doesn't define that code yet! */ 718 return(EINVAL); 719#endif 720 } 721 722 /* 723 * Put the message into the queue 724 */ 725 726 if (msqptr->msg_first == NULL) { 727 msqptr->msg_first = msghdr; 728 msqptr->msg_last = msghdr; 729 } else { 730 msqptr->msg_last->msg_next = msghdr; 731 msqptr->msg_last = msghdr; 732 } 733 msqptr->msg_last->msg_next = NULL; 734 735 msqptr->msg_cbytes += msghdr->msg_ts; 736 msqptr->msg_qnum++; 737 msqptr->msg_lspid = p->p_pid; 738 msqptr->msg_stime = time_second; 739 740 wakeup((caddr_t)msqptr); 741 p->p_retval[0] = 0; 742 return(0); 743} 744 745#ifndef _SYS_SYSPROTO_H_ 746struct msgrcv_args { 747 int msqid; 748 void *msgp; 749 size_t msgsz; 750 long msgtyp; 751 int msgflg; 752}; 753#endif 754 755int 756msgrcv(p, uap) 757 struct proc *p; 758 register struct msgrcv_args *uap; 759{ 760 int msqid = uap->msqid; 761 void *user_msgp = uap->msgp; 762 size_t msgsz = uap->msgsz; 763 long msgtyp = uap->msgtyp; 764 int msgflg = uap->msgflg; 765 size_t len; 766 struct ucred *cred = p->p_ucred; 767 register struct msqid_ds *msqptr; 768 register struct msg *msghdr; 769 int eval; 770 short next; 771 772#ifdef MSG_DEBUG_OK 773 printf("call to msgrcv(%d, 0x%x, %d, %ld, %d)\n", msqid, user_msgp, 774 msgsz, msgtyp, msgflg); 775#endif 776 777 msqid = IPCID_TO_IX(msqid); 778 779 if (msqid < 0 || msqid >= msginfo.msgmni) { 780#ifdef MSG_DEBUG_OK 781 printf("msqid (%d) out of range (0<=msqid<%d)\n", msqid, 782 msginfo.msgmni); 783#endif 784 return(EINVAL); 785 } 786 787 msqptr = &msqids[msqid]; 788 if (msqptr->msg_qbytes == 0) { 789#ifdef MSG_DEBUG_OK 790 printf("no such message queue id\n"); 791#endif 792 return(EINVAL); 793 } 794 if (msqptr->msg_perm.seq != IPCID_TO_SEQ(uap->msqid)) { 795#ifdef MSG_DEBUG_OK 796 printf("wrong sequence number\n"); 797#endif 798 return(EINVAL); 799 } 800 801 if ((eval = ipcperm(cred, &msqptr->msg_perm, IPC_R))) { 802#ifdef MSG_DEBUG_OK 803 printf("requester doesn't have read access\n"); 804#endif 805 return(eval); 806 } 807 808 msghdr = NULL; 809 while (msghdr == NULL) { 810 if (msgtyp == 0) { 811 msghdr = msqptr->msg_first; 812 if (msghdr != NULL) { 813 if (msgsz < msghdr->msg_ts && 814 (msgflg & MSG_NOERROR) == 0) { 815#ifdef MSG_DEBUG_OK 816 printf("first message on the queue is too big (want %d, got %d)\n", 817 msgsz, msghdr->msg_ts); 818#endif 819 return(E2BIG); 820 } 821 if (msqptr->msg_first == msqptr->msg_last) { 822 msqptr->msg_first = NULL; 823 msqptr->msg_last = NULL; 824 } else { 825 msqptr->msg_first = msghdr->msg_next; 826 if (msqptr->msg_first == NULL) 827 panic("msg_first/last screwed up #1"); 828 } 829 } 830 } else { 831 struct msg *previous; 832 struct msg **prev; 833 834 previous = NULL; 835 prev = &(msqptr->msg_first); 836 while ((msghdr = *prev) != NULL) { 837 /* 838 * Is this message's type an exact match or is 839 * this message's type less than or equal to 840 * the absolute value of a negative msgtyp? 841 * Note that the second half of this test can 842 * NEVER be true if msgtyp is positive since 843 * msg_type is always positive! 844 */ 845 846 if (msgtyp == msghdr->msg_type || 847 msghdr->msg_type <= -msgtyp) { 848#ifdef MSG_DEBUG_OK 849 printf("found message type %d, requested %d\n", 850 msghdr->msg_type, msgtyp); 851#endif 852 if (msgsz < msghdr->msg_ts && 853 (msgflg & MSG_NOERROR) == 0) { 854#ifdef MSG_DEBUG_OK 855 printf("requested message on the queue is too big (want %d, got %d)\n", 856 msgsz, msghdr->msg_ts); 857#endif 858 return(E2BIG); 859 } 860 *prev = msghdr->msg_next; 861 if (msghdr == msqptr->msg_last) { 862 if (previous == NULL) { 863 if (prev != 864 &msqptr->msg_first) 865 panic("msg_first/last screwed up #2"); 866 msqptr->msg_first = 867 NULL; 868 msqptr->msg_last = 869 NULL; 870 } else { 871 if (prev == 872 &msqptr->msg_first) 873 panic("msg_first/last screwed up #3"); 874 msqptr->msg_last = 875 previous; 876 } 877 } 878 break; 879 } 880 previous = msghdr; 881 prev = &(msghdr->msg_next); 882 } 883 } 884 885 /* 886 * We've either extracted the msghdr for the appropriate 887 * message or there isn't one. 888 * If there is one then bail out of this loop. 889 */ 890 891 if (msghdr != NULL) 892 break; 893 894 /* 895 * Hmph! No message found. Does the user want to wait? 896 */ 897 898 if ((msgflg & IPC_NOWAIT) != 0) { 899#ifdef MSG_DEBUG_OK 900 printf("no appropriate message found (msgtyp=%d)\n", 901 msgtyp); 902#endif 903 /* The SVID says to return ENOMSG. */ 904#ifdef ENOMSG 905 return(ENOMSG); 906#else 907 /* Unfortunately, BSD doesn't define that code yet! */ 908 return(EAGAIN); 909#endif 910 } 911 912 /* 913 * Wait for something to happen 914 */ 915 916#ifdef MSG_DEBUG_OK 917 printf("msgrcv: goodnight\n"); 918#endif 919 eval = tsleep((caddr_t)msqptr, (PZERO - 4) | PCATCH, "msgwait", 920 0); 921#ifdef MSG_DEBUG_OK 922 printf("msgrcv: good morning (eval=%d)\n", eval); 923#endif 924 925 if (eval != 0) { 926#ifdef MSG_DEBUG_OK 927 printf("msgsnd: interrupted system call\n"); 928#endif 929 return(EINTR); 930 } 931 932 /* 933 * Make sure that the msq queue still exists 934 */ 935 936 if (msqptr->msg_qbytes == 0 || 937 msqptr->msg_perm.seq != IPCID_TO_SEQ(uap->msqid)) { 938#ifdef MSG_DEBUG_OK 939 printf("msqid deleted\n"); 940#endif 941 /* The SVID says to return EIDRM. */ 942#ifdef EIDRM 943 return(EIDRM); 944#else 945 /* Unfortunately, BSD doesn't define that code yet! */ 946 return(EINVAL); 947#endif 948 } 949 } 950 951 /* 952 * Return the message to the user. 953 * 954 * First, do the bookkeeping (before we risk being interrupted). 955 */ 956 957 msqptr->msg_cbytes -= msghdr->msg_ts; 958 msqptr->msg_qnum--; 959 msqptr->msg_lrpid = p->p_pid; 960 msqptr->msg_rtime = time_second; 961 962 /* 963 * Make msgsz the actual amount that we'll be returning. 964 * Note that this effectively truncates the message if it is too long 965 * (since msgsz is never increased). 966 */ 967 968#ifdef MSG_DEBUG_OK 969 printf("found a message, msgsz=%d, msg_ts=%d\n", msgsz, 970 msghdr->msg_ts); 971#endif 972 if (msgsz > msghdr->msg_ts) 973 msgsz = msghdr->msg_ts; 974 975 /* 976 * Return the type to the user. 977 */ 978 979 eval = copyout((caddr_t)&(msghdr->msg_type), user_msgp, 980 sizeof(msghdr->msg_type)); 981 if (eval != 0) { 982#ifdef MSG_DEBUG_OK 983 printf("error (%d) copying out message type\n", eval); 984#endif 985 msg_freehdr(msghdr); 986 wakeup((caddr_t)msqptr); 987 return(eval); 988 } 989 user_msgp = (char *)user_msgp + sizeof(msghdr->msg_type); 990 991 /* 992 * Return the segments to the user 993 */ 994 995 next = msghdr->msg_spot; 996 for (len = 0; len < msgsz; len += msginfo.msgssz) { 997 size_t tlen; 998 999 if (msgsz - len > msginfo.msgssz) 1000 tlen = msginfo.msgssz; 1001 else 1002 tlen = msgsz - len; 1003 if (next <= -1) 1004 panic("next too low #3"); 1005 if (next >= msginfo.msgseg) 1006 panic("next out of range #3"); 1007 eval = copyout((caddr_t)&msgpool[next * msginfo.msgssz], 1008 user_msgp, tlen); 1009 if (eval != 0) { 1010#ifdef MSG_DEBUG_OK 1011 printf("error (%d) copying out message segment\n", 1012 eval); 1013#endif 1014 msg_freehdr(msghdr); 1015 wakeup((caddr_t)msqptr); 1016 return(eval); 1017 } 1018 user_msgp = (char *)user_msgp + tlen; 1019 next = msgmaps[next].next; 1020 } 1021 1022 /* 1023 * Done, return the actual number of bytes copied out. 1024 */ 1025 1026 msg_freehdr(msghdr); 1027 wakeup((caddr_t)msqptr); 1028 p->p_retval[0] = msgsz; 1029 return(0); 1030} 1031