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