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