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
put_shmdata(int id)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*
get_msqpptr(int msqid,int to_remove,int shm_access)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
msqp_exist(int msqid,struct msqid_pool * msqpptr)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
try_rwlock_rdlock(int msqid,struct msqid_pool * msqpptr)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
try_rwlock_wrlock(int msqid,struct msqid_pool * msqpptr)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
rwlock_unlock(int msqid,struct msqid_pool * msqpptr)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
msg_freehdr(struct msqid_pool * msqpptr,struct msg * msghdr)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
sysvipc_msgget(key_t key,int msgflg)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
sysvipc_msgctl(int msqid,int cmd,struct msqid_ds * buf)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
sysvipc_msgsnd(int msqid,const void * msgp,size_t msgsz,int msgflg)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
sysvipc_msgrcv(int msqid,void * msgp,size_t msgsz,long mtype,int msgflg)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