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