a0dd533769353cc8c58b47c9142e40d8ade864eb
[dragonfly.git] / lib / libc / sysvipc / msg.c
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 }