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