xref: /dragonfly/lib/libc/sysvipc/msg.c (revision cd1c6085)
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