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