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