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