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