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