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