]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/kern/sysv_msg.c
This commit was generated by cvs2svn to compensate for changes in r68673,
[FreeBSD/FreeBSD.git] / sys / kern / sysv_msg.c
1 /* $FreeBSD$ */
2
3 /*
4  * Implementation of SVID messages
5  *
6  * Author:  Daniel Boulet
7  *
8  * Copyright 1993 Daniel Boulet and RTMX Inc.
9  *
10  * This system call was implemented by Daniel Boulet under contract from RTMX.
11  *
12  * Redistribution and use in source forms, with and without modification,
13  * are permitted provided that this entire comment appears intact.
14  *
15  * Redistribution in binary form may occur without any restrictions.
16  * Obviously, it would be nice if you gave credit where credit is due
17  * but requiring it would be too onerous.
18  *
19  * This software is provided ``AS IS'' without any warranties of any kind.
20  */
21
22 #include "opt_sysvipc.h"
23
24 #include <sys/param.h>
25 #include <sys/systm.h>
26 #include <sys/sysproto.h>
27 #include <sys/kernel.h>
28 #include <sys/proc.h>
29 #include <sys/msg.h>
30 #include <sys/sysent.h>
31 #include <sys/sysctl.h>
32 #include <sys/malloc.h>
33 #include <sys/jail.h>
34
35 static MALLOC_DEFINE(M_MSG, "msg", "SVID compatible message queues");
36
37 static void msginit __P((void *));
38
39 #define MSG_DEBUG
40 #undef MSG_DEBUG_OK
41
42 static void msg_freehdr __P((struct msg *msghdr));
43
44 /* XXX casting to (sy_call_t *) is bogus, as usual. */
45 static sy_call_t *msgcalls[] = {
46         (sy_call_t *)msgctl, (sy_call_t *)msgget,
47         (sy_call_t *)msgsnd, (sy_call_t *)msgrcv
48 };
49
50 struct msg {
51         struct  msg *msg_next;  /* next msg in the chain */
52         long    msg_type;       /* type of this message */
53                                 /* >0 -> type of this message */
54                                 /* 0 -> free header */
55         u_short msg_ts;         /* size of this message */
56         short   msg_spot;       /* location of start of msg in buffer */
57 };
58
59
60 #ifndef MSGSSZ
61 #define MSGSSZ  8               /* Each segment must be 2^N long */
62 #endif
63 #ifndef MSGSEG
64 #define MSGSEG  2048            /* must be less than 32767 */
65 #endif
66 #define MSGMAX  (MSGSSZ*MSGSEG)
67 #ifndef MSGMNB
68 #define MSGMNB  2048            /* max # of bytes in a queue */
69 #endif
70 #ifndef MSGMNI
71 #define MSGMNI  40
72 #endif
73 #ifndef MSGTQL
74 #define MSGTQL  40
75 #endif
76
77 /*
78  * Based on the configuration parameters described in an SVR2 (yes, two)
79  * config(1m) man page.
80  *
81  * Each message is broken up and stored in segments that are msgssz bytes
82  * long.  For efficiency reasons, this should be a power of two.  Also,
83  * it doesn't make sense if it is less than 8 or greater than about 256.
84  * Consequently, msginit in kern/sysv_msg.c checks that msgssz is a power of
85  * two between 8 and 1024 inclusive (and panic's if it isn't).
86  */
87 struct msginfo msginfo = {
88                 MSGMAX,         /* max chars in a message */
89                 MSGMNI,         /* # of message queue identifiers */
90                 MSGMNB,         /* max chars in a queue */
91                 MSGTQL,         /* max messages in system */
92                 MSGSSZ,         /* size of a message segment */
93                                 /* (must be small power of 2 greater than 4) */
94                 MSGSEG          /* number of message segments */
95 };
96
97 /*
98  * macros to convert between msqid_ds's and msqid's.
99  * (specific to this implementation)
100  */
101 #define MSQID(ix,ds)    ((ix) & 0xffff | (((ds).msg_perm.seq << 16) & 0xffff0000))
102 #define MSQID_IX(id)    ((id) & 0xffff)
103 #define MSQID_SEQ(id)   (((id) >> 16) & 0xffff)
104
105 /*
106  * The rest of this file is specific to this particular implementation.
107  */
108
109 struct msgmap {
110         short   next;           /* next segment in buffer */
111                                 /* -1 -> available */
112                                 /* 0..(MSGSEG-1) -> index of next segment */
113 };
114
115 #define MSG_LOCKED      01000   /* Is this msqid_ds locked? */
116
117 static int nfree_msgmaps;       /* # of free map entries */
118 static short free_msgmaps;      /* head of linked list of free map entries */
119 static struct msg *free_msghdrs;/* list of free msg headers */
120 static char *msgpool;           /* MSGMAX byte long msg buffer pool */
121 static struct msgmap *msgmaps;  /* MSGSEG msgmap structures */
122 static struct msg *msghdrs;     /* MSGTQL msg headers */
123 static struct msqid_ds *msqids; /* MSGMNI msqid_ds struct's */
124
125 static void
126 msginit(dummy)
127         void *dummy;
128 {
129         register int i;
130
131         msgpool = malloc(msginfo.msgmax, M_MSG, M_WAITOK);
132         if (msgpool == NULL)
133                 panic("msgpool is NULL");
134         msgmaps = malloc(sizeof(struct msgmap) * msginfo.msgseg, M_MSG, M_WAITOK);
135         if (msgmaps == NULL)
136                 panic("msgmaps is NULL");
137         msghdrs = malloc(sizeof(struct msg) * msginfo.msgtql, M_MSG, M_WAITOK);
138         if (msghdrs == NULL)
139                 panic("msghdrs is NULL");
140         msqids = malloc(sizeof(struct msqid_ds) * msginfo.msgmni, M_MSG, M_WAITOK);
141         if (msqids == NULL)
142                 panic("msqids is NULL");
143
144         /*
145          * msginfo.msgssz should be a power of two for efficiency reasons.
146          * It is also pretty silly if msginfo.msgssz is less than 8
147          * or greater than about 256 so ...
148          */
149
150         i = 8;
151         while (i < 1024 && i != msginfo.msgssz)
152                 i <<= 1;
153         if (i != msginfo.msgssz) {
154                 printf("msginfo.msgssz=%d (0x%x)\n", msginfo.msgssz,
155                     msginfo.msgssz);
156                 panic("msginfo.msgssz not a small power of 2");
157         }
158
159         if (msginfo.msgseg > 32767) {
160                 printf("msginfo.msgseg=%d\n", msginfo.msgseg);
161                 panic("msginfo.msgseg > 32767");
162         }
163
164         if (msgmaps == NULL)
165                 panic("msgmaps is NULL");
166
167         for (i = 0; i < msginfo.msgseg; i++) {
168                 if (i > 0)
169                         msgmaps[i-1].next = i;
170                 msgmaps[i].next = -1;   /* implies entry is available */
171         }
172         free_msgmaps = 0;
173         nfree_msgmaps = msginfo.msgseg;
174
175         if (msghdrs == NULL)
176                 panic("msghdrs is NULL");
177
178         for (i = 0; i < msginfo.msgtql; i++) {
179                 msghdrs[i].msg_type = 0;
180                 if (i > 0)
181                         msghdrs[i-1].msg_next = &msghdrs[i];
182                 msghdrs[i].msg_next = NULL;
183         }
184         free_msghdrs = &msghdrs[0];
185
186         if (msqids == NULL)
187                 panic("msqids is NULL");
188
189         for (i = 0; i < msginfo.msgmni; i++) {
190                 msqids[i].msg_qbytes = 0;       /* implies entry is available */
191                 msqids[i].msg_perm.seq = 0;     /* reset to a known value */
192                 msqids[i].msg_perm.mode = 0;
193         }
194 }
195 SYSINIT(sysv_msg, SI_SUB_SYSV_MSG, SI_ORDER_FIRST, msginit, NULL)
196
197 /*
198  * Entry point for all MSG calls
199  */
200 int
201 msgsys(p, uap)
202         struct proc *p;
203         /* XXX actually varargs. */
204         struct msgsys_args /* {
205                 u_int   which;
206                 int     a2;
207                 int     a3;
208                 int     a4;
209                 int     a5;
210                 int     a6;
211         } */ *uap;
212 {
213
214         if (!jail_sysvipc_allowed && p->p_prison != NULL)
215                 return (ENOSYS);
216
217         if (uap->which >= sizeof(msgcalls)/sizeof(msgcalls[0]))
218                 return (EINVAL);
219         return ((*msgcalls[uap->which])(p, &uap->a2));
220 }
221
222 static void
223 msg_freehdr(msghdr)
224         struct msg *msghdr;
225 {
226         while (msghdr->msg_ts > 0) {
227                 short next;
228                 if (msghdr->msg_spot < 0 || msghdr->msg_spot >= msginfo.msgseg)
229                         panic("msghdr->msg_spot out of range");
230                 next = msgmaps[msghdr->msg_spot].next;
231                 msgmaps[msghdr->msg_spot].next = free_msgmaps;
232                 free_msgmaps = msghdr->msg_spot;
233                 nfree_msgmaps++;
234                 msghdr->msg_spot = next;
235                 if (msghdr->msg_ts >= msginfo.msgssz)
236                         msghdr->msg_ts -= msginfo.msgssz;
237                 else
238                         msghdr->msg_ts = 0;
239         }
240         if (msghdr->msg_spot != -1)
241                 panic("msghdr->msg_spot != -1");
242         msghdr->msg_next = free_msghdrs;
243         free_msghdrs = msghdr;
244 }
245
246 #ifndef _SYS_SYSPROTO_H_
247 struct msgctl_args {
248         int     msqid;
249         int     cmd;
250         struct  msqid_ds *buf;
251 };
252 #endif
253
254 int
255 msgctl(p, uap)
256         struct proc *p;
257         register struct msgctl_args *uap;
258 {
259         int msqid = uap->msqid;
260         int cmd = uap->cmd;
261         struct msqid_ds *user_msqptr = uap->buf;
262         int rval, eval;
263         struct msqid_ds msqbuf;
264         register struct msqid_ds *msqptr;
265
266 #ifdef MSG_DEBUG_OK
267         printf("call to msgctl(%d, %d, 0x%x)\n", msqid, cmd, user_msqptr);
268 #endif
269
270         if (!jail_sysvipc_allowed && p->p_prison != NULL)
271                 return (ENOSYS);
272
273         msqid = IPCID_TO_IX(msqid);
274
275         if (msqid < 0 || msqid >= msginfo.msgmni) {
276 #ifdef MSG_DEBUG_OK
277                 printf("msqid (%d) out of range (0<=msqid<%d)\n", msqid,
278                     msginfo.msgmni);
279 #endif
280                 return(EINVAL);
281         }
282
283         msqptr = &msqids[msqid];
284
285         if (msqptr->msg_qbytes == 0) {
286 #ifdef MSG_DEBUG_OK
287                 printf("no such msqid\n");
288 #endif
289                 return(EINVAL);
290         }
291         if (msqptr->msg_perm.seq != IPCID_TO_SEQ(uap->msqid)) {
292 #ifdef MSG_DEBUG_OK
293                 printf("wrong sequence number\n");
294 #endif
295                 return(EINVAL);
296         }
297
298         eval = 0;
299         rval = 0;
300
301         switch (cmd) {
302
303         case IPC_RMID:
304         {
305                 struct msg *msghdr;
306                 if ((eval = ipcperm(p, &msqptr->msg_perm, IPC_M)))
307                         return(eval);
308                 /* Free the message headers */
309                 msghdr = msqptr->msg_first;
310                 while (msghdr != NULL) {
311                         struct msg *msghdr_tmp;
312
313                         /* Free the segments of each message */
314                         msqptr->msg_cbytes -= msghdr->msg_ts;
315                         msqptr->msg_qnum--;
316                         msghdr_tmp = msghdr;
317                         msghdr = msghdr->msg_next;
318                         msg_freehdr(msghdr_tmp);
319                 }
320
321                 if (msqptr->msg_cbytes != 0)
322                         panic("msg_cbytes is screwed up");
323                 if (msqptr->msg_qnum != 0)
324                         panic("msg_qnum is screwed up");
325
326                 msqptr->msg_qbytes = 0; /* Mark it as free */
327
328                 wakeup((caddr_t)msqptr);
329         }
330
331                 break;
332
333         case IPC_SET:
334                 if ((eval = ipcperm(p, &msqptr->msg_perm, IPC_M)))
335                         return(eval);
336                 if ((eval = copyin(user_msqptr, &msqbuf, sizeof(msqbuf))) != 0)
337                         return(eval);
338                 if (msqbuf.msg_qbytes > msqptr->msg_qbytes) {
339                         eval = suser(p);
340                         if (eval)
341                                 return(eval);
342                 }
343                 if (msqbuf.msg_qbytes > msginfo.msgmnb) {
344 #ifdef MSG_DEBUG_OK
345                         printf("can't increase msg_qbytes beyond %d (truncating)\n",
346                             msginfo.msgmnb);
347 #endif
348                         msqbuf.msg_qbytes = msginfo.msgmnb;     /* silently restrict qbytes to system limit */
349                 }
350                 if (msqbuf.msg_qbytes == 0) {
351 #ifdef MSG_DEBUG_OK
352                         printf("can't reduce msg_qbytes to 0\n");
353 #endif
354                         return(EINVAL);         /* non-standard errno! */
355                 }
356                 msqptr->msg_perm.uid = msqbuf.msg_perm.uid;     /* change the owner */
357                 msqptr->msg_perm.gid = msqbuf.msg_perm.gid;     /* change the owner */
358                 msqptr->msg_perm.mode = (msqptr->msg_perm.mode & ~0777) |
359                     (msqbuf.msg_perm.mode & 0777);
360                 msqptr->msg_qbytes = msqbuf.msg_qbytes;
361                 msqptr->msg_ctime = time_second;
362                 break;
363
364         case IPC_STAT:
365                 if ((eval = ipcperm(p, &msqptr->msg_perm, IPC_R))) {
366 #ifdef MSG_DEBUG_OK
367                         printf("requester doesn't have read access\n");
368 #endif
369                         return(eval);
370                 }
371                 eval = copyout((caddr_t)msqptr, user_msqptr,
372                     sizeof(struct msqid_ds));
373                 break;
374
375         default:
376 #ifdef MSG_DEBUG_OK
377                 printf("invalid command %d\n", cmd);
378 #endif
379                 return(EINVAL);
380         }
381
382         if (eval == 0)
383                 p->p_retval[0] = rval;
384         return(eval);
385 }
386
387 #ifndef _SYS_SYSPROTO_H_
388 struct msgget_args {
389         key_t   key;
390         int     msgflg;
391 };
392 #endif
393
394 int
395 msgget(p, uap)
396         struct proc *p;
397         register struct msgget_args *uap;
398 {
399         int msqid, eval;
400         int key = uap->key;
401         int msgflg = uap->msgflg;
402         struct ucred *cred = p->p_ucred;
403         register struct msqid_ds *msqptr = NULL;
404
405 #ifdef MSG_DEBUG_OK
406         printf("msgget(0x%x, 0%o)\n", key, msgflg);
407 #endif
408
409         if (!jail_sysvipc_allowed && p->p_prison != NULL)
410                 return (ENOSYS);
411
412         if (key != IPC_PRIVATE) {
413                 for (msqid = 0; msqid < msginfo.msgmni; msqid++) {
414                         msqptr = &msqids[msqid];
415                         if (msqptr->msg_qbytes != 0 &&
416                             msqptr->msg_perm.key == key)
417                                 break;
418                 }
419                 if (msqid < msginfo.msgmni) {
420 #ifdef MSG_DEBUG_OK
421                         printf("found public key\n");
422 #endif
423                         if ((msgflg & IPC_CREAT) && (msgflg & IPC_EXCL)) {
424 #ifdef MSG_DEBUG_OK
425                                 printf("not exclusive\n");
426 #endif
427                                 return(EEXIST);
428                         }
429                         if ((eval = ipcperm(p, &msqptr->msg_perm, msgflg & 0700 ))) {
430 #ifdef MSG_DEBUG_OK
431                                 printf("requester doesn't have 0%o access\n",
432                                     msgflg & 0700);
433 #endif
434                                 return(eval);
435                         }
436                         goto found;
437                 }
438         }
439
440 #ifdef MSG_DEBUG_OK
441         printf("need to allocate the msqid_ds\n");
442 #endif
443         if (key == IPC_PRIVATE || (msgflg & IPC_CREAT)) {
444                 for (msqid = 0; msqid < msginfo.msgmni; msqid++) {
445                         /*
446                          * Look for an unallocated and unlocked msqid_ds.
447                          * msqid_ds's can be locked by msgsnd or msgrcv while
448                          * they are copying the message in/out.  We can't
449                          * re-use the entry until they release it.
450                          */
451                         msqptr = &msqids[msqid];
452                         if (msqptr->msg_qbytes == 0 &&
453                             (msqptr->msg_perm.mode & MSG_LOCKED) == 0)
454                                 break;
455                 }
456                 if (msqid == msginfo.msgmni) {
457 #ifdef MSG_DEBUG_OK
458                         printf("no more msqid_ds's available\n");
459 #endif
460                         return(ENOSPC);
461                 }
462 #ifdef MSG_DEBUG_OK
463                 printf("msqid %d is available\n", msqid);
464 #endif
465                 msqptr->msg_perm.key = key;
466                 msqptr->msg_perm.cuid = cred->cr_uid;
467                 msqptr->msg_perm.uid = cred->cr_uid;
468                 msqptr->msg_perm.cgid = cred->cr_gid;
469                 msqptr->msg_perm.gid = cred->cr_gid;
470                 msqptr->msg_perm.mode = (msgflg & 0777);
471                 /* Make sure that the returned msqid is unique */
472                 msqptr->msg_perm.seq++;
473                 msqptr->msg_first = NULL;
474                 msqptr->msg_last = NULL;
475                 msqptr->msg_cbytes = 0;
476                 msqptr->msg_qnum = 0;
477                 msqptr->msg_qbytes = msginfo.msgmnb;
478                 msqptr->msg_lspid = 0;
479                 msqptr->msg_lrpid = 0;
480                 msqptr->msg_stime = 0;
481                 msqptr->msg_rtime = 0;
482                 msqptr->msg_ctime = time_second;
483         } else {
484 #ifdef MSG_DEBUG_OK
485                 printf("didn't find it and wasn't asked to create it\n");
486 #endif
487                 return(ENOENT);
488         }
489
490 found:
491         /* Construct the unique msqid */
492         p->p_retval[0] = IXSEQ_TO_IPCID(msqid, msqptr->msg_perm);
493         return(0);
494 }
495
496 #ifndef _SYS_SYSPROTO_H_
497 struct msgsnd_args {
498         int     msqid;
499         void    *msgp;
500         size_t  msgsz;
501         int     msgflg;
502 };
503 #endif
504
505 int
506 msgsnd(p, uap)
507         struct proc *p;
508         register struct msgsnd_args *uap;
509 {
510         int msqid = uap->msqid;
511         void *user_msgp = uap->msgp;
512         size_t msgsz = uap->msgsz;
513         int msgflg = uap->msgflg;
514         int segs_needed, eval;
515         register struct msqid_ds *msqptr;
516         register struct msg *msghdr;
517         short next;
518
519 #ifdef MSG_DEBUG_OK
520         printf("call to msgsnd(%d, 0x%x, %d, %d)\n", msqid, user_msgp, msgsz,
521             msgflg);
522 #endif
523
524         if (!jail_sysvipc_allowed && p->p_prison != NULL)
525                 return (ENOSYS);
526
527         msqid = IPCID_TO_IX(msqid);
528
529         if (msqid < 0 || msqid >= msginfo.msgmni) {
530 #ifdef MSG_DEBUG_OK
531                 printf("msqid (%d) out of range (0<=msqid<%d)\n", msqid,
532                     msginfo.msgmni);
533 #endif
534                 return(EINVAL);
535         }
536
537         msqptr = &msqids[msqid];
538         if (msqptr->msg_qbytes == 0) {
539 #ifdef MSG_DEBUG_OK
540                 printf("no such message queue id\n");
541 #endif
542                 return(EINVAL);
543         }
544         if (msqptr->msg_perm.seq != IPCID_TO_SEQ(uap->msqid)) {
545 #ifdef MSG_DEBUG_OK
546                 printf("wrong sequence number\n");
547 #endif
548                 return(EINVAL);
549         }
550
551         if ((eval = ipcperm(p, &msqptr->msg_perm, IPC_W))) {
552 #ifdef MSG_DEBUG_OK
553                 printf("requester doesn't have write access\n");
554 #endif
555                 return(eval);
556         }
557
558         segs_needed = (msgsz + msginfo.msgssz - 1) / msginfo.msgssz;
559 #ifdef MSG_DEBUG_OK
560         printf("msgsz=%d, msgssz=%d, segs_needed=%d\n", msgsz, msginfo.msgssz,
561             segs_needed);
562 #endif
563         for (;;) {
564                 int need_more_resources = 0;
565
566                 /*
567                  * check msgsz
568                  * (inside this loop in case msg_qbytes changes while we sleep)
569                  */
570
571                 if (msgsz > msqptr->msg_qbytes) {
572 #ifdef MSG_DEBUG_OK
573                         printf("msgsz > msqptr->msg_qbytes\n");
574 #endif
575                         return(EINVAL);
576                 }
577
578                 if (msqptr->msg_perm.mode & MSG_LOCKED) {
579 #ifdef MSG_DEBUG_OK
580                         printf("msqid is locked\n");
581 #endif
582                         need_more_resources = 1;
583                 }
584                 if (msgsz + msqptr->msg_cbytes > msqptr->msg_qbytes) {
585 #ifdef MSG_DEBUG_OK
586                         printf("msgsz + msg_cbytes > msg_qbytes\n");
587 #endif
588                         need_more_resources = 1;
589                 }
590                 if (segs_needed > nfree_msgmaps) {
591 #ifdef MSG_DEBUG_OK
592                         printf("segs_needed > nfree_msgmaps\n");
593 #endif
594                         need_more_resources = 1;
595                 }
596                 if (free_msghdrs == NULL) {
597 #ifdef MSG_DEBUG_OK
598                         printf("no more msghdrs\n");
599 #endif
600                         need_more_resources = 1;
601                 }
602
603                 if (need_more_resources) {
604                         int we_own_it;
605
606                         if ((msgflg & IPC_NOWAIT) != 0) {
607 #ifdef MSG_DEBUG_OK
608                                 printf("need more resources but caller doesn't want to wait\n");
609 #endif
610                                 return(EAGAIN);
611                         }
612
613                         if ((msqptr->msg_perm.mode & MSG_LOCKED) != 0) {
614 #ifdef MSG_DEBUG_OK
615                                 printf("we don't own the msqid_ds\n");
616 #endif
617                                 we_own_it = 0;
618                         } else {
619                                 /* Force later arrivals to wait for our
620                                    request */
621 #ifdef MSG_DEBUG_OK
622                                 printf("we own the msqid_ds\n");
623 #endif
624                                 msqptr->msg_perm.mode |= MSG_LOCKED;
625                                 we_own_it = 1;
626                         }
627 #ifdef MSG_DEBUG_OK
628                         printf("goodnight\n");
629 #endif
630                         eval = tsleep((caddr_t)msqptr, (PZERO - 4) | PCATCH,
631                             "msgwait", 0);
632 #ifdef MSG_DEBUG_OK
633                         printf("good morning, eval=%d\n", eval);
634 #endif
635                         if (we_own_it)
636                                 msqptr->msg_perm.mode &= ~MSG_LOCKED;
637                         if (eval != 0) {
638 #ifdef MSG_DEBUG_OK
639                                 printf("msgsnd:  interrupted system call\n");
640 #endif
641                                 return(EINTR);
642                         }
643
644                         /*
645                          * Make sure that the msq queue still exists
646                          */
647
648                         if (msqptr->msg_qbytes == 0) {
649 #ifdef MSG_DEBUG_OK
650                                 printf("msqid deleted\n");
651 #endif
652                                 return(EIDRM);
653                         }
654
655                 } else {
656 #ifdef MSG_DEBUG_OK
657                         printf("got all the resources that we need\n");
658 #endif
659                         break;
660                 }
661         }
662
663         /*
664          * We have the resources that we need.
665          * Make sure!
666          */
667
668         if (msqptr->msg_perm.mode & MSG_LOCKED)
669                 panic("msg_perm.mode & MSG_LOCKED");
670         if (segs_needed > nfree_msgmaps)
671                 panic("segs_needed > nfree_msgmaps");
672         if (msgsz + msqptr->msg_cbytes > msqptr->msg_qbytes)
673                 panic("msgsz + msg_cbytes > msg_qbytes");
674         if (free_msghdrs == NULL)
675                 panic("no more msghdrs");
676
677         /*
678          * Re-lock the msqid_ds in case we page-fault when copying in the
679          * message
680          */
681
682         if ((msqptr->msg_perm.mode & MSG_LOCKED) != 0)
683                 panic("msqid_ds is already locked");
684         msqptr->msg_perm.mode |= MSG_LOCKED;
685
686         /*
687          * Allocate a message header
688          */
689
690         msghdr = free_msghdrs;
691         free_msghdrs = msghdr->msg_next;
692         msghdr->msg_spot = -1;
693         msghdr->msg_ts = msgsz;
694
695         /*
696          * Allocate space for the message
697          */
698
699         while (segs_needed > 0) {
700                 if (nfree_msgmaps <= 0)
701                         panic("not enough msgmaps");
702                 if (free_msgmaps == -1)
703                         panic("nil free_msgmaps");
704                 next = free_msgmaps;
705                 if (next <= -1)
706                         panic("next too low #1");
707                 if (next >= msginfo.msgseg)
708                         panic("next out of range #1");
709 #ifdef MSG_DEBUG_OK
710                 printf("allocating segment %d to message\n", next);
711 #endif
712                 free_msgmaps = msgmaps[next].next;
713                 nfree_msgmaps--;
714                 msgmaps[next].next = msghdr->msg_spot;
715                 msghdr->msg_spot = next;
716                 segs_needed--;
717         }
718
719         /*
720          * Copy in the message type
721          */
722
723         if ((eval = copyin(user_msgp, &msghdr->msg_type,
724             sizeof(msghdr->msg_type))) != 0) {
725 #ifdef MSG_DEBUG_OK
726                 printf("error %d copying the message type\n", eval);
727 #endif
728                 msg_freehdr(msghdr);
729                 msqptr->msg_perm.mode &= ~MSG_LOCKED;
730                 wakeup((caddr_t)msqptr);
731                 return(eval);
732         }
733         user_msgp = (char *)user_msgp + sizeof(msghdr->msg_type);
734
735         /*
736          * Validate the message type
737          */
738
739         if (msghdr->msg_type < 1) {
740                 msg_freehdr(msghdr);
741                 msqptr->msg_perm.mode &= ~MSG_LOCKED;
742                 wakeup((caddr_t)msqptr);
743 #ifdef MSG_DEBUG_OK
744                 printf("mtype (%d) < 1\n", msghdr->msg_type);
745 #endif
746                 return(EINVAL);
747         }
748
749         /*
750          * Copy in the message body
751          */
752
753         next = msghdr->msg_spot;
754         while (msgsz > 0) {
755                 size_t tlen;
756                 if (msgsz > msginfo.msgssz)
757                         tlen = msginfo.msgssz;
758                 else
759                         tlen = msgsz;
760                 if (next <= -1)
761                         panic("next too low #2");
762                 if (next >= msginfo.msgseg)
763                         panic("next out of range #2");
764                 if ((eval = copyin(user_msgp, &msgpool[next * msginfo.msgssz],
765                     tlen)) != 0) {
766 #ifdef MSG_DEBUG_OK
767                         printf("error %d copying in message segment\n", eval);
768 #endif
769                         msg_freehdr(msghdr);
770                         msqptr->msg_perm.mode &= ~MSG_LOCKED;
771                         wakeup((caddr_t)msqptr);
772                         return(eval);
773                 }
774                 msgsz -= tlen;
775                 user_msgp = (char *)user_msgp + tlen;
776                 next = msgmaps[next].next;
777         }
778         if (next != -1)
779                 panic("didn't use all the msg segments");
780
781         /*
782          * We've got the message.  Unlock the msqid_ds.
783          */
784
785         msqptr->msg_perm.mode &= ~MSG_LOCKED;
786
787         /*
788          * Make sure that the msqid_ds is still allocated.
789          */
790
791         if (msqptr->msg_qbytes == 0) {
792                 msg_freehdr(msghdr);
793                 wakeup((caddr_t)msqptr);
794                 return(EIDRM);
795         }
796
797         /*
798          * Put the message into the queue
799          */
800
801         if (msqptr->msg_first == NULL) {
802                 msqptr->msg_first = msghdr;
803                 msqptr->msg_last = msghdr;
804         } else {
805                 msqptr->msg_last->msg_next = msghdr;
806                 msqptr->msg_last = msghdr;
807         }
808         msqptr->msg_last->msg_next = NULL;
809
810         msqptr->msg_cbytes += msghdr->msg_ts;
811         msqptr->msg_qnum++;
812         msqptr->msg_lspid = p->p_pid;
813         msqptr->msg_stime = time_second;
814
815         wakeup((caddr_t)msqptr);
816         p->p_retval[0] = 0;
817         return(0);
818 }
819
820 #ifndef _SYS_SYSPROTO_H_
821 struct msgrcv_args {
822         int     msqid;
823         void    *msgp;
824         size_t  msgsz;
825         long    msgtyp;
826         int     msgflg;
827 };
828 #endif
829
830 int
831 msgrcv(p, uap)
832         struct proc *p;
833         register struct msgrcv_args *uap;
834 {
835         int msqid = uap->msqid;
836         void *user_msgp = uap->msgp;
837         size_t msgsz = uap->msgsz;
838         long msgtyp = uap->msgtyp;
839         int msgflg = uap->msgflg;
840         size_t len;
841         register struct msqid_ds *msqptr;
842         register struct msg *msghdr;
843         int eval;
844         short next;
845
846 #ifdef MSG_DEBUG_OK
847         printf("call to msgrcv(%d, 0x%x, %d, %ld, %d)\n", msqid, user_msgp,
848             msgsz, msgtyp, msgflg);
849 #endif
850
851         if (!jail_sysvipc_allowed && p->p_prison != NULL)
852                 return (ENOSYS);
853
854         msqid = IPCID_TO_IX(msqid);
855
856         if (msqid < 0 || msqid >= msginfo.msgmni) {
857 #ifdef MSG_DEBUG_OK
858                 printf("msqid (%d) out of range (0<=msqid<%d)\n", msqid,
859                     msginfo.msgmni);
860 #endif
861                 return(EINVAL);
862         }
863
864         msqptr = &msqids[msqid];
865         if (msqptr->msg_qbytes == 0) {
866 #ifdef MSG_DEBUG_OK
867                 printf("no such message queue id\n");
868 #endif
869                 return(EINVAL);
870         }
871         if (msqptr->msg_perm.seq != IPCID_TO_SEQ(uap->msqid)) {
872 #ifdef MSG_DEBUG_OK
873                 printf("wrong sequence number\n");
874 #endif
875                 return(EINVAL);
876         }
877
878         if ((eval = ipcperm(p, &msqptr->msg_perm, IPC_R))) {
879 #ifdef MSG_DEBUG_OK
880                 printf("requester doesn't have read access\n");
881 #endif
882                 return(eval);
883         }
884
885         msghdr = NULL;
886         while (msghdr == NULL) {
887                 if (msgtyp == 0) {
888                         msghdr = msqptr->msg_first;
889                         if (msghdr != NULL) {
890                                 if (msgsz < msghdr->msg_ts &&
891                                     (msgflg & MSG_NOERROR) == 0) {
892 #ifdef MSG_DEBUG_OK
893                                         printf("first message on the queue is too big (want %d, got %d)\n",
894                                             msgsz, msghdr->msg_ts);
895 #endif
896                                         return(E2BIG);
897                                 }
898                                 if (msqptr->msg_first == msqptr->msg_last) {
899                                         msqptr->msg_first = NULL;
900                                         msqptr->msg_last = NULL;
901                                 } else {
902                                         msqptr->msg_first = msghdr->msg_next;
903                                         if (msqptr->msg_first == NULL)
904                                                 panic("msg_first/last screwed up #1");
905                                 }
906                         }
907                 } else {
908                         struct msg *previous;
909                         struct msg **prev;
910
911                         previous = NULL;
912                         prev = &(msqptr->msg_first);
913                         while ((msghdr = *prev) != NULL) {
914                                 /*
915                                  * Is this message's type an exact match or is
916                                  * this message's type less than or equal to
917                                  * the absolute value of a negative msgtyp?
918                                  * Note that the second half of this test can
919                                  * NEVER be true if msgtyp is positive since
920                                  * msg_type is always positive!
921                                  */
922
923                                 if (msgtyp == msghdr->msg_type ||
924                                     msghdr->msg_type <= -msgtyp) {
925 #ifdef MSG_DEBUG_OK
926                                         printf("found message type %d, requested %d\n",
927                                             msghdr->msg_type, msgtyp);
928 #endif
929                                         if (msgsz < msghdr->msg_ts &&
930                                             (msgflg & MSG_NOERROR) == 0) {
931 #ifdef MSG_DEBUG_OK
932                                                 printf("requested message on the queue is too big (want %d, got %d)\n",
933                                                     msgsz, msghdr->msg_ts);
934 #endif
935                                                 return(E2BIG);
936                                         }
937                                         *prev = msghdr->msg_next;
938                                         if (msghdr == msqptr->msg_last) {
939                                                 if (previous == NULL) {
940                                                         if (prev !=
941                                                             &msqptr->msg_first)
942                                                                 panic("msg_first/last screwed up #2");
943                                                         msqptr->msg_first =
944                                                             NULL;
945                                                         msqptr->msg_last =
946                                                             NULL;
947                                                 } else {
948                                                         if (prev ==
949                                                             &msqptr->msg_first)
950                                                                 panic("msg_first/last screwed up #3");
951                                                         msqptr->msg_last =
952                                                             previous;
953                                                 }
954                                         }
955                                         break;
956                                 }
957                                 previous = msghdr;
958                                 prev = &(msghdr->msg_next);
959                         }
960                 }
961
962                 /*
963                  * We've either extracted the msghdr for the appropriate
964                  * message or there isn't one.
965                  * If there is one then bail out of this loop.
966                  */
967
968                 if (msghdr != NULL)
969                         break;
970
971                 /*
972                  * Hmph!  No message found.  Does the user want to wait?
973                  */
974
975                 if ((msgflg & IPC_NOWAIT) != 0) {
976 #ifdef MSG_DEBUG_OK
977                         printf("no appropriate message found (msgtyp=%d)\n",
978                             msgtyp);
979 #endif
980                         /* The SVID says to return ENOMSG. */
981 #ifdef ENOMSG
982                         return(ENOMSG);
983 #else
984                         /* Unfortunately, BSD doesn't define that code yet! */
985                         return(EAGAIN);
986 #endif
987                 }
988
989                 /*
990                  * Wait for something to happen
991                  */
992
993 #ifdef MSG_DEBUG_OK
994                 printf("msgrcv:  goodnight\n");
995 #endif
996                 eval = tsleep((caddr_t)msqptr, (PZERO - 4) | PCATCH, "msgwait",
997                     0);
998 #ifdef MSG_DEBUG_OK
999                 printf("msgrcv:  good morning (eval=%d)\n", eval);
1000 #endif
1001
1002                 if (eval != 0) {
1003 #ifdef MSG_DEBUG_OK
1004                         printf("msgsnd:  interrupted system call\n");
1005 #endif
1006                         return(EINTR);
1007                 }
1008
1009                 /*
1010                  * Make sure that the msq queue still exists
1011                  */
1012
1013                 if (msqptr->msg_qbytes == 0 ||
1014                     msqptr->msg_perm.seq != IPCID_TO_SEQ(uap->msqid)) {
1015 #ifdef MSG_DEBUG_OK
1016                         printf("msqid deleted\n");
1017 #endif
1018                         return(EIDRM);
1019                 }
1020         }
1021
1022         /*
1023          * Return the message to the user.
1024          *
1025          * First, do the bookkeeping (before we risk being interrupted).
1026          */
1027
1028         msqptr->msg_cbytes -= msghdr->msg_ts;
1029         msqptr->msg_qnum--;
1030         msqptr->msg_lrpid = p->p_pid;
1031         msqptr->msg_rtime = time_second;
1032
1033         /*
1034          * Make msgsz the actual amount that we'll be returning.
1035          * Note that this effectively truncates the message if it is too long
1036          * (since msgsz is never increased).
1037          */
1038
1039 #ifdef MSG_DEBUG_OK
1040         printf("found a message, msgsz=%d, msg_ts=%d\n", msgsz,
1041             msghdr->msg_ts);
1042 #endif
1043         if (msgsz > msghdr->msg_ts)
1044                 msgsz = msghdr->msg_ts;
1045
1046         /*
1047          * Return the type to the user.
1048          */
1049
1050         eval = copyout((caddr_t)&(msghdr->msg_type), user_msgp,
1051             sizeof(msghdr->msg_type));
1052         if (eval != 0) {
1053 #ifdef MSG_DEBUG_OK
1054                 printf("error (%d) copying out message type\n", eval);
1055 #endif
1056                 msg_freehdr(msghdr);
1057                 wakeup((caddr_t)msqptr);
1058                 return(eval);
1059         }
1060         user_msgp = (char *)user_msgp + sizeof(msghdr->msg_type);
1061
1062         /*
1063          * Return the segments to the user
1064          */
1065
1066         next = msghdr->msg_spot;
1067         for (len = 0; len < msgsz; len += msginfo.msgssz) {
1068                 size_t tlen;
1069
1070                 if (msgsz - len > msginfo.msgssz)
1071                         tlen = msginfo.msgssz;
1072                 else
1073                         tlen = msgsz - len;
1074                 if (next <= -1)
1075                         panic("next too low #3");
1076                 if (next >= msginfo.msgseg)
1077                         panic("next out of range #3");
1078                 eval = copyout((caddr_t)&msgpool[next * msginfo.msgssz],
1079                     user_msgp, tlen);
1080                 if (eval != 0) {
1081 #ifdef MSG_DEBUG_OK
1082                         printf("error (%d) copying out message segment\n",
1083                             eval);
1084 #endif
1085                         msg_freehdr(msghdr);
1086                         wakeup((caddr_t)msqptr);
1087                         return(eval);
1088                 }
1089                 user_msgp = (char *)user_msgp + tlen;
1090                 next = msgmaps[next].next;
1091         }
1092
1093         /*
1094          * Done, return the actual number of bytes copied out.
1095          */
1096
1097         msg_freehdr(msghdr);
1098         wakeup((caddr_t)msqptr);
1099         p->p_retval[0] = msgsz;
1100         return(0);
1101 }