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