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