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