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