Deleted Added
full compact
1/* $FreeBSD: head/sys/kern/sysv_msg.c 80670 2001-07-30 19:28:02Z asmodai $ */
1/* $FreeBSD: head/sys/kern/sysv_msg.c 82607 2001-08-31 00:02:18Z dillon $ */
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 "opt_sysvipc.h"
23
24#include <sys/param.h>
25#include <sys/systm.h>
26#include <sys/sysproto.h>
27#include <sys/kernel.h>
28#include <sys/proc.h>
29#include <sys/lock.h>
30#include <sys/mutex.h>
31#include <sys/msg.h>
32#include <sys/syscall.h>
33#include <sys/sysent.h>
34#include <sys/sysctl.h>
35#include <sys/malloc.h>
36#include <sys/jail.h>
37
38static MALLOC_DEFINE(M_MSG, "msg", "SVID compatible message queues");
39
40static void msginit __P((void));
41static int msgunload __P((void));
42static int sysvmsg_modload __P((struct module *, int, void *));
43
44#define MSG_DEBUG
45#undef MSG_DEBUG_OK
46
47static void msg_freehdr __P((struct msg *msghdr));
48
49/* XXX casting to (sy_call_t *) is bogus, as usual. */
50static sy_call_t *msgcalls[] = {
51 (sy_call_t *)msgctl, (sy_call_t *)msgget,
52 (sy_call_t *)msgsnd, (sy_call_t *)msgrcv
53};
54
55struct msg {
56 struct msg *msg_next; /* next msg in the chain */
57 long msg_type; /* type of this message */
58 /* >0 -> type of this message */
59 /* 0 -> free header */
60 u_short msg_ts; /* size of this message */
61 short msg_spot; /* location of start of msg in buffer */
62};
63
64
65#ifndef MSGSSZ
66#define MSGSSZ 8 /* Each segment must be 2^N long */
67#endif
68#ifndef MSGSEG
69#define MSGSEG 2048 /* must be less than 32767 */
70#endif
71#define MSGMAX (MSGSSZ*MSGSEG)
72#ifndef MSGMNB
73#define MSGMNB 2048 /* max # of bytes in a queue */
74#endif
75#ifndef MSGMNI
76#define MSGMNI 40
77#endif
78#ifndef MSGTQL
79#define MSGTQL 40
80#endif
81
82/*
83 * Based on the configuration parameters described in an SVR2 (yes, two)
84 * config(1m) man page.
85 *
86 * Each message is broken up and stored in segments that are msgssz bytes
87 * long. For efficiency reasons, this should be a power of two. Also,
88 * it doesn't make sense if it is less than 8 or greater than about 256.
89 * Consequently, msginit in kern/sysv_msg.c checks that msgssz is a power of
90 * two between 8 and 1024 inclusive (and panic's if it isn't).
91 */
92struct msginfo msginfo = {
93 MSGMAX, /* max chars in a message */
94 MSGMNI, /* # of message queue identifiers */
95 MSGMNB, /* max chars in a queue */
96 MSGTQL, /* max messages in system */
97 MSGSSZ, /* size of a message segment */
98 /* (must be small power of 2 greater than 4) */
99 MSGSEG /* number of message segments */
100};
101
102/*
103 * macros to convert between msqid_ds's and msqid's.
104 * (specific to this implementation)
105 */
106#define MSQID(ix,ds) ((ix) & 0xffff | (((ds).msg_perm.seq << 16) & 0xffff0000))
107#define MSQID_IX(id) ((id) & 0xffff)
108#define MSQID_SEQ(id) (((id) >> 16) & 0xffff)
109
110/*
111 * The rest of this file is specific to this particular implementation.
112 */
113
114struct msgmap {
115 short next; /* next segment in buffer */
116 /* -1 -> available */
117 /* 0..(MSGSEG-1) -> index of next segment */
118};
119
120#define MSG_LOCKED 01000 /* Is this msqid_ds locked? */
121
122static int nfree_msgmaps; /* # of free map entries */
123static short free_msgmaps; /* head of linked list of free map entries */
124static struct msg *free_msghdrs;/* list of free msg headers */
125static char *msgpool; /* MSGMAX byte long msg buffer pool */
126static struct msgmap *msgmaps; /* MSGSEG msgmap structures */
127static struct msg *msghdrs; /* MSGTQL msg headers */
128static struct msqid_ds *msqids; /* MSGMNI msqid_ds struct's */
129
130static void
131msginit()
132{
133 register int i;
134
135 msgpool = malloc(msginfo.msgmax, M_MSG, M_WAITOK);
136 if (msgpool == NULL)
137 panic("msgpool is NULL");
138 msgmaps = malloc(sizeof(struct msgmap) * msginfo.msgseg, M_MSG, M_WAITOK);
139 if (msgmaps == NULL)
140 panic("msgmaps is NULL");
141 msghdrs = malloc(sizeof(struct msg) * msginfo.msgtql, M_MSG, M_WAITOK);
142 if (msghdrs == NULL)
143 panic("msghdrs is NULL");
144 msqids = malloc(sizeof(struct msqid_ds) * msginfo.msgmni, M_MSG, M_WAITOK);
145 if (msqids == NULL)
146 panic("msqids is NULL");
147
148 /*
149 * msginfo.msgssz should be a power of two for efficiency reasons.
150 * It is also pretty silly if msginfo.msgssz is less than 8
151 * or greater than about 256 so ...
152 */
153
154 i = 8;
155 while (i < 1024 && i != msginfo.msgssz)
156 i <<= 1;
157 if (i != msginfo.msgssz) {
158 printf("msginfo.msgssz=%d (0x%x)\n", msginfo.msgssz,
159 msginfo.msgssz);
160 panic("msginfo.msgssz not a small power of 2");
161 }
162
163 if (msginfo.msgseg > 32767) {
164 printf("msginfo.msgseg=%d\n", msginfo.msgseg);
165 panic("msginfo.msgseg > 32767");
166 }
167
168 if (msgmaps == NULL)
169 panic("msgmaps is NULL");
170
171 for (i = 0; i < msginfo.msgseg; i++) {
172 if (i > 0)
173 msgmaps[i-1].next = i;
174 msgmaps[i].next = -1; /* implies entry is available */
175 }
176 free_msgmaps = 0;
177 nfree_msgmaps = msginfo.msgseg;
178
179 if (msghdrs == NULL)
180 panic("msghdrs is NULL");
181
182 for (i = 0; i < msginfo.msgtql; i++) {
183 msghdrs[i].msg_type = 0;
184 if (i > 0)
185 msghdrs[i-1].msg_next = &msghdrs[i];
186 msghdrs[i].msg_next = NULL;
187 }
188 free_msghdrs = &msghdrs[0];
189
190 if (msqids == NULL)
191 panic("msqids is NULL");
192
193 for (i = 0; i < msginfo.msgmni; i++) {
194 msqids[i].msg_qbytes = 0; /* implies entry is available */
195 msqids[i].msg_perm.seq = 0; /* reset to a known value */
196 msqids[i].msg_perm.mode = 0;
197 }
198}
199
200static int
201msgunload()
202{
203 struct msqid_ds *msqptr;
204 int msqid;
205
206 for (msqid = 0; msqid < msginfo.msgmni; msqid++) {
207 /*
208 * Look for an unallocated and unlocked msqid_ds.
209 * msqid_ds's can be locked by msgsnd or msgrcv while
210 * they are copying the message in/out. We can't
211 * re-use the entry until they release it.
212 */
213 msqptr = &msqids[msqid];
214 if (msqptr->msg_qbytes != 0 ||
215 (msqptr->msg_perm.mode & MSG_LOCKED) != 0)
216 break;
217 }
218 if (msqid != msginfo.msgmni)
219 return (EBUSY);
220
221 free(msgpool, M_MSG);
222 free(msgmaps, M_MSG);
223 free(msghdrs, M_MSG);
224 free(msqids, M_MSG);
225 return (0);
226}
227
228
229static int
230sysvmsg_modload(struct module *module, int cmd, void *arg)
231{
232 int error = 0;
233
234 switch (cmd) {
235 case MOD_LOAD:
236 msginit();
237 break;
238 case MOD_UNLOAD:
239 error = msgunload();
240 break;
241 case MOD_SHUTDOWN:
242 break;
243 default:
244 error = EINVAL;
245 break;
246 }
247 return (error);
248}
249
250static moduledata_t sysvmsg_mod = {
251 "sysvmsg",
252 &sysvmsg_modload,
253 NULL
254};
255
256SYSCALL_MODULE_HELPER(msgsys, 6);
257SYSCALL_MODULE_HELPER(msgctl, 3);
258SYSCALL_MODULE_HELPER(msgget, 2);
259SYSCALL_MODULE_HELPER(msgsnd, 4);
260SYSCALL_MODULE_HELPER(msgrcv, 5);
261
262DECLARE_MODULE(sysvmsg, sysvmsg_mod,
263 SI_SUB_SYSV_MSG, SI_ORDER_FIRST);
264MODULE_VERSION(sysvmsg, 1);
265
266/*
267 * Entry point for all MSG calls
268 *
269 * MPSAFE
270 */
271int
272msgsys(p, uap)
273 struct proc *p;
274 /* XXX actually varargs. */
275 struct msgsys_args /* {
276 u_int which;
277 int a2;
278 int a3;
279 int a4;
280 int a5;
281 int a6;
282 } */ *uap;
283{
284 int error;
285
281 if (!jail_sysvipc_allowed && jailed(p->p_ucred))
282 return (ENOSYS);
286 mtx_lock(&Giant);
287
284 if (uap->which >= sizeof(msgcalls)/sizeof(msgcalls[0]))
285 return (EINVAL);
286 return ((*msgcalls[uap->which])(p, &uap->a2));
288 if (!jail_sysvipc_allowed && jailed(p->p_ucred)) {
289 error = ENOSYS;
290 goto done2;
291 }
292 if (uap->which >= sizeof(msgcalls)/sizeof(msgcalls[0])) {
293 error = EINVAL;
294 goto done2;
295 }
296 error = (*msgcalls[uap->which])(p, &uap->a2);
297done2:
298 mtx_unlock(&Giant);
299 return (error);
300}
301
302static void
303msg_freehdr(msghdr)
304 struct msg *msghdr;
305{
306 while (msghdr->msg_ts > 0) {
307 short next;
308 if (msghdr->msg_spot < 0 || msghdr->msg_spot >= msginfo.msgseg)
309 panic("msghdr->msg_spot out of range");
310 next = msgmaps[msghdr->msg_spot].next;
311 msgmaps[msghdr->msg_spot].next = free_msgmaps;
312 free_msgmaps = msghdr->msg_spot;
313 nfree_msgmaps++;
314 msghdr->msg_spot = next;
315 if (msghdr->msg_ts >= msginfo.msgssz)
316 msghdr->msg_ts -= msginfo.msgssz;
317 else
318 msghdr->msg_ts = 0;
319 }
320 if (msghdr->msg_spot != -1)
321 panic("msghdr->msg_spot != -1");
322 msghdr->msg_next = free_msghdrs;
323 free_msghdrs = msghdr;
324}
325
326#ifndef _SYS_SYSPROTO_H_
327struct msgctl_args {
328 int msqid;
329 int cmd;
330 struct msqid_ds *buf;
331};
332#endif
333
334/*
335 * MPSAFE
336 */
337int
338msgctl(p, uap)
339 struct proc *p;
340 register struct msgctl_args *uap;
341{
342 int msqid = uap->msqid;
343 int cmd = uap->cmd;
344 struct msqid_ds *user_msqptr = uap->buf;
329 int rval, eval;
345 int rval, error;
346 struct msqid_ds msqbuf;
347 register struct msqid_ds *msqptr;
348
349#ifdef MSG_DEBUG_OK
350 printf("call to msgctl(%d, %d, 0x%x)\n", msqid, cmd, user_msqptr);
351#endif
352 mtx_lock(&Giant);
353
337 if (!jail_sysvipc_allowed && jailed(p->p_ucred))
338 return (ENOSYS);
354 if (!jail_sysvipc_allowed && jailed(p->p_ucred)) {
355 error = ENOSYS;
356 goto done2;
357 }
358
359 msqid = IPCID_TO_IX(msqid);
360
361 if (msqid < 0 || msqid >= msginfo.msgmni) {
362#ifdef MSG_DEBUG_OK
363 printf("msqid (%d) out of range (0<=msqid<%d)\n", msqid,
364 msginfo.msgmni);
365#endif
347 return(EINVAL);
366 error = EINVAL;
367 goto done2;
368 }
369
370 msqptr = &msqids[msqid];
371
372 if (msqptr->msg_qbytes == 0) {
373#ifdef MSG_DEBUG_OK
374 printf("no such msqid\n");
375#endif
356 return(EINVAL);
376 error = EINVAL;
377 goto done2;
378 }
379 if (msqptr->msg_perm.seq != IPCID_TO_SEQ(uap->msqid)) {
380#ifdef MSG_DEBUG_OK
381 printf("wrong sequence number\n");
382#endif
362 return(EINVAL);
383 error = EINVAL;
384 goto done2;
385 }
386
365 eval = 0;
387 error = 0;
388 rval = 0;
389
390 switch (cmd) {
391
392 case IPC_RMID:
393 {
394 struct msg *msghdr;
373 if ((eval = ipcperm(p, &msqptr->msg_perm, IPC_M)))
374 return(eval);
395 if ((error = ipcperm(p, &msqptr->msg_perm, IPC_M)))
396 goto done2;
397 /* Free the message headers */
398 msghdr = msqptr->msg_first;
399 while (msghdr != NULL) {
400 struct msg *msghdr_tmp;
401
402 /* Free the segments of each message */
403 msqptr->msg_cbytes -= msghdr->msg_ts;
404 msqptr->msg_qnum--;
405 msghdr_tmp = msghdr;
406 msghdr = msghdr->msg_next;
407 msg_freehdr(msghdr_tmp);
408 }
409
410 if (msqptr->msg_cbytes != 0)
411 panic("msg_cbytes is screwed up");
412 if (msqptr->msg_qnum != 0)
413 panic("msg_qnum is screwed up");
414
415 msqptr->msg_qbytes = 0; /* Mark it as free */
416
417 wakeup((caddr_t)msqptr);
418 }
419
420 break;
421
422 case IPC_SET:
401 if ((eval = ipcperm(p, &msqptr->msg_perm, IPC_M)))
402 return(eval);
403 if ((eval = copyin(user_msqptr, &msqbuf, sizeof(msqbuf))) != 0)
404 return(eval);
423 if ((error = ipcperm(p, &msqptr->msg_perm, IPC_M)))
424 goto done2;
425 if ((error = copyin(user_msqptr, &msqbuf, sizeof(msqbuf))) != 0)
426 goto done2;
427 if (msqbuf.msg_qbytes > msqptr->msg_qbytes) {
406 eval = suser(p);
407 if (eval)
408 return(eval);
428 error = suser(p);
429 if (error)
430 goto done2;
431 }
432 if (msqbuf.msg_qbytes > msginfo.msgmnb) {
433#ifdef MSG_DEBUG_OK
434 printf("can't increase msg_qbytes beyond %d (truncating)\n",
435 msginfo.msgmnb);
436#endif
437 msqbuf.msg_qbytes = msginfo.msgmnb; /* silently restrict qbytes to system limit */
438 }
439 if (msqbuf.msg_qbytes == 0) {
440#ifdef MSG_DEBUG_OK
441 printf("can't reduce msg_qbytes to 0\n");
442#endif
421 return(EINVAL); /* non-standard errno! */
443 error = EINVAL; /* non-standard errno! */
444 goto done2;
445 }
446 msqptr->msg_perm.uid = msqbuf.msg_perm.uid; /* change the owner */
447 msqptr->msg_perm.gid = msqbuf.msg_perm.gid; /* change the owner */
448 msqptr->msg_perm.mode = (msqptr->msg_perm.mode & ~0777) |
449 (msqbuf.msg_perm.mode & 0777);
450 msqptr->msg_qbytes = msqbuf.msg_qbytes;
451 msqptr->msg_ctime = time_second;
452 break;
453
454 case IPC_STAT:
432 if ((eval = ipcperm(p, &msqptr->msg_perm, IPC_R))) {
455 if ((error = ipcperm(p, &msqptr->msg_perm, IPC_R))) {
456#ifdef MSG_DEBUG_OK
457 printf("requester doesn't have read access\n");
458#endif
436 return(eval);
459 goto done2;
460 }
438 eval = copyout((caddr_t)msqptr, user_msqptr,
461 error = copyout((caddr_t)msqptr, user_msqptr,
462 sizeof(struct msqid_ds));
463 break;
464
465 default:
466#ifdef MSG_DEBUG_OK
467 printf("invalid command %d\n", cmd);
468#endif
446 return(EINVAL);
469 error = EINVAL;
470 goto done2;
471 }
472
449 if (eval == 0)
473 if (error == 0)
474 p->p_retval[0] = rval;
451 return(eval);
475done2:
476 mtx_unlock(&Giant);
477 return(error);
478}
479
480#ifndef _SYS_SYSPROTO_H_
481struct msgget_args {
482 key_t key;
483 int msgflg;
484};
485#endif
486
487/*
488 * MPSAFE
489 */
490int
491msgget(p, uap)
492 struct proc *p;
493 register struct msgget_args *uap;
494{
466 int msqid, eval;
495 int msqid, error = 0;
496 int key = uap->key;
497 int msgflg = uap->msgflg;
498 struct ucred *cred = p->p_ucred;
499 register struct msqid_ds *msqptr = NULL;
500
501#ifdef MSG_DEBUG_OK
502 printf("msgget(0x%x, 0%o)\n", key, msgflg);
503#endif
504
476 if (!jail_sysvipc_allowed && jailed(p->p_ucred))
477 return (ENOSYS);
505 mtx_lock(&Giant);
506
507 if (!jail_sysvipc_allowed && jailed(p->p_ucred)) {
508 error = ENOSYS;
509 goto done2;
510 }
511
512 if (key != IPC_PRIVATE) {
513 for (msqid = 0; msqid < msginfo.msgmni; msqid++) {
514 msqptr = &msqids[msqid];
515 if (msqptr->msg_qbytes != 0 &&
516 msqptr->msg_perm.key == key)
517 break;
518 }
519 if (msqid < msginfo.msgmni) {
520#ifdef MSG_DEBUG_OK
521 printf("found public key\n");
522#endif
523 if ((msgflg & IPC_CREAT) && (msgflg & IPC_EXCL)) {
524#ifdef MSG_DEBUG_OK
525 printf("not exclusive\n");
526#endif
494 return(EEXIST);
527 error = EEXIST;
528 goto done2;
529 }
496 if ((eval = ipcperm(p, &msqptr->msg_perm, msgflg & 0700 ))) {
530 if ((error = ipcperm(p, &msqptr->msg_perm, msgflg & 0700 ))) {
531#ifdef MSG_DEBUG_OK
532 printf("requester doesn't have 0%o access\n",
533 msgflg & 0700);
534#endif
501 return(eval);
535 goto done2;
536 }
537 goto found;
538 }
539 }
540
541#ifdef MSG_DEBUG_OK
542 printf("need to allocate the msqid_ds\n");
543#endif
544 if (key == IPC_PRIVATE || (msgflg & IPC_CREAT)) {
545 for (msqid = 0; msqid < msginfo.msgmni; msqid++) {
546 /*
547 * Look for an unallocated and unlocked msqid_ds.
548 * msqid_ds's can be locked by msgsnd or msgrcv while
549 * they are copying the message in/out. We can't
550 * re-use the entry until they release it.
551 */
552 msqptr = &msqids[msqid];
553 if (msqptr->msg_qbytes == 0 &&
554 (msqptr->msg_perm.mode & MSG_LOCKED) == 0)
555 break;
556 }
557 if (msqid == msginfo.msgmni) {
558#ifdef MSG_DEBUG_OK
559 printf("no more msqid_ds's available\n");
560#endif
527 return(ENOSPC);
561 error = ENOSPC;
562 goto done2;
563 }
564#ifdef MSG_DEBUG_OK
565 printf("msqid %d is available\n", msqid);
566#endif
567 msqptr->msg_perm.key = key;
568 msqptr->msg_perm.cuid = cred->cr_uid;
569 msqptr->msg_perm.uid = cred->cr_uid;
570 msqptr->msg_perm.cgid = cred->cr_gid;
571 msqptr->msg_perm.gid = cred->cr_gid;
572 msqptr->msg_perm.mode = (msgflg & 0777);
573 /* Make sure that the returned msqid is unique */
574 msqptr->msg_perm.seq++;
575 msqptr->msg_first = NULL;
576 msqptr->msg_last = NULL;
577 msqptr->msg_cbytes = 0;
578 msqptr->msg_qnum = 0;
579 msqptr->msg_qbytes = msginfo.msgmnb;
580 msqptr->msg_lspid = 0;
581 msqptr->msg_lrpid = 0;
582 msqptr->msg_stime = 0;
583 msqptr->msg_rtime = 0;
584 msqptr->msg_ctime = time_second;
585 } else {
586#ifdef MSG_DEBUG_OK
587 printf("didn't find it and wasn't asked to create it\n");
588#endif
554 return(ENOENT);
589 error = ENOENT;
590 goto done2;
591 }
592
593found:
594 /* Construct the unique msqid */
595 p->p_retval[0] = IXSEQ_TO_IPCID(msqid, msqptr->msg_perm);
560 return(0);
596done2:
597 mtx_unlock(&Giant);
598 return (error);
599}
600
601#ifndef _SYS_SYSPROTO_H_
602struct msgsnd_args {
603 int msqid;
604 void *msgp;
605 size_t msgsz;
606 int msgflg;
607};
608#endif
609
610/*
611 * MPSAFE
612 */
613int
614msgsnd(p, uap)
615 struct proc *p;
616 register struct msgsnd_args *uap;
617{
618 int msqid = uap->msqid;
619 void *user_msgp = uap->msgp;
620 size_t msgsz = uap->msgsz;
621 int msgflg = uap->msgflg;
581 int segs_needed, eval;
622 int segs_needed, error = 0;
623 register struct msqid_ds *msqptr;
624 register struct msg *msghdr;
625 short next;
626
627#ifdef MSG_DEBUG_OK
628 printf("call to msgsnd(%d, 0x%x, %d, %d)\n", msqid, user_msgp, msgsz,
629 msgflg);
630#endif
631 mtx_lock(&Giant);
632
591 if (!jail_sysvipc_allowed && jailed(p->p_ucred))
592 return (ENOSYS);
633 if (!jail_sysvipc_allowed && jailed(p->p_ucred)) {
634 error = ENOSYS;
635 goto done2;
636 }
637
638 msqid = IPCID_TO_IX(msqid);
639
640 if (msqid < 0 || msqid >= msginfo.msgmni) {
641#ifdef MSG_DEBUG_OK
642 printf("msqid (%d) out of range (0<=msqid<%d)\n", msqid,
643 msginfo.msgmni);
644#endif
601 return(EINVAL);
645 error = EINVAL;
646 goto done2;
647 }
648
649 msqptr = &msqids[msqid];
650 if (msqptr->msg_qbytes == 0) {
651#ifdef MSG_DEBUG_OK
652 printf("no such message queue id\n");
653#endif
609 return(EINVAL);
654 error = EINVAL;
655 goto done2;
656 }
657 if (msqptr->msg_perm.seq != IPCID_TO_SEQ(uap->msqid)) {
658#ifdef MSG_DEBUG_OK
659 printf("wrong sequence number\n");
660#endif
615 return(EINVAL);
661 error = EINVAL;
662 goto done2;
663 }
664
618 if ((eval = ipcperm(p, &msqptr->msg_perm, IPC_W))) {
665 if ((error = ipcperm(p, &msqptr->msg_perm, IPC_W))) {
666#ifdef MSG_DEBUG_OK
667 printf("requester doesn't have write access\n");
668#endif
622 return(eval);
669 goto done2;
670 }
671
672 segs_needed = (msgsz + msginfo.msgssz - 1) / msginfo.msgssz;
673#ifdef MSG_DEBUG_OK
674 printf("msgsz=%d, msgssz=%d, segs_needed=%d\n", msgsz, msginfo.msgssz,
675 segs_needed);
676#endif
677 for (;;) {
678 int need_more_resources = 0;
679
680 /*
681 * check msgsz
682 * (inside this loop in case msg_qbytes changes while we sleep)
683 */
684
685 if (msgsz > msqptr->msg_qbytes) {
686#ifdef MSG_DEBUG_OK
687 printf("msgsz > msqptr->msg_qbytes\n");
688#endif
642 return(EINVAL);
689 error = EINVAL;
690 goto done2;
691 }
692
693 if (msqptr->msg_perm.mode & MSG_LOCKED) {
694#ifdef MSG_DEBUG_OK
695 printf("msqid is locked\n");
696#endif
697 need_more_resources = 1;
698 }
699 if (msgsz + msqptr->msg_cbytes > msqptr->msg_qbytes) {
700#ifdef MSG_DEBUG_OK
701 printf("msgsz + msg_cbytes > msg_qbytes\n");
702#endif
703 need_more_resources = 1;
704 }
705 if (segs_needed > nfree_msgmaps) {
706#ifdef MSG_DEBUG_OK
707 printf("segs_needed > nfree_msgmaps\n");
708#endif
709 need_more_resources = 1;
710 }
711 if (free_msghdrs == NULL) {
712#ifdef MSG_DEBUG_OK
713 printf("no more msghdrs\n");
714#endif
715 need_more_resources = 1;
716 }
717
718 if (need_more_resources) {
719 int we_own_it;
720
721 if ((msgflg & IPC_NOWAIT) != 0) {
722#ifdef MSG_DEBUG_OK
723 printf("need more resources but caller doesn't want to wait\n");
724#endif
677 return(EAGAIN);
725 error = EAGAIN;
726 goto done2;
727 }
728
729 if ((msqptr->msg_perm.mode & MSG_LOCKED) != 0) {
730#ifdef MSG_DEBUG_OK
731 printf("we don't own the msqid_ds\n");
732#endif
733 we_own_it = 0;
734 } else {
735 /* Force later arrivals to wait for our
736 request */
737#ifdef MSG_DEBUG_OK
738 printf("we own the msqid_ds\n");
739#endif
740 msqptr->msg_perm.mode |= MSG_LOCKED;
741 we_own_it = 1;
742 }
743#ifdef MSG_DEBUG_OK
744 printf("goodnight\n");
745#endif
697 eval = tsleep((caddr_t)msqptr, (PZERO - 4) | PCATCH,
746 error = tsleep((caddr_t)msqptr, (PZERO - 4) | PCATCH,
747 "msgwait", 0);
748#ifdef MSG_DEBUG_OK
700 printf("good morning, eval=%d\n", eval);
749 printf("good morning, error=%d\n", error);
750#endif
751 if (we_own_it)
752 msqptr->msg_perm.mode &= ~MSG_LOCKED;
704 if (eval != 0) {
753 if (error != 0) {
754#ifdef MSG_DEBUG_OK
755 printf("msgsnd: interrupted system call\n");
756#endif
708 return(EINTR);
757 error = EINTR;
758 goto done2;
759 }
760
761 /*
762 * Make sure that the msq queue still exists
763 */
764
765 if (msqptr->msg_qbytes == 0) {
766#ifdef MSG_DEBUG_OK
767 printf("msqid deleted\n");
768#endif
719 return(EIDRM);
769 error = EIDRM;
770 goto done2;
771 }
772
773 } else {
774#ifdef MSG_DEBUG_OK
775 printf("got all the resources that we need\n");
776#endif
777 break;
778 }
779 }
780
781 /*
782 * We have the resources that we need.
783 * Make sure!
784 */
785
786 if (msqptr->msg_perm.mode & MSG_LOCKED)
787 panic("msg_perm.mode & MSG_LOCKED");
788 if (segs_needed > nfree_msgmaps)
789 panic("segs_needed > nfree_msgmaps");
790 if (msgsz + msqptr->msg_cbytes > msqptr->msg_qbytes)
791 panic("msgsz + msg_cbytes > msg_qbytes");
792 if (free_msghdrs == NULL)
793 panic("no more msghdrs");
794
795 /*
796 * Re-lock the msqid_ds in case we page-fault when copying in the
797 * message
798 */
799
800 if ((msqptr->msg_perm.mode & MSG_LOCKED) != 0)
801 panic("msqid_ds is already locked");
802 msqptr->msg_perm.mode |= MSG_LOCKED;
803
804 /*
805 * Allocate a message header
806 */
807
808 msghdr = free_msghdrs;
809 free_msghdrs = msghdr->msg_next;
810 msghdr->msg_spot = -1;
811 msghdr->msg_ts = msgsz;
812
813 /*
814 * Allocate space for the message
815 */
816
817 while (segs_needed > 0) {
818 if (nfree_msgmaps <= 0)
819 panic("not enough msgmaps");
820 if (free_msgmaps == -1)
821 panic("nil free_msgmaps");
822 next = free_msgmaps;
823 if (next <= -1)
824 panic("next too low #1");
825 if (next >= msginfo.msgseg)
826 panic("next out of range #1");
827#ifdef MSG_DEBUG_OK
828 printf("allocating segment %d to message\n", next);
829#endif
830 free_msgmaps = msgmaps[next].next;
831 nfree_msgmaps--;
832 msgmaps[next].next = msghdr->msg_spot;
833 msghdr->msg_spot = next;
834 segs_needed--;
835 }
836
837 /*
838 * Copy in the message type
839 */
840
790 if ((eval = copyin(user_msgp, &msghdr->msg_type,
841 if ((error = copyin(user_msgp, &msghdr->msg_type,
842 sizeof(msghdr->msg_type))) != 0) {
843#ifdef MSG_DEBUG_OK
793 printf("error %d copying the message type\n", eval);
844 printf("error %d copying the message type\n", error);
845#endif
846 msg_freehdr(msghdr);
847 msqptr->msg_perm.mode &= ~MSG_LOCKED;
848 wakeup((caddr_t)msqptr);
798 return(eval);
849 goto done2;
850 }
851 user_msgp = (char *)user_msgp + sizeof(msghdr->msg_type);
852
853 /*
854 * Validate the message type
855 */
856
857 if (msghdr->msg_type < 1) {
858 msg_freehdr(msghdr);
859 msqptr->msg_perm.mode &= ~MSG_LOCKED;
860 wakeup((caddr_t)msqptr);
861#ifdef MSG_DEBUG_OK
862 printf("mtype (%d) < 1\n", msghdr->msg_type);
863#endif
813 return(EINVAL);
864 error = EINVAL;
865 goto done2;
866 }
867
868 /*
869 * Copy in the message body
870 */
871
872 next = msghdr->msg_spot;
873 while (msgsz > 0) {
874 size_t tlen;
875 if (msgsz > msginfo.msgssz)
876 tlen = msginfo.msgssz;
877 else
878 tlen = msgsz;
879 if (next <= -1)
880 panic("next too low #2");
881 if (next >= msginfo.msgseg)
882 panic("next out of range #2");
831 if ((eval = copyin(user_msgp, &msgpool[next * msginfo.msgssz],
883 if ((error = copyin(user_msgp, &msgpool[next * msginfo.msgssz],
884 tlen)) != 0) {
885#ifdef MSG_DEBUG_OK
834 printf("error %d copying in message segment\n", eval);
886 printf("error %d copying in message segment\n", error);
887#endif
888 msg_freehdr(msghdr);
889 msqptr->msg_perm.mode &= ~MSG_LOCKED;
890 wakeup((caddr_t)msqptr);
839 return(eval);
891 goto done2;
892 }
893 msgsz -= tlen;
894 user_msgp = (char *)user_msgp + tlen;
895 next = msgmaps[next].next;
896 }
897 if (next != -1)
898 panic("didn't use all the msg segments");
899
900 /*
901 * We've got the message. Unlock the msqid_ds.
902 */
903
904 msqptr->msg_perm.mode &= ~MSG_LOCKED;
905
906 /*
907 * Make sure that the msqid_ds is still allocated.
908 */
909
910 if (msqptr->msg_qbytes == 0) {
911 msg_freehdr(msghdr);
912 wakeup((caddr_t)msqptr);
861 return(EIDRM);
913 error = EIDRM;
914 goto done2;
915 }
916
917 /*
918 * Put the message into the queue
919 */
920
921 if (msqptr->msg_first == NULL) {
922 msqptr->msg_first = msghdr;
923 msqptr->msg_last = msghdr;
924 } else {
925 msqptr->msg_last->msg_next = msghdr;
926 msqptr->msg_last = msghdr;
927 }
928 msqptr->msg_last->msg_next = NULL;
929
930 msqptr->msg_cbytes += msghdr->msg_ts;
931 msqptr->msg_qnum++;
932 msqptr->msg_lspid = p->p_pid;
933 msqptr->msg_stime = time_second;
934
935 wakeup((caddr_t)msqptr);
936 p->p_retval[0] = 0;
884 return(0);
937done2:
938 mtx_unlock(&Giant);
939 return (error);
940}
941
942#ifndef _SYS_SYSPROTO_H_
943struct msgrcv_args {
944 int msqid;
945 void *msgp;
946 size_t msgsz;
947 long msgtyp;
948 int msgflg;
949};
950#endif
951
952/*
953 * MPSAFE
954 */
955int
956msgrcv(p, uap)
957 struct proc *p;
958 register struct msgrcv_args *uap;
959{
960 int msqid = uap->msqid;
961 void *user_msgp = uap->msgp;
962 size_t msgsz = uap->msgsz;
963 long msgtyp = uap->msgtyp;
964 int msgflg = uap->msgflg;
965 size_t len;
966 register struct msqid_ds *msqptr;
967 register struct msg *msghdr;
910 int eval;
968 int error = 0;
969 short next;
970
971#ifdef MSG_DEBUG_OK
972 printf("call to msgrcv(%d, 0x%x, %d, %ld, %d)\n", msqid, user_msgp,
973 msgsz, msgtyp, msgflg);
974#endif
975
918 if (!jail_sysvipc_allowed && jailed(p->p_ucred))
919 return (ENOSYS);
976 mtx_lock(&Giant);
977
978 if (!jail_sysvipc_allowed && jailed(p->p_ucred)) {
979 error = ENOSYS;
980 goto done2;
981 }
982
983 msqid = IPCID_TO_IX(msqid);
984
985 if (msqid < 0 || msqid >= msginfo.msgmni) {
986#ifdef MSG_DEBUG_OK
987 printf("msqid (%d) out of range (0<=msqid<%d)\n", msqid,
988 msginfo.msgmni);
989#endif
928 return(EINVAL);
990 error = EINVAL;
991 goto done2;
992 }
993
994 msqptr = &msqids[msqid];
995 if (msqptr->msg_qbytes == 0) {
996#ifdef MSG_DEBUG_OK
997 printf("no such message queue id\n");
998#endif
936 return(EINVAL);
999 error = EINVAL;
1000 goto done2;
1001 }
1002 if (msqptr->msg_perm.seq != IPCID_TO_SEQ(uap->msqid)) {
1003#ifdef MSG_DEBUG_OK
1004 printf("wrong sequence number\n");
1005#endif
942 return(EINVAL);
1006 error = EINVAL;
1007 goto done2;
1008 }
1009
945 if ((eval = ipcperm(p, &msqptr->msg_perm, IPC_R))) {
1010 if ((error = ipcperm(p, &msqptr->msg_perm, IPC_R))) {
1011#ifdef MSG_DEBUG_OK
1012 printf("requester doesn't have read access\n");
1013#endif
949 return(eval);
1014 goto done2;
1015 }
1016
1017 msghdr = NULL;
1018 while (msghdr == NULL) {
1019 if (msgtyp == 0) {
1020 msghdr = msqptr->msg_first;
1021 if (msghdr != NULL) {
1022 if (msgsz < msghdr->msg_ts &&
1023 (msgflg & MSG_NOERROR) == 0) {
1024#ifdef MSG_DEBUG_OK
1025 printf("first message on the queue is too big (want %d, got %d)\n",
1026 msgsz, msghdr->msg_ts);
1027#endif
963 return(E2BIG);
1028 error = E2BIG;
1029 goto done2;
1030 }
1031 if (msqptr->msg_first == msqptr->msg_last) {
1032 msqptr->msg_first = NULL;
1033 msqptr->msg_last = NULL;
1034 } else {
1035 msqptr->msg_first = msghdr->msg_next;
1036 if (msqptr->msg_first == NULL)
1037 panic("msg_first/last screwed up #1");
1038 }
1039 }
1040 } else {
1041 struct msg *previous;
1042 struct msg **prev;
1043
1044 previous = NULL;
1045 prev = &(msqptr->msg_first);
1046 while ((msghdr = *prev) != NULL) {
1047 /*
1048 * Is this message's type an exact match or is
1049 * this message's type less than or equal to
1050 * the absolute value of a negative msgtyp?
1051 * Note that the second half of this test can
1052 * NEVER be true if msgtyp is positive since
1053 * msg_type is always positive!
1054 */
1055
1056 if (msgtyp == msghdr->msg_type ||
1057 msghdr->msg_type <= -msgtyp) {
1058#ifdef MSG_DEBUG_OK
1059 printf("found message type %d, requested %d\n",
1060 msghdr->msg_type, msgtyp);
1061#endif
1062 if (msgsz < msghdr->msg_ts &&
1063 (msgflg & MSG_NOERROR) == 0) {
1064#ifdef MSG_DEBUG_OK
1065 printf("requested message on the queue is too big (want %d, got %d)\n",
1066 msgsz, msghdr->msg_ts);
1067#endif
1002 return(E2BIG);
1068 error = E2BIG;
1069 goto done2;
1070 }
1071 *prev = msghdr->msg_next;
1072 if (msghdr == msqptr->msg_last) {
1073 if (previous == NULL) {
1074 if (prev !=
1075 &msqptr->msg_first)
1076 panic("msg_first/last screwed up #2");
1077 msqptr->msg_first =
1078 NULL;
1079 msqptr->msg_last =
1080 NULL;
1081 } else {
1082 if (prev ==
1083 &msqptr->msg_first)
1084 panic("msg_first/last screwed up #3");
1085 msqptr->msg_last =
1086 previous;
1087 }
1088 }
1089 break;
1090 }
1091 previous = msghdr;
1092 prev = &(msghdr->msg_next);
1093 }
1094 }
1095
1096 /*
1097 * We've either extracted the msghdr for the appropriate
1098 * message or there isn't one.
1099 * If there is one then bail out of this loop.
1100 */
1101
1102 if (msghdr != NULL)
1103 break;
1104
1105 /*
1106 * Hmph! No message found. Does the user want to wait?
1107 */
1108
1109 if ((msgflg & IPC_NOWAIT) != 0) {
1110#ifdef MSG_DEBUG_OK
1111 printf("no appropriate message found (msgtyp=%d)\n",
1112 msgtyp);
1113#endif
1114 /* The SVID says to return ENOMSG. */
1048 return(ENOMSG);
1115 error = ENOMSG;
1116 goto done2;
1117 }
1118
1119 /*
1120 * Wait for something to happen
1121 */
1122
1123#ifdef MSG_DEBUG_OK
1124 printf("msgrcv: goodnight\n");
1125#endif
1058 eval = tsleep((caddr_t)msqptr, (PZERO - 4) | PCATCH, "msgwait",
1126 error = tsleep((caddr_t)msqptr, (PZERO - 4) | PCATCH, "msgwait",
1127 0);
1128#ifdef MSG_DEBUG_OK
1061 printf("msgrcv: good morning (eval=%d)\n", eval);
1129 printf("msgrcv: good morning (error=%d)\n", error);
1130#endif
1131
1064 if (eval != 0) {
1132 if (error != 0) {
1133#ifdef MSG_DEBUG_OK
1134 printf("msgsnd: interrupted system call\n");
1135#endif
1068 return(EINTR);
1136 error = EINTR;
1137 goto done2;
1138 }
1139
1140 /*
1141 * Make sure that the msq queue still exists
1142 */
1143
1144 if (msqptr->msg_qbytes == 0 ||
1145 msqptr->msg_perm.seq != IPCID_TO_SEQ(uap->msqid)) {
1146#ifdef MSG_DEBUG_OK
1147 printf("msqid deleted\n");
1148#endif
1080 return(EIDRM);
1149 error = EIDRM;
1150 goto done2;
1151 }
1152 }
1153
1154 /*
1155 * Return the message to the user.
1156 *
1157 * First, do the bookkeeping (before we risk being interrupted).
1158 */
1159
1160 msqptr->msg_cbytes -= msghdr->msg_ts;
1161 msqptr->msg_qnum--;
1162 msqptr->msg_lrpid = p->p_pid;
1163 msqptr->msg_rtime = time_second;
1164
1165 /*
1166 * Make msgsz the actual amount that we'll be returning.
1167 * Note that this effectively truncates the message if it is too long
1168 * (since msgsz is never increased).
1169 */
1170
1171#ifdef MSG_DEBUG_OK
1172 printf("found a message, msgsz=%d, msg_ts=%d\n", msgsz,
1173 msghdr->msg_ts);
1174#endif
1175 if (msgsz > msghdr->msg_ts)
1176 msgsz = msghdr->msg_ts;
1177
1178 /*
1179 * Return the type to the user.
1180 */
1181
1112 eval = copyout((caddr_t)&(msghdr->msg_type), user_msgp,
1182 error = copyout((caddr_t)&(msghdr->msg_type), user_msgp,
1183 sizeof(msghdr->msg_type));
1114 if (eval != 0) {
1184 if (error != 0) {
1185#ifdef MSG_DEBUG_OK
1116 printf("error (%d) copying out message type\n", eval);
1186 printf("error (%d) copying out message type\n", error);
1187#endif
1188 msg_freehdr(msghdr);
1189 wakeup((caddr_t)msqptr);
1120 return(eval);
1190 goto done2;
1191 }
1192 user_msgp = (char *)user_msgp + sizeof(msghdr->msg_type);
1193
1194 /*
1195 * Return the segments to the user
1196 */
1197
1198 next = msghdr->msg_spot;
1199 for (len = 0; len < msgsz; len += msginfo.msgssz) {
1200 size_t tlen;
1201
1202 if (msgsz - len > msginfo.msgssz)
1203 tlen = msginfo.msgssz;
1204 else
1205 tlen = msgsz - len;
1206 if (next <= -1)
1207 panic("next too low #3");
1208 if (next >= msginfo.msgseg)
1209 panic("next out of range #3");
1140 eval = copyout((caddr_t)&msgpool[next * msginfo.msgssz],
1210 error = copyout((caddr_t)&msgpool[next * msginfo.msgssz],
1211 user_msgp, tlen);
1142 if (eval != 0) {
1212 if (error != 0) {
1213#ifdef MSG_DEBUG_OK
1214 printf("error (%d) copying out message segment\n",
1145 eval);
1215 error);
1216#endif
1217 msg_freehdr(msghdr);
1218 wakeup((caddr_t)msqptr);
1149 return(eval);
1219 goto done2;
1220 }
1221 user_msgp = (char *)user_msgp + tlen;
1222 next = msgmaps[next].next;
1223 }
1224
1225 /*
1226 * Done, return the actual number of bytes copied out.
1227 */
1228
1229 msg_freehdr(msghdr);
1230 wakeup((caddr_t)msqptr);
1231 p->p_retval[0] = msgsz;
1162 return(0);
1232done2:
1233 mtx_unlock(&Giant);
1234 return (error);
1235}
1236
1237static int
1238sysctl_msqids(SYSCTL_HANDLER_ARGS)
1239{
1240
1241 return (SYSCTL_OUT(req, msqids,
1242 sizeof(struct msqid_ds) * msginfo.msgmni));
1243}
1244
1245SYSCTL_DECL(_kern_ipc);
1246SYSCTL_INT(_kern_ipc, OID_AUTO, msgmax, CTLFLAG_RD, &msginfo.msgmax, 0, "");
1247SYSCTL_INT(_kern_ipc, OID_AUTO, msgmni, CTLFLAG_RD, &msginfo.msgmni, 0, "");
1248SYSCTL_INT(_kern_ipc, OID_AUTO, msgmnb, CTLFLAG_RD, &msginfo.msgmnb, 0, "");
1249SYSCTL_INT(_kern_ipc, OID_AUTO, msgtql, CTLFLAG_RD, &msginfo.msgtql, 0, "");
1250SYSCTL_INT(_kern_ipc, OID_AUTO, msgssz, CTLFLAG_RD, &msginfo.msgssz, 0, "");
1251SYSCTL_INT(_kern_ipc, OID_AUTO, msgseg, CTLFLAG_RD, &msginfo.msgseg, 0, "")
1252SYSCTL_PROC(_kern_ipc, OID_AUTO, msqids, CTLFLAG_RD,
1253 NULL, 0, sysctl_msqids, "", "Message queue IDs");