369e05f7185ec7ab932bb9b7dc65ae6406059674
[dragonfly.git] / sys / kern / sysv_msg.c
1 /* $FreeBSD: src/sys/kern/sysv_msg.c,v 1.23.2.5 2002/12/31 08:54:53 maxim Exp $ */
2
3 /*
4  * Implementation of SVID messages
5  *
6  * Author:  Daniel Boulet
7  *
8  * Copyright 1993 Daniel Boulet and RTMX Inc.
9  *
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/priv.h>
30 #include <sys/msg.h>
31 #include <sys/sysent.h>
32 #include <sys/sysctl.h>
33 #include <sys/malloc.h>
34 #include <sys/jail.h>
35
36 #include <sys/mplock2.h>
37
38 static MALLOC_DEFINE(M_MSG, "msg", "SVID compatible message queues");
39
40 static void msginit (void *);
41
42 #define MSG_DEBUG
43 #undef MSG_DEBUG_OK
44
45 static void msg_freehdr (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 *)sys_msgctl, (sy_call_t *)sys_msgget,
50         (sy_call_t *)sys_msgsnd, (sy_call_t *)sys_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(void *dummy)
130 {
131         int i;
132
133         msginfo.msgmax = msginfo.msgseg * msginfo.msgssz;
134         msgpool = kmalloc(msginfo.msgmax, M_MSG, M_WAITOK);
135         msgmaps = kmalloc(sizeof(struct msgmap) * msginfo.msgseg, M_MSG, M_WAITOK);
136         msghdrs = kmalloc(sizeof(struct msg) * msginfo.msgtql, M_MSG, M_WAITOK);
137         msqids = kmalloc(sizeof(struct msqid_ds) * msginfo.msgmni, M_MSG, M_WAITOK);
138
139         /*
140          * msginfo.msgssz should be a power of two for efficiency reasons.
141          * It is also pretty silly if msginfo.msgssz is less than 8
142          * or greater than about 256 so ...
143          */
144
145         i = 8;
146         while (i < 1024 && i != msginfo.msgssz)
147                 i <<= 1;
148         if (i != msginfo.msgssz) {
149                 kprintf("msginfo.msgssz=%d (0x%x)\n", msginfo.msgssz,
150                     msginfo.msgssz);
151                 panic("msginfo.msgssz not a small power of 2");
152         }
153
154         if (msginfo.msgseg > 32767) {
155                 kprintf("msginfo.msgseg=%d\n", msginfo.msgseg);
156                 panic("msginfo.msgseg > 32767");
157         }
158
159         if (msgmaps == NULL)
160                 panic("msgmaps is NULL");
161
162         for (i = 0; i < msginfo.msgseg; i++) {
163                 if (i > 0)
164                         msgmaps[i-1].next = i;
165                 msgmaps[i].next = -1;   /* implies entry is available */
166         }
167         free_msgmaps = 0;
168         nfree_msgmaps = msginfo.msgseg;
169
170         if (msghdrs == NULL)
171                 panic("msghdrs is NULL");
172
173         for (i = 0; i < msginfo.msgtql; i++) {
174                 msghdrs[i].msg_type = 0;
175                 if (i > 0)
176                         msghdrs[i-1].msg_next = &msghdrs[i];
177                 msghdrs[i].msg_next = NULL;
178         }
179         free_msghdrs = &msghdrs[0];
180
181         if (msqids == NULL)
182                 panic("msqids is NULL");
183
184         for (i = 0; i < msginfo.msgmni; i++) {
185                 msqids[i].msg_qbytes = 0;       /* implies entry is available */
186                 msqids[i].msg_perm.seq = 0;     /* reset to a known value */
187                 msqids[i].msg_perm.mode = 0;
188         }
189 }
190 SYSINIT(sysv_msg, SI_SUB_SYSV_MSG, SI_ORDER_FIRST, msginit, NULL)
191
192 /*
193  * Entry point for all MSG calls
194  *
195  * msgsys_args(int which, int a2, ...) (VARARGS)
196  *
197  * MPALMOSTSAFE
198  */
199 int
200 sys_msgsys(struct msgsys_args *uap)
201 {
202         struct thread *td = curthread;
203         unsigned int which = (unsigned int)uap->which;
204         int error;
205
206         if (!jail_sysvipc_allowed && td->td_ucred->cr_prison != NULL)
207                 return (ENOSYS);
208
209         if (which >= NELEM(msgcalls))
210                 return (EINVAL);
211         bcopy(&uap->a2, &uap->which,
212               sizeof(struct msgsys_args) - offsetof(struct msgsys_args, a2));
213         get_mplock();
214         error = (*msgcalls[which])(uap);
215         rel_mplock();
216         return (error);
217 }
218
219 static void
220 msg_freehdr(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 /*
243  * MPALMOSTSAFE
244  */
245 int
246 sys_msgctl(struct msgctl_args *uap)
247 {
248         struct thread *td = curthread;
249         struct proc *p = td->td_proc;
250         int msqid = uap->msqid;
251         int cmd = uap->cmd;
252         struct msqid_ds *user_msqptr = uap->buf;
253         int rval, eval;
254         struct msqid_ds msqbuf;
255         struct msqid_ds *msqptr;
256
257 #ifdef MSG_DEBUG_OK
258         kprintf("call to msgctl(%d, %d, 0x%x)\n", msqid, cmd, user_msqptr);
259 #endif
260
261         if (!jail_sysvipc_allowed && td->td_ucred->cr_prison != NULL)
262                 return (ENOSYS);
263
264         get_mplock();
265         msqid = IPCID_TO_IX(msqid);
266
267         if (msqid < 0 || msqid >= msginfo.msgmni) {
268 #ifdef MSG_DEBUG_OK
269                 kprintf("msqid (%d) out of range (0<=msqid<%d)\n", msqid,
270                     msginfo.msgmni);
271 #endif
272                 eval = EINVAL;
273                 goto done;
274         }
275
276         msqptr = &msqids[msqid];
277
278         if (msqptr->msg_qbytes == 0) {
279 #ifdef MSG_DEBUG_OK
280                 kprintf("no such msqid\n");
281 #endif
282                 eval = EINVAL;
283                 goto done;
284         }
285         if (msqptr->msg_perm.seq != IPCID_TO_SEQ(uap->msqid)) {
286 #ifdef MSG_DEBUG_OK
287                 kprintf("wrong sequence number\n");
288 #endif
289                 eval = EINVAL;
290                 goto done;
291         }
292
293         rval = 0;
294
295         switch (cmd) {
296         case IPC_RMID:
297         {
298                 struct msg *msghdr;
299                 if ((eval = ipcperm(p, &msqptr->msg_perm, IPC_M)) != 0)
300                         break;
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)) != 0)
328                         break;
329                 if ((eval = copyin(user_msqptr, &msqbuf, sizeof(msqbuf))) != 0)
330                         break;
331                 if (msqbuf.msg_qbytes > msqptr->msg_qbytes) {
332                         eval = priv_check(td, PRIV_ROOT);
333                         if (eval)
334                                 break;
335                 }
336                 if (msqbuf.msg_qbytes > msginfo.msgmnb) {
337 #ifdef MSG_DEBUG_OK
338                         kprintf("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                         kprintf("can't reduce msg_qbytes to 0\n");
346 #endif
347                         eval = EINVAL;          /* non-standard errno! */
348                         break;
349                 }
350                 msqptr->msg_perm.uid = msqbuf.msg_perm.uid;     /* change the owner */
351                 msqptr->msg_perm.gid = msqbuf.msg_perm.gid;     /* change the owner */
352                 msqptr->msg_perm.mode = (msqptr->msg_perm.mode & ~0777) |
353                                         (msqbuf.msg_perm.mode & 0777);
354                 msqptr->msg_qbytes = msqbuf.msg_qbytes;
355                 msqptr->msg_ctime = time_second;
356                 break;
357
358         case IPC_STAT:
359                 if ((eval = ipcperm(p, &msqptr->msg_perm, IPC_R))) {
360 #ifdef MSG_DEBUG_OK
361                         kprintf("requester doesn't have read access\n");
362 #endif
363                         eval = EINVAL;
364                         break;
365                 }
366                 eval = copyout(msqptr, user_msqptr, sizeof(struct msqid_ds));
367                 break;
368
369         default:
370 #ifdef MSG_DEBUG_OK
371                 kprintf("invalid command %d\n", cmd);
372 #endif
373                 eval = EINVAL;
374                 break;
375         }
376 done:
377         rel_mplock();
378         if (eval == 0)
379                 uap->sysmsg_result = rval;
380         return(eval);
381 }
382
383 /*
384  * MPALMOSTSAFE
385  */
386 int
387 sys_msgget(struct msgget_args *uap)
388 {
389         struct thread *td = curthread;
390         int msqid, eval;
391         int key = uap->key;
392         int msgflg = uap->msgflg;
393         struct ucred *cred = td->td_ucred;
394         struct msqid_ds *msqptr = NULL;
395
396 #ifdef MSG_DEBUG_OK
397         kprintf("msgget(0x%x, 0%o)\n", key, msgflg);
398 #endif
399         if (!jail_sysvipc_allowed && cred->cr_prison != NULL)
400                 return (ENOSYS);
401
402         eval = 0;
403         get_mplock();
404
405         if (key != IPC_PRIVATE) {
406                 for (msqid = 0; msqid < msginfo.msgmni; msqid++) {
407                         msqptr = &msqids[msqid];
408                         if (msqptr->msg_qbytes != 0 &&
409                             msqptr->msg_perm.key == key)
410                                 break;
411                 }
412                 if (msqid < msginfo.msgmni) {
413 #ifdef MSG_DEBUG_OK
414                         kprintf("found public key\n");
415 #endif
416                         if ((msgflg & IPC_CREAT) && (msgflg & IPC_EXCL)) {
417 #ifdef MSG_DEBUG_OK
418                                 kprintf("not exclusive\n");
419 #endif
420                                 eval = EEXIST;
421                                 goto done;
422                         }
423                         if ((eval = ipcperm(td->td_proc, &msqptr->msg_perm, msgflg & 0700 ))) {
424 #ifdef MSG_DEBUG_OK
425                                 kprintf("requester doesn't have 0%o access\n",
426                                     msgflg & 0700);
427 #endif
428                                 goto done;
429                         }
430                         goto done;
431                 }
432         }
433
434 #ifdef MSG_DEBUG_OK
435         kprintf("need to allocate the msqid_ds\n");
436 #endif
437         if (key == IPC_PRIVATE || (msgflg & IPC_CREAT)) {
438                 for (msqid = 0; msqid < msginfo.msgmni; msqid++) {
439                         /*
440                          * Look for an unallocated and unlocked msqid_ds.
441                          * msqid_ds's can be locked by msgsnd or msgrcv while
442                          * they are copying the message in/out.  We can't
443                          * re-use the entry until they release it.
444                          */
445                         msqptr = &msqids[msqid];
446                         if (msqptr->msg_qbytes == 0 &&
447                             (msqptr->msg_perm.mode & MSG_LOCKED) == 0)
448                                 break;
449                 }
450                 if (msqid == msginfo.msgmni) {
451 #ifdef MSG_DEBUG_OK
452                         kprintf("no more msqid_ds's available\n");
453 #endif
454                         eval = ENOSPC;
455                         goto done;
456                 }
457 #ifdef MSG_DEBUG_OK
458                 kprintf("msqid %d is available\n", msqid);
459 #endif
460                 msqptr->msg_perm.key = key;
461                 msqptr->msg_perm.cuid = cred->cr_uid;
462                 msqptr->msg_perm.uid = cred->cr_uid;
463                 msqptr->msg_perm.cgid = cred->cr_gid;
464                 msqptr->msg_perm.gid = cred->cr_gid;
465                 msqptr->msg_perm.mode = (msgflg & 0777);
466                 /* Make sure that the returned msqid is unique */
467                 msqptr->msg_perm.seq = (msqptr->msg_perm.seq + 1) & 0x7fff;
468                 msqptr->msg_first = NULL;
469                 msqptr->msg_last = NULL;
470                 msqptr->msg_cbytes = 0;
471                 msqptr->msg_qnum = 0;
472                 msqptr->msg_qbytes = msginfo.msgmnb;
473                 msqptr->msg_lspid = 0;
474                 msqptr->msg_lrpid = 0;
475                 msqptr->msg_stime = 0;
476                 msqptr->msg_rtime = 0;
477                 msqptr->msg_ctime = time_second;
478         } else {
479 #ifdef MSG_DEBUG_OK
480                 kprintf("didn't find it and wasn't asked to create it\n");
481 #endif
482                 eval = ENOENT;
483         }
484
485 done:
486         rel_mplock();
487         /* Construct the unique msqid */
488         if (eval == 0)
489                 uap->sysmsg_result = IXSEQ_TO_IPCID(msqid, msqptr->msg_perm);
490         return(eval);
491 }
492
493 /*
494  * MPALMOSTSAFE
495  */
496 int
497 sys_msgsnd(struct msgsnd_args *uap)
498 {
499         struct thread *td = curthread;
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         struct msqid_ds *msqptr;
506         struct msg *msghdr;
507         short next;
508
509 #ifdef MSG_DEBUG_OK
510         kprintf("call to msgsnd(%d, 0x%x, %d, %d)\n", msqid, user_msgp, msgsz,
511             msgflg);
512 #endif
513
514         if (!jail_sysvipc_allowed && td->td_ucred->cr_prison != NULL)
515                 return (ENOSYS);
516
517         get_mplock();
518         msqid = IPCID_TO_IX(msqid);
519
520         if (msqid < 0 || msqid >= msginfo.msgmni) {
521 #ifdef MSG_DEBUG_OK
522                 kprintf("msqid (%d) out of range (0<=msqid<%d)\n", msqid,
523                     msginfo.msgmni);
524 #endif
525                 eval = EINVAL;
526                 goto done;
527         }
528
529         msqptr = &msqids[msqid];
530         if (msqptr->msg_qbytes == 0) {
531 #ifdef MSG_DEBUG_OK
532                 kprintf("no such message queue id\n");
533 #endif
534                 eval = EINVAL;
535                 goto done;
536         }
537         if (msqptr->msg_perm.seq != IPCID_TO_SEQ(uap->msqid)) {
538 #ifdef MSG_DEBUG_OK
539                 kprintf("wrong sequence number\n");
540 #endif
541                 eval = EINVAL;
542                 goto done;
543         }
544
545         if ((eval = ipcperm(td->td_proc, &msqptr->msg_perm, IPC_W))) {
546 #ifdef MSG_DEBUG_OK
547                 kprintf("requester doesn't have write access\n");
548 #endif
549                 eval = EINVAL;
550                 goto done;
551         }
552
553         segs_needed = (msgsz + msginfo.msgssz - 1) / msginfo.msgssz;
554 #ifdef MSG_DEBUG_OK
555         kprintf("msgsz=%d, msgssz=%d, segs_needed=%d\n", msgsz, msginfo.msgssz,
556             segs_needed);
557 #endif
558         for (;;) {
559                 int need_more_resources = 0;
560
561                 /*
562                  * check msgsz
563                  * (inside this loop in case msg_qbytes changes while we sleep)
564                  */
565
566                 if (msgsz > msqptr->msg_qbytes) {
567 #ifdef MSG_DEBUG_OK
568                         kprintf("msgsz > msqptr->msg_qbytes\n");
569 #endif
570                         eval = EINVAL;
571                         goto done;
572                 }
573
574                 if (msqptr->msg_perm.mode & MSG_LOCKED) {
575 #ifdef MSG_DEBUG_OK
576                         kprintf("msqid is locked\n");
577 #endif
578                         need_more_resources = 1;
579                 }
580                 if (msgsz + msqptr->msg_cbytes > msqptr->msg_qbytes) {
581 #ifdef MSG_DEBUG_OK
582                         kprintf("msgsz + msg_cbytes > msg_qbytes\n");
583 #endif
584                         need_more_resources = 1;
585                 }
586                 if (segs_needed > nfree_msgmaps) {
587 #ifdef MSG_DEBUG_OK
588                         kprintf("segs_needed > nfree_msgmaps\n");
589 #endif
590                         need_more_resources = 1;
591                 }
592                 if (free_msghdrs == NULL) {
593 #ifdef MSG_DEBUG_OK
594                         kprintf("no more msghdrs\n");
595 #endif
596                         need_more_resources = 1;
597                 }
598
599                 if (need_more_resources) {
600                         int we_own_it;
601
602                         if ((msgflg & IPC_NOWAIT) != 0) {
603 #ifdef MSG_DEBUG_OK
604                                 kprintf("need more resources but caller doesn't want to wait\n");
605 #endif
606                                 eval = EAGAIN;
607                                 goto done;
608                         }
609
610                         if ((msqptr->msg_perm.mode & MSG_LOCKED) != 0) {
611 #ifdef MSG_DEBUG_OK
612                                 kprintf("we don't own the msqid_ds\n");
613 #endif
614                                 we_own_it = 0;
615                         } else {
616                                 /* Force later arrivals to wait for our
617                                    request */
618 #ifdef MSG_DEBUG_OK
619                                 kprintf("we own the msqid_ds\n");
620 #endif
621                                 msqptr->msg_perm.mode |= MSG_LOCKED;
622                                 we_own_it = 1;
623                         }
624 #ifdef MSG_DEBUG_OK
625                         kprintf("goodnight\n");
626 #endif
627                         eval = tsleep((caddr_t)msqptr, PCATCH, "msgwait", 0);
628 #ifdef MSG_DEBUG_OK
629                         kprintf("good morning, eval=%d\n", eval);
630 #endif
631                         if (we_own_it)
632                                 msqptr->msg_perm.mode &= ~MSG_LOCKED;
633                         if (eval != 0) {
634 #ifdef MSG_DEBUG_OK
635                                 kprintf("msgsnd:  interrupted system call\n");
636 #endif
637                                 eval = EINTR;
638                                 goto done;
639                         }
640
641                         /*
642                          * Make sure that the msq queue still exists
643                          */
644
645                         if (msqptr->msg_qbytes == 0) {
646 #ifdef MSG_DEBUG_OK
647                                 kprintf("msqid deleted\n");
648 #endif
649                                 eval = EIDRM;
650                                 goto done;
651                         }
652
653                 } else {
654 #ifdef MSG_DEBUG_OK
655                         kprintf("got all the resources that we need\n");
656 #endif
657                         break;
658                 }
659         }
660
661         /*
662          * We have the resources that we need.
663          * Make sure!
664          */
665
666         if (msqptr->msg_perm.mode & MSG_LOCKED)
667                 panic("msg_perm.mode & MSG_LOCKED");
668         if (segs_needed > nfree_msgmaps)
669                 panic("segs_needed > nfree_msgmaps");
670         if (msgsz + msqptr->msg_cbytes > msqptr->msg_qbytes)
671                 panic("msgsz + msg_cbytes > msg_qbytes");
672         if (free_msghdrs == NULL)
673                 panic("no more msghdrs");
674
675         /*
676          * Re-lock the msqid_ds in case we page-fault when copying in the
677          * message
678          */
679
680         if ((msqptr->msg_perm.mode & MSG_LOCKED) != 0)
681                 panic("msqid_ds is already locked");
682         msqptr->msg_perm.mode |= MSG_LOCKED;
683
684         /*
685          * Allocate a message header
686          */
687
688         msghdr = free_msghdrs;
689         free_msghdrs = msghdr->msg_next;
690         msghdr->msg_spot = -1;
691         msghdr->msg_ts = msgsz;
692
693         /*
694          * Allocate space for the message
695          */
696
697         while (segs_needed > 0) {
698                 if (nfree_msgmaps <= 0)
699                         panic("not enough msgmaps");
700                 if (free_msgmaps == -1)
701                         panic("nil free_msgmaps");
702                 next = free_msgmaps;
703                 if (next <= -1)
704                         panic("next too low #1");
705                 if (next >= msginfo.msgseg)
706                         panic("next out of range #1");
707 #ifdef MSG_DEBUG_OK
708                 kprintf("allocating segment %d to message\n", next);
709 #endif
710                 free_msgmaps = msgmaps[next].next;
711                 nfree_msgmaps--;
712                 msgmaps[next].next = msghdr->msg_spot;
713                 msghdr->msg_spot = next;
714                 segs_needed--;
715         }
716
717         /*
718          * Copy in the message type
719          */
720
721         if ((eval = copyin(user_msgp, &msghdr->msg_type,
722             sizeof(msghdr->msg_type))) != 0) {
723 #ifdef MSG_DEBUG_OK
724                 kprintf("error %d copying the message type\n", eval);
725 #endif
726                 msg_freehdr(msghdr);
727                 msqptr->msg_perm.mode &= ~MSG_LOCKED;
728                 wakeup((caddr_t)msqptr);
729                 goto done;
730         }
731         user_msgp = (char *)user_msgp + sizeof(msghdr->msg_type);
732
733         /*
734          * Validate the message type
735          */
736
737         if (msghdr->msg_type < 1) {
738                 msg_freehdr(msghdr);
739                 msqptr->msg_perm.mode &= ~MSG_LOCKED;
740                 wakeup((caddr_t)msqptr);
741 #ifdef MSG_DEBUG_OK
742                 kprintf("mtype (%d) < 1\n", msghdr->msg_type);
743 #endif
744                 eval = EINVAL;
745                 goto done;
746         }
747
748         /*
749          * Copy in the message body
750          */
751
752         next = msghdr->msg_spot;
753         while (msgsz > 0) {
754                 size_t tlen;
755                 if (msgsz > msginfo.msgssz)
756                         tlen = msginfo.msgssz;
757                 else
758                         tlen = msgsz;
759                 if (next <= -1)
760                         panic("next too low #2");
761                 if (next >= msginfo.msgseg)
762                         panic("next out of range #2");
763                 if ((eval = copyin(user_msgp, &msgpool[next * msginfo.msgssz],
764                     tlen)) != 0) {
765 #ifdef MSG_DEBUG_OK
766                         kprintf("error %d copying in message segment\n", eval);
767 #endif
768                         msg_freehdr(msghdr);
769                         msqptr->msg_perm.mode &= ~MSG_LOCKED;
770                         wakeup((caddr_t)msqptr);
771                         goto done;
772                 }
773                 msgsz -= tlen;
774                 user_msgp = (char *)user_msgp + tlen;
775                 next = msgmaps[next].next;
776         }
777         if (next != -1)
778                 panic("didn't use all the msg segments");
779
780         /*
781          * We've got the message.  Unlock the msqid_ds.
782          */
783
784         msqptr->msg_perm.mode &= ~MSG_LOCKED;
785
786         /*
787          * Make sure that the msqid_ds is still allocated.
788          */
789
790         if (msqptr->msg_qbytes == 0) {
791                 msg_freehdr(msghdr);
792                 wakeup((caddr_t)msqptr);
793                 eval = EIDRM;
794                 goto done;
795         }
796
797         /*
798          * Put the message into the queue
799          */
800
801         if (msqptr->msg_first == NULL) {
802                 msqptr->msg_first = msghdr;
803                 msqptr->msg_last = msghdr;
804         } else {
805                 msqptr->msg_last->msg_next = msghdr;
806                 msqptr->msg_last = msghdr;
807         }
808         msqptr->msg_last->msg_next = NULL;
809
810         msqptr->msg_cbytes += msghdr->msg_ts;
811         msqptr->msg_qnum++;
812         msqptr->msg_lspid = td->td_proc->p_pid;
813         msqptr->msg_stime = time_second;
814
815         wakeup((caddr_t)msqptr);
816         eval = 0;
817 done:
818         rel_mplock();
819         if (eval == 0)
820                 uap->sysmsg_result = 0;
821         return (eval);
822 }
823
824 /*
825  * MPALMOSTSAFE
826  */
827 int
828 sys_msgrcv(struct msgrcv_args *uap)
829 {
830         struct thread *td = curthread;
831         int msqid = uap->msqid;
832         void *user_msgp = uap->msgp;
833         size_t msgsz = uap->msgsz;
834         long msgtyp = uap->msgtyp;
835         int msgflg = uap->msgflg;
836         size_t len;
837         struct msqid_ds *msqptr;
838         struct msg *msghdr;
839         int eval;
840         short next;
841
842 #ifdef MSG_DEBUG_OK
843         kprintf("call to msgrcv(%d, 0x%x, %d, %ld, %d)\n", msqid, user_msgp,
844             msgsz, msgtyp, msgflg);
845 #endif
846
847         if (!jail_sysvipc_allowed && td->td_ucred->cr_prison != NULL)
848                 return (ENOSYS);
849
850         get_mplock();
851         msqid = IPCID_TO_IX(msqid);
852
853         if (msqid < 0 || msqid >= msginfo.msgmni) {
854 #ifdef MSG_DEBUG_OK
855                 kprintf("msqid (%d) out of range (0<=msqid<%d)\n", msqid,
856                     msginfo.msgmni);
857 #endif
858                 eval = EINVAL;
859                 goto done;
860         }
861
862         msqptr = &msqids[msqid];
863         if (msqptr->msg_qbytes == 0) {
864 #ifdef MSG_DEBUG_OK
865                 kprintf("no such message queue id\n");
866 #endif
867                 eval = EINVAL;
868                 goto done;
869         }
870         if (msqptr->msg_perm.seq != IPCID_TO_SEQ(uap->msqid)) {
871 #ifdef MSG_DEBUG_OK
872                 kprintf("wrong sequence number\n");
873 #endif
874                 eval = EINVAL;
875                 goto done;
876         }
877
878         if ((eval = ipcperm(td->td_proc, &msqptr->msg_perm, IPC_R))) {
879 #ifdef MSG_DEBUG_OK
880                 kprintf("requester doesn't have read access\n");
881 #endif
882                 goto done;
883         }
884
885         msghdr = NULL;
886         while (msghdr == NULL) {
887                 if (msgtyp == 0) {
888                         msghdr = msqptr->msg_first;
889                         if (msghdr != NULL) {
890                                 if (msgsz < msghdr->msg_ts &&
891                                     (msgflg & MSG_NOERROR) == 0) {
892 #ifdef MSG_DEBUG_OK
893                                         kprintf("first message on the queue is too big (want %d, got %d)\n",
894                                             msgsz, msghdr->msg_ts);
895 #endif
896                                         eval = E2BIG;
897                                         goto done;
898                                 }
899                                 if (msqptr->msg_first == msqptr->msg_last) {
900                                         msqptr->msg_first = NULL;
901                                         msqptr->msg_last = NULL;
902                                 } else {
903                                         msqptr->msg_first = msghdr->msg_next;
904                                         if (msqptr->msg_first == NULL)
905                                                 panic("msg_first/last screwed up #1");
906                                 }
907                         }
908                 } else {
909                         struct msg *previous;
910                         struct msg **prev;
911
912                         previous = NULL;
913                         prev = &(msqptr->msg_first);
914                         while ((msghdr = *prev) != NULL) {
915                                 /*
916                                  * Is this message's type an exact match or is
917                                  * this message's type less than or equal to
918                                  * the absolute value of a negative msgtyp?
919                                  * Note that the second half of this test can
920                                  * NEVER be true if msgtyp is positive since
921                                  * msg_type is always positive!
922                                  */
923
924                                 if (msgtyp == msghdr->msg_type ||
925                                     msghdr->msg_type <= -msgtyp) {
926 #ifdef MSG_DEBUG_OK
927                                         kprintf("found message type %d, requested %d\n",
928                                             msghdr->msg_type, msgtyp);
929 #endif
930                                         if (msgsz < msghdr->msg_ts &&
931                                             (msgflg & MSG_NOERROR) == 0) {
932 #ifdef MSG_DEBUG_OK
933                                                 kprintf("requested message on the queue is too big (want %d, got %d)\n",
934                                                     msgsz, msghdr->msg_ts);
935 #endif
936                                                 eval = E2BIG;
937                                                 goto done;
938                                         }
939                                         *prev = msghdr->msg_next;
940                                         if (msghdr == msqptr->msg_last) {
941                                                 if (previous == NULL) {
942                                                         if (prev !=
943                                                             &msqptr->msg_first)
944                                                                 panic("msg_first/last screwed up #2");
945                                                         msqptr->msg_first =
946                                                             NULL;
947                                                         msqptr->msg_last =
948                                                             NULL;
949                                                 } else {
950                                                         if (prev ==
951                                                             &msqptr->msg_first)
952                                                                 panic("msg_first/last screwed up #3");
953                                                         msqptr->msg_last =
954                                                             previous;
955                                                 }
956                                         }
957                                         break;
958                                 }
959                                 previous = msghdr;
960                                 prev = &(msghdr->msg_next);
961                         }
962                 }
963
964                 /*
965                  * We've either extracted the msghdr for the appropriate
966                  * message or there isn't one.
967                  * If there is one then bail out of this loop.
968                  */
969
970                 if (msghdr != NULL)
971                         break;
972
973                 /*
974                  * Hmph!  No message found.  Does the user want to wait?
975                  */
976
977                 if ((msgflg & IPC_NOWAIT) != 0) {
978 #ifdef MSG_DEBUG_OK
979                         kprintf("no appropriate message found (msgtyp=%d)\n",
980                             msgtyp);
981 #endif
982                         /* The SVID says to return ENOMSG. */
983 #ifdef ENOMSG
984                         eval = ENOMSG;
985 #else
986                         /* Unfortunately, BSD doesn't define that code yet! */
987                         eval = EAGAIN;
988 #endif
989                         goto done;
990                 }
991
992                 /*
993                  * Wait for something to happen
994                  */
995
996 #ifdef MSG_DEBUG_OK
997                 kprintf("msgrcv:  goodnight\n");
998 #endif
999                 eval = tsleep((caddr_t)msqptr, PCATCH, "msgwait", 0);
1000 #ifdef MSG_DEBUG_OK
1001                 kprintf("msgrcv:  good morning (eval=%d)\n", eval);
1002 #endif
1003
1004                 if (eval != 0) {
1005 #ifdef MSG_DEBUG_OK
1006                         kprintf("msgsnd:  interrupted system call\n");
1007 #endif
1008                         eval = EINTR;
1009                         goto done;
1010                 }
1011
1012                 /*
1013                  * Make sure that the msq queue still exists
1014                  */
1015
1016                 if (msqptr->msg_qbytes == 0 ||
1017                     msqptr->msg_perm.seq != IPCID_TO_SEQ(uap->msqid)) {
1018 #ifdef MSG_DEBUG_OK
1019                         kprintf("msqid deleted\n");
1020 #endif
1021                         eval = EIDRM;
1022                         goto done;
1023                 }
1024         }
1025
1026         /*
1027          * Return the message to the user.
1028          *
1029          * First, do the bookkeeping (before we risk being interrupted).
1030          */
1031
1032         msqptr->msg_cbytes -= msghdr->msg_ts;
1033         msqptr->msg_qnum--;
1034         msqptr->msg_lrpid = td->td_proc->p_pid;
1035         msqptr->msg_rtime = time_second;
1036
1037         /*
1038          * Make msgsz the actual amount that we'll be returning.
1039          * Note that this effectively truncates the message if it is too long
1040          * (since msgsz is never increased).
1041          */
1042
1043 #ifdef MSG_DEBUG_OK
1044         kprintf("found a message, msgsz=%d, msg_ts=%d\n", msgsz,
1045             msghdr->msg_ts);
1046 #endif
1047         if (msgsz > msghdr->msg_ts)
1048                 msgsz = msghdr->msg_ts;
1049
1050         /*
1051          * Return the type to the user.
1052          */
1053
1054         eval = copyout((caddr_t)&(msghdr->msg_type), user_msgp,
1055             sizeof(msghdr->msg_type));
1056         if (eval != 0) {
1057 #ifdef MSG_DEBUG_OK
1058                 kprintf("error (%d) copying out message type\n", eval);
1059 #endif
1060                 msg_freehdr(msghdr);
1061                 wakeup((caddr_t)msqptr);
1062                 goto done;
1063         }
1064         user_msgp = (char *)user_msgp + sizeof(msghdr->msg_type);
1065
1066         /*
1067          * Return the segments to the user
1068          */
1069
1070         next = msghdr->msg_spot;
1071         for (len = 0; len < msgsz; len += msginfo.msgssz) {
1072                 size_t tlen;
1073
1074                 if (msgsz - len > msginfo.msgssz)
1075                         tlen = msginfo.msgssz;
1076                 else
1077                         tlen = msgsz - len;
1078                 if (next <= -1)
1079                         panic("next too low #3");
1080                 if (next >= msginfo.msgseg)
1081                         panic("next out of range #3");
1082                 eval = copyout((caddr_t)&msgpool[next * msginfo.msgssz],
1083                     user_msgp, tlen);
1084                 if (eval != 0) {
1085 #ifdef MSG_DEBUG_OK
1086                         kprintf("error (%d) copying out message segment\n",
1087                             eval);
1088 #endif
1089                         msg_freehdr(msghdr);
1090                         wakeup((caddr_t)msqptr);
1091                         goto done;
1092                 }
1093                 user_msgp = (char *)user_msgp + tlen;
1094                 next = msgmaps[next].next;
1095         }
1096
1097         /*
1098          * Done, return the actual number of bytes copied out.
1099          */
1100
1101         msg_freehdr(msghdr);
1102         wakeup((caddr_t)msqptr);
1103         eval = 0;
1104 done:
1105         rel_mplock();
1106         if (eval == 0)
1107                 uap->sysmsg_result = msgsz;
1108         return(eval);
1109 }
1110
1111 static int
1112 sysctl_msqids(SYSCTL_HANDLER_ARGS)
1113 {
1114
1115         return (SYSCTL_OUT(req, msqids,
1116             sizeof(struct msqid_ds) * msginfo.msgmni));
1117 }
1118
1119 TUNABLE_INT("kern.ipc.msgseg", &msginfo.msgseg);
1120 TUNABLE_INT("kern.ipc.msgssz", &msginfo.msgssz);
1121 TUNABLE_INT("kern.ipc.msgmni", &msginfo.msgmni);
1122
1123 SYSCTL_INT(_kern_ipc, OID_AUTO, msgmax, CTLFLAG_RD, &msginfo.msgmax, 0,
1124     "Max characters in message");
1125 SYSCTL_INT(_kern_ipc, OID_AUTO, msgmni, CTLFLAG_RD, &msginfo.msgmni, 0,
1126     "Max message queue identifiers");
1127 SYSCTL_INT(_kern_ipc, OID_AUTO, msgmnb, CTLFLAG_RD, &msginfo.msgmnb, 0,
1128     "Max characters in message queue");
1129 SYSCTL_INT(_kern_ipc, OID_AUTO, msgtql, CTLFLAG_RD, &msginfo.msgtql, 0,
1130     "Max SVID messages in system");
1131 SYSCTL_INT(_kern_ipc, OID_AUTO, msgssz, CTLFLAG_RD, &msginfo.msgssz, 0,
1132     "Power-of-two size of a message segment");
1133 SYSCTL_INT(_kern_ipc, OID_AUTO, msgseg, CTLFLAG_RD, &msginfo.msgseg, 0,
1134     "Number of message segments");
1135 SYSCTL_PROC(_kern_ipc, OID_AUTO, msqids, CTLFLAG_RD,
1136     NULL, 0, sysctl_msqids, "", "Message queue IDs");