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