1 /* $FreeBSD: src/sys/kern/sysv_msg.c,v 1.23.2.5 2002/12/31 08:54:53 maxim Exp $ */ 2 3 /* 4 * Implementation of SVID messages 5 * 6 * Author: Daniel Boulet 7 * 8 * Copyright 1993 Daniel Boulet and RTMX Inc. 9 * Copyright (c) 2013 Larisa Grigore <larisagrigore@gmail.com> 10 * 11 * This system call was implemented by Daniel Boulet under contract from RTMX. 12 * 13 * Redistribution and use in source forms, with and without modification, 14 * are permitted provided that this entire comment appears intact. 15 * 16 * Redistribution in binary form may occur without any restrictions. 17 * Obviously, it would be nice if you gave credit where credit is due 18 * but requiring it would be too onerous. 19 * 20 * This software is provided ``AS IS'' without any warranties of any kind. 21 */ 22 23 #include "namespace.h" 24 #include <stdio.h> 25 #include <stdlib.h> 26 #include <errno.h> 27 #include <err.h> 28 #include <pthread.h> 29 #include <string.h> 30 #include <stdarg.h> 31 #include <sys/param.h> 32 #include <sys/queue.h> 33 #include <sys/mman.h> 34 #include "un-namespace.h" 35 36 #include "sysvipc_lock.h" 37 #include "sysvipc_ipc.h" 38 #include "sysvipc_hash.h" 39 #include "sysvipc_msg.h" 40 #include "sysvipc_shm.h" 41 42 #define SYSV_MUTEX_LOCK(x) if (__isthreaded) _pthread_mutex_lock(x) 43 #define SYSV_MUTEX_UNLOCK(x) if (__isthreaded) _pthread_mutex_unlock(x) 44 #define SYSV_MUTEX_DESTROY(x) if (__isthreaded) _pthread_mutex_destroy(x) 45 46 extern struct hashtable *shmaddrs; 47 extern struct hashtable *shmres; 48 extern pthread_mutex_t lock_resources; 49 50 struct msginfo msginfo = { 51 MSGMAX, /* max chars in a message */ 52 MSGMNI, /* # of message queue identifiers */ 53 MSGMNB, /* max chars in a queue */ 54 MSGTQL, /* max messages in system */ 55 MSGSSZ, /* size of a message segment (must be small power of 2 greater than 4) */ 56 MSGSEG /* number of message segments */ 57 }; 58 59 static int 60 put_shmdata(int id) { 61 struct shm_data *data; 62 int ret = -1; 63 64 SYSV_MUTEX_LOCK(&lock_resources); 65 data = _hash_lookup(shmres, id); 66 if (!data) { 67 sysv_print_err("something wrong put_shmdata\n"); 68 goto done; /* It should not reach here. */ 69 } 70 71 data->used--; 72 if (data->used == 0 && data->removed) { 73 sysv_print("really remove the sem\n"); 74 SYSV_MUTEX_UNLOCK(&lock_resources); 75 /* OBS: Even if the shmctl fails (the thread doesn't 76 * have IPC_M permissions), all structures associated 77 * with it will be removed in the current process.*/ 78 shmdt(data->internal); 79 if (data->removed == SEG_ALREADY_REMOVED) 80 return 1; /* The queue was removed 81 by another process so there is nothing else 82 we must do. */ 83 /* Else inform the daemon that the segment is removed. */ 84 return (sysvipc_shmctl(id, IPC_RMID, NULL)); 85 } 86 87 ret = 0; 88 done: 89 SYSV_MUTEX_UNLOCK(&lock_resources); 90 return (ret); 91 } 92 93 static struct msqid_pool* 94 get_msqpptr(int msqid, int to_remove, int shm_access) { 95 struct msqid_pool *msqpptr; 96 97 struct shm_data *shmdata = 98 get_shmdata(msqid, to_remove, shm_access); 99 if (!shmdata) { 100 /* Error is set in get_shmdata. */ 101 return NULL; 102 } 103 104 msqpptr = (struct msqid_pool *)shmdata->internal; 105 if (!msqpptr) { 106 put_shmdata(msqid); 107 errno = EINVAL; 108 return NULL; 109 } 110 111 return msqpptr; 112 } 113 114 static int 115 msqp_exist(int msqid, struct msqid_pool *msqpptr) { 116 /* Was it removed? */ 117 if (msqpptr->gen == -1 || 118 msqpptr->ds.msg_perm.seq != IPCID_TO_SEQ(msqid)) 119 return 0; 120 121 return 1; 122 } 123 124 static int 125 try_rwlock_rdlock(int msqid, struct msqid_pool *msqpptr) { 126 sysv_print("try get rd lock\n"); 127 #ifdef SYSV_RWLOCK 128 sysv_rwlock_rdlock(&msqpptr->rwlock); 129 #else 130 sysv_mutex_lock(&msqpptr->mutex); 131 #endif 132 sysv_print("get rd lock\n"); 133 if (!msqp_exist(msqid, msqpptr)) { 134 errno = EINVAL; 135 sysv_print("error rd lock\n"); 136 #ifdef SYSV_RWLOCK 137 sysv_rwlock_unlock(&msqpptr->rwlock); 138 #else 139 sysv_mutex_unlock(&msqpptr->mutex); 140 #endif 141 return -1; 142 } 143 sysv_print("end rd lock\n"); 144 return 0; 145 } 146 147 static int 148 try_rwlock_wrlock(int msqid, struct msqid_pool *msqpptr) { 149 sysv_print("try get wr lock\n"); 150 #ifdef SYSV_RWLOCK 151 sysv_rwlock_wrlock(&msqpptr->rwlock); 152 #else 153 sysv_mutex_lock(&msqpptr->mutex); 154 #endif 155 sysv_print("get wr lock\n"); 156 if (!msqp_exist(msqid, msqpptr)) { 157 sysv_print("error rw lock\n"); 158 errno = EINVAL; 159 #ifdef SYSV_RWLOCK 160 sysv_rwlock_unlock(&msqpptr->rwlock); 161 #else 162 sysv_mutex_unlock(&msqpptr->mutex); 163 #endif 164 return -1; 165 } 166 sysv_print("end rw lock\n"); 167 return 0; 168 } 169 170 static int 171 rwlock_unlock(int msqid, struct msqid_pool *msqpptr) { 172 if (!msqp_exist(msqid, msqpptr)) { 173 errno = EINVAL; 174 return -1; 175 } 176 #ifdef SYSV_RWLOCK 177 sysv_rwlock_unlock(&msqpptr->rwlock); 178 #else 179 sysv_mutex_unlock(&msqpptr->mutex); 180 #endif 181 sysv_print("unlock rw lock\n"); 182 return 0; 183 } 184 185 static void 186 msg_freehdr(struct msqid_pool *msqpptr, struct msg *msghdr) 187 { 188 while (msghdr->msg_ts > 0) { 189 short next; 190 if (msghdr->msg_spot < 0 || msghdr->msg_spot >= msginfo.msgseg) { 191 sysv_print_err("msghdr->msg_spot out of range"); 192 exit(-1); 193 } 194 next = msqpptr->msgmaps[msghdr->msg_spot].next; 195 msqpptr->msgmaps[msghdr->msg_spot].next = 196 msqpptr->free_msgmaps; 197 msqpptr->free_msgmaps = msghdr->msg_spot; 198 msqpptr->nfree_msgmaps++; 199 msghdr->msg_spot = next; 200 if (msghdr->msg_ts >= msginfo.msgssz) 201 msghdr->msg_ts -= msginfo.msgssz; 202 else 203 msghdr->msg_ts = 0; 204 } 205 if (msghdr->msg_spot != -1) { 206 sysv_print_err("msghdr->msg_spot != -1"); 207 exit(-1); 208 } 209 msghdr->msg_next = msqpptr->free_msghdrs; 210 msqpptr->free_msghdrs = (msghdr - &msqpptr->msghdrs[0]) / 211 sizeof(struct msg); 212 } 213 214 int 215 sysvipc_msgget(key_t key, int msgflg) { 216 int msqid; 217 void *shmaddr; 218 size_t size = sizeof(struct msqid_pool); 219 220 msqid = _shmget(key, size, msgflg, MSGGET); 221 if (msqid == -1) 222 goto done; 223 224 /* If the msg is in process of being removed there are two cases: 225 * - the daemon knows that and it will handle this situation. 226 * - one of the threads from this address space remove it and the daemon 227 * wasn't announced yet; in this scenario, the msg is marked 228 * using "removed" field of shm_data and future calls will return 229 * EIDRM error. 230 */ 231 232 #if 0 233 /* Set access type. */ 234 shm_access = semflg & (IPC_W | IPC_R); 235 if(set_shmdata_access(semid, shm_access) != 0) { 236 /* errno already set. */ 237 goto done; 238 } 239 #endif 240 241 shmaddr = sysvipc_shmat(msqid, NULL, 0); 242 if (!shmaddr) { 243 msqid = -1; 244 sysvipc_shmctl(msqid, IPC_RMID, NULL); 245 goto done; 246 } 247 sysv_print("shmaddr = %lx\n", (unsigned long)shmaddr); 248 249 done: 250 return msqid; 251 } 252 253 int 254 sysvipc_msgctl(int msqid, int cmd, struct msqid_ds *buf) { 255 int error; 256 struct msqid_pool *msqpptr = NULL; 257 struct shmid_ds shmds; 258 int shm_access = 0; 259 260 error = 0; 261 262 switch (cmd) { 263 case IPC_SET: /* Originally was IPC_M but this is checked 264 by daemon. */ 265 shm_access = IPC_W; 266 break; 267 case IPC_STAT: 268 shm_access = IPC_R; 269 break; 270 default: 271 break; 272 } 273 274 msqpptr = get_msqpptr(msqid, cmd==IPC_RMID, shm_access); 275 if (!msqpptr) { 276 errno = EINVAL; 277 return -1; 278 } 279 280 switch (cmd) { 281 case IPC_RMID: 282 /* Mark that the segment is removed. This is done in 283 * get_msqpptr call in order to announce other processes. 284 * It will be actually removed after put_shmdata call and 285 * not other thread from this address space use shm_data 286 * structure. 287 */ 288 break; 289 case IPC_SET: 290 error = try_rwlock_rdlock(msqid, msqpptr); 291 if (error) 292 break; 293 if (buf->msg_qbytes == 0) { 294 sysv_print_err("can't reduce msg_qbytes to 0\n"); 295 errno = EINVAL; /* non-standard errno! */ 296 rwlock_unlock(msqid, msqpptr); 297 break; 298 } 299 rwlock_unlock(msqid, msqpptr); 300 301 memset(&shmds, 0, sizeof(shmds)/sizeof(unsigned char)); 302 memcpy(&shmds.shm_perm, &buf->msg_perm, 303 sizeof(struct ipc_perm)); 304 error = sysvipc_shmctl(msqid, cmd, &shmds); 305 if (error) 306 break; 307 308 /* There is no need to check if we have right to modify the 309 * size because we have right to change other fileds. */ 310 311 if (round_page(buf->msg_qbytes) != 312 round_page(msqpptr->ds.msg_qbytes)) { 313 sysv_print("change msg size\n"); 314 /* TODO same as in semundo_adjust only 315 * that there is no way to inform other 316 * processes about the change. */ 317 } 318 319 error = try_rwlock_wrlock(msqid, msqpptr); 320 if (error) 321 break; 322 msqpptr->ds.msg_qbytes = buf->msg_qbytes; 323 rwlock_unlock(msqid, msqpptr); 324 /* OBS: didn't update ctime and mode as in kernel implementation 325 * it is done. Those fields are already updated for shmid_ds 326 * struct when calling shmctl 327 */ 328 break; 329 330 case IPC_STAT: 331 error = sysvipc_shmctl(msqid, cmd, &shmds); 332 if (error) 333 break; 334 335 memcpy(&buf->msg_perm, &shmds.shm_perm, 336 sizeof(struct ipc_perm)); 337 buf->msg_ctime = shmds.shm_ctime; 338 339 /* Read fields that are not kept in shmds. */ 340 error = try_rwlock_rdlock(msqid, msqpptr); 341 if (error) 342 break; 343 buf->msg_first = (struct msg *)(u_long) 344 msqpptr->ds.first.msg_first_index; 345 buf->msg_last = (struct msg *)(u_long) 346 msqpptr->ds.last.msg_last_index; 347 buf->msg_cbytes = msqpptr->ds.msg_cbytes; 348 buf->msg_qnum = msqpptr->ds.msg_qnum; 349 buf->msg_qbytes = msqpptr->ds.msg_qbytes; 350 buf->msg_lspid = msqpptr->ds.msg_lspid; 351 buf->msg_lrpid = msqpptr->ds.msg_lrpid; 352 buf->msg_stime = msqpptr->ds.msg_stime; 353 buf->msg_rtime = msqpptr->ds.msg_rtime; 354 rwlock_unlock(msqid, msqpptr); 355 break; 356 default: 357 sysv_print_err("invalid command %d\n", cmd); 358 errno = EINVAL; 359 break; 360 } 361 362 put_shmdata(msqid); 363 364 return(error); 365 } 366 367 int 368 sysvipc_msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg) 369 { 370 int segs_needed, error; 371 struct msg *msghdr; 372 struct msqid_pool *msqpptr, *auxmsqpptr; 373 struct msqid_ds_internal *msqptr; 374 short next; 375 int val_to_sleep; 376 char *auxmsgp = (char *)msgp; 377 int _index; 378 379 sysv_print("call to msgsnd(%d, %ld, %d)\n", msqid, msgsz, msgflg); 380 381 /*if (!jail_sysvipc_allowed && td->td_ucred->cr_prison != NULL) 382 return (ENOSYS); 383 */ 384 if (!msgp) { 385 errno = EINVAL; 386 return -1; 387 } 388 389 msqpptr = get_msqpptr(msqid, 0, IPC_W); 390 if (!msqpptr) { 391 errno = EINVAL; 392 return -1; 393 } 394 error = -1; 395 396 if (try_rwlock_wrlock(msqid, msqpptr) == -1) { 397 errno = EIDRM; 398 goto done; 399 } 400 401 msqptr = &msqpptr->ds; 402 403 segs_needed = (msgsz + msginfo.msgssz - 1) / msginfo.msgssz; 404 sysv_print("msgsz=%ld, msgssz=%d, segs_needed=%d\n", msgsz, msginfo.msgssz, 405 segs_needed); 406 for (;;) { 407 int need_more_resources = 0; 408 409 if (msgsz + msqptr->msg_cbytes > msqptr->msg_qbytes) { 410 sysv_print("msgsz + msg_cbytes > msg_qbytes\n"); 411 need_more_resources = 1; 412 } 413 414 if (segs_needed > msqpptr->nfree_msgmaps) { 415 sysv_print("segs_needed > nfree_msgmaps (= %d)\n", 416 msqpptr->nfree_msgmaps); 417 need_more_resources = 1; 418 } 419 420 if (msqpptr->free_msghdrs == -1) { 421 sysv_print("no more msghdrs\n"); 422 need_more_resources = 1; 423 } 424 425 if (need_more_resources) { 426 if ((msgflg & IPC_NOWAIT) != 0) { 427 sysv_print_err("need more resources but caller doesn't want to wait\n"); 428 errno = EAGAIN; 429 goto done; 430 } 431 432 sysv_print("goodnight\n"); 433 val_to_sleep = msqpptr->gen; 434 rwlock_unlock(msqid, msqpptr); 435 put_shmdata(msqid); 436 437 if (umtx_sleep((int *)&msqpptr->gen, val_to_sleep, SYSV_TIMEOUT) != 0) { 438 sysv_print_err("msgsnd: interrupted system call\n"); 439 errno = EINTR; 440 goto done; 441 } 442 443 /* Check if another thread didn't remove the msg queue. */ 444 auxmsqpptr = get_msqpptr(msqid, 0, IPC_W); 445 if (!auxmsqpptr) { 446 errno = EIDRM; 447 return -1; 448 } 449 450 if (auxmsqpptr != msqpptr) { 451 errno = EIDRM; 452 goto done; 453 } 454 455 /* Check if another process didn't remove the queue. */ 456 if (try_rwlock_wrlock(msqid, msqpptr) == -1) { 457 errno = EIDRM; 458 goto done; 459 } 460 461 if (msqptr != &msqpptr->ds) { 462 sysv_print("msqptr != &msqpptr->ds"); 463 exit(-1); 464 } 465 466 } else { 467 sysv_print("got all the resources that we need\n"); 468 break; 469 } 470 } 471 472 /* 473 * We have the resources that we need. 474 * Make sure! 475 */ 476 #if 0 477 if (segs_needed > nfree_msgmaps) { 478 sysv_print_err("segs_needed > nfree_msgmaps"); 479 exit(-1); 480 } 481 #endif 482 if (msgsz + msqptr->msg_cbytes > msqptr->msg_qbytes) { 483 sysv_print_err("msgsz + msg_cbytes > msg_qbytes"); 484 exit(-1); 485 } 486 487 /* 488 * Allocate a message header 489 */ 490 msghdr = &msqpptr->msghdrs[msqpptr->free_msghdrs]; 491 msqpptr->free_msghdrs = msghdr->msg_next; 492 msghdr->msg_spot = -1; 493 msghdr->msg_ts = msgsz; 494 495 /* 496 * Allocate space for the message 497 */ 498 while (segs_needed > 0) { 499 next = msqpptr->free_msgmaps; 500 if (next < 0 || next > msginfo.msgseg) { 501 sysv_print_err("out of range free_msgmaps %d #1\n", next); 502 exit(-1); 503 } 504 505 msqpptr->free_msgmaps = msqpptr->msgmaps[next].next; 506 msqpptr->nfree_msgmaps--; 507 msqpptr->msgmaps[next].next = msghdr->msg_spot; 508 msghdr->msg_spot = next; 509 segs_needed--; 510 } 511 512 /* 513 * Copy in the message type 514 */ 515 memcpy(&msghdr->msg_type, auxmsgp, sizeof(msghdr->msg_type)); 516 auxmsgp = (char *)auxmsgp + sizeof(msghdr->msg_type); 517 518 /* 519 * Validate the message type 520 */ 521 sysv_print("msg_type = %ld\n", msghdr->msg_type); 522 523 if (msghdr->msg_type < 1) { 524 msg_freehdr(msqpptr, msghdr); 525 umtx_wakeup((int *)&msqpptr->gen, 0); 526 sysv_print_err("mtype (%ld) < 1\n", msghdr->msg_type); 527 errno = EINVAL; 528 goto done; 529 } 530 531 /* 532 * Copy in the message body 533 */ 534 next = msghdr->msg_spot; 535 while (msgsz > 0) { 536 size_t tlen; 537 if (msgsz > (size_t)msginfo.msgssz) 538 tlen = msginfo.msgssz; 539 else 540 tlen = msgsz; 541 if (next < 0 || next > msginfo.msgseg) { 542 sysv_print_err("out of range free_msgmaps %d #2\n", next); 543 exit(-1); 544 } 545 546 memcpy(&msqpptr->msgpool[next * msginfo.msgssz], auxmsgp, tlen); 547 msgsz -= tlen; 548 auxmsgp = (char *)auxmsgp + tlen; 549 next = msqpptr->msgmaps[next].next; 550 } 551 552 /* 553 * Put the message into the queue 554 */ 555 _index = (msghdr - &msqpptr->msghdrs[0]) / 556 sizeof(struct msg); 557 sysv_print("index_msghdr = %d\n", _index); 558 if (msqptr->first.msg_first_index == -1) { 559 msqptr->first.msg_first_index = _index; 560 msqptr->last.msg_last_index = _index; 561 } else { 562 msqpptr->msghdrs[msqptr->last.msg_last_index].msg_next = _index; 563 msqptr->last.msg_last_index = _index; 564 } 565 msqpptr->msghdrs[msqptr->last.msg_last_index].msg_next = -1; 566 567 msqptr->msg_cbytes += msghdr->msg_ts; 568 msqptr->msg_qnum++; 569 msqptr->msg_lspid = getpid(); 570 msqptr->msg_stime = time(NULL); 571 572 umtx_wakeup((int *)&msqpptr->gen, 0); 573 error = 0; 574 575 done: 576 rwlock_unlock(msqid, msqpptr); 577 put_shmdata(msqid); 578 return(error); 579 } 580 581 int 582 sysvipc_msgrcv(int msqid, void *msgp, size_t msgsz, long mtype, int msgflg) 583 { 584 size_t len; 585 struct msqid_pool *msqpptr, *auxmsqpptr; 586 struct msqid_ds_internal *msqptr; 587 struct msg *msghdr; 588 short msghdr_index; 589 int error; 590 short next; 591 int val_to_sleep; 592 char *auxmsgp = (char *)msgp; 593 594 sysv_print("call to msgrcv(%d, %ld, %ld, %d)\n", msqid, msgsz, mtype, msgflg); 595 /* 596 if (!jail_sysvipc_allowed && td->td_ucred->cr_prison != NULL) 597 return (ENOSYS); 598 */ 599 600 if (!msgp) { 601 errno = EINVAL; 602 return -1; 603 } 604 605 msqpptr = get_msqpptr(msqid, 0, IPC_R); 606 if (!msqpptr) { 607 errno = EINVAL; 608 return -1; 609 } 610 611 error = -1; 612 613 if (try_rwlock_wrlock(msqid, msqpptr) == -1) { 614 errno = EIDRM; 615 goto done; 616 } 617 618 msqptr = &msqpptr->ds; 619 620 msghdr_index = -1; 621 while (msghdr_index == -1) { 622 if (mtype == 0) { 623 msghdr_index = msqptr->first.msg_first_index; 624 msghdr = &msqpptr->msghdrs[msghdr_index]; 625 if (msghdr_index != -1) { 626 if (msgsz < msghdr->msg_ts && 627 (msgflg & MSG_NOERROR) == 0) { 628 sysv_print_err("first message on the queue is too big" 629 "(want %d, got %d)\n", 630 msgsz, msghdr->msg_ts); 631 errno = E2BIG; 632 goto done; 633 } 634 if (msqptr->first.msg_first_index == msqptr->last.msg_last_index) { 635 msqptr->first.msg_first_index = -1; 636 msqptr->last.msg_last_index = -1; 637 } else { 638 msqptr->first.msg_first_index = msghdr->msg_next; 639 if (msqptr->first.msg_first_index == -1) { 640 sysv_print_err("first.msg_first_index/last screwed up #1"); 641 exit(-1); 642 } 643 } 644 } 645 } else { 646 short previous; 647 short prev; 648 previous = -1; 649 prev = msqptr->first.msg_first_index; 650 while ((msghdr_index = prev) != -1) { 651 msghdr = &msqpptr->msghdrs[msghdr_index]; 652 /* 653 * Is this message's type an exact match or is 654 * this message's type less than or equal to 655 * the absolute value of a negative mtype? 656 * Note that the second half of this test can 657 * NEVER be true if mtype is positive since 658 * msg_type is always positive! 659 */ 660 if (mtype == msghdr->msg_type || 661 msghdr->msg_type <= -mtype) { 662 sysv_print("found message type %d, requested %d\n", 663 msghdr->msg_type, mtype); 664 if (msgsz < msghdr->msg_ts && 665 (msgflg & MSG_NOERROR) == 0) { 666 sysv_print_err("requested message on the queue" 667 " is too big (want %d, got %d)\n", 668 msgsz, msghdr->msg_ts); 669 errno = E2BIG; 670 goto done; 671 } 672 prev = msghdr->msg_next; 673 if (msghdr_index == msqptr->last.msg_last_index) { 674 if (previous == -1) { 675 msqptr->first.msg_first_index = -1; 676 msqptr->last.msg_last_index = -1; 677 } else { 678 msqptr->last.msg_last_index = previous; 679 } 680 } 681 break; 682 } 683 previous = msghdr_index; 684 prev = msghdr->msg_next; 685 } 686 } 687 688 /* 689 * We've either extracted the msghdr for the appropriate 690 * message or there isn't one. 691 * If there is one then bail out of this loop. 692 */ 693 if (msghdr_index != -1) 694 break; 695 696 /* 697 * No message found. Does the user want to wait? 698 */ 699 if ((msgflg & IPC_NOWAIT) != 0) { 700 sysv_print_err("no appropriate message found (mtype=%d)\n", 701 mtype); 702 errno = ENOMSG; 703 goto done; 704 } 705 706 /* 707 * Wait for something to happen 708 */ 709 sysv_print("goodnight\n"); 710 val_to_sleep = msqpptr->gen; 711 rwlock_unlock(msqid, msqpptr); 712 put_shmdata(msqid); 713 714 /* We don't sleep more than SYSV_TIMEOUT because we could 715 * go to sleep after another process calls wakeup and remain 716 * blocked. 717 */ 718 if (umtx_sleep((int *)&msqpptr->gen, val_to_sleep, SYSV_TIMEOUT) != 0) { 719 sysv_print_err("msgrcv: interrupted system call\n"); 720 errno = EINTR; 721 goto done; 722 } 723 sysv_print("msgrcv: good morning (error=%d)\n", errno); 724 725 /* Check if another thread didn't remove the msg queue. */ 726 auxmsqpptr = get_msqpptr(msqid, 0, IPC_R); 727 if (!auxmsqpptr) { 728 errno = EIDRM; 729 return -1; 730 } 731 732 if (auxmsqpptr != msqpptr) { 733 errno = EIDRM; 734 goto done; 735 } 736 737 /* Check if another process didn't remove the msg queue. */ 738 if (try_rwlock_wrlock(msqid, msqpptr) == -1) { 739 errno = EIDRM; 740 goto done; 741 } 742 743 if (msqptr != &msqpptr->ds) { 744 sysv_print_err("msqptr != &msqpptr->ds"); 745 exit(-1); 746 } 747 } 748 749 /* 750 * Return the message to the user. 751 */ 752 msqptr->msg_cbytes -= msghdr->msg_ts; 753 msqptr->msg_qnum--; 754 msqptr->msg_lrpid = getpid(); 755 msqptr->msg_rtime = time(NULL); 756 757 /* 758 * Make msgsz the actual amount that we'll be returning. 759 * Note that this effectively truncates the message if it is too long 760 * (since msgsz is never increased). 761 */ 762 sysv_print("found a message, msgsz=%d, msg_ts=%d\n", msgsz, 763 msghdr->msg_ts); 764 if (msgsz > msghdr->msg_ts) 765 msgsz = msghdr->msg_ts; 766 767 /* 768 * Return the type to the user. 769 */ 770 memcpy(auxmsgp, (caddr_t)&(msghdr->msg_type), sizeof(msghdr->msg_type)); 771 auxmsgp = (char *)auxmsgp + sizeof(msghdr->msg_type); 772 773 /* 774 * Return the segments to the user 775 */ 776 next = msghdr->msg_spot; 777 for (len = 0; len < msgsz; len += msginfo.msgssz) { 778 size_t tlen; 779 780 if (msgsz - len > (size_t)msginfo.msgssz) 781 tlen = msginfo.msgssz; 782 else 783 tlen = msgsz - len; 784 if (next < 0 || next > msginfo.msgseg) { 785 sysv_print_err("out of range free_msgmaps %d #3\n", next); 786 exit(-1); 787 } 788 789 memcpy(auxmsgp, (caddr_t)&msqpptr->msgpool[next * msginfo.msgssz], tlen); 790 auxmsgp = (char *)auxmsgp + tlen; 791 next = msqpptr->msgmaps[next].next; 792 } 793 794 /* 795 * Done, return the actual number of bytes copied out. 796 */ 797 msg_freehdr(msqpptr, msghdr); 798 umtx_wakeup((int *)&msqpptr->gen, 0); 799 error = msgsz; 800 done: 801 rwlock_unlock(msqid, msqpptr); 802 put_shmdata(msqid); 803 return(error); 804 } 805