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