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