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