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