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