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