Line data Source code
1 : /* $OpenBSD: sysv_msg.c,v 1.33 2016/09/15 02:00:16 dlg Exp $ */
2 : /* $NetBSD: sysv_msg.c,v 1.19 1996/02/09 19:00:18 christos Exp $ */
3 : /*
4 : * Copyright (c) 2009 Bret S. Lambert <blambert@openbsd.org>
5 : *
6 : * Permission to use, copy, modify, and distribute this software for any
7 : * purpose with or without fee is hereby granted, provided that the above
8 : * copyright notice and this permission notice appear in all copies.
9 : *
10 : * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 : * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 : * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 : * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 : * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 : * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 : * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 : */
18 : /*
19 : * Implementation of SVID messages
20 : *
21 : * Author: Daniel Boulet
22 : *
23 : * Copyright 1993 Daniel Boulet and RTMX Inc.
24 : *
25 : * This system call was implemented by Daniel Boulet under contract from RTMX.
26 : *
27 : * Redistribution and use in source forms, with and without modification,
28 : * are permitted provided that this entire comment appears intact.
29 : *
30 : * Redistribution in binary form may occur without any restrictions.
31 : * Obviously, it would be nice if you gave credit where credit is due
32 : * but requiring it would be too onerous.
33 : *
34 : * This software is provided ``AS IS'' without any warranties of any kind.
35 : */
36 :
37 : #include <sys/param.h>
38 : #include <sys/malloc.h>
39 : #include <sys/mbuf.h>
40 : #include <sys/mount.h>
41 : #include <sys/msg.h>
42 : #include <sys/pool.h>
43 : #include <sys/proc.h>
44 : #include <sys/queue.h>
45 : #include <sys/syscallargs.h>
46 : #include <sys/sysctl.h>
47 : #include <sys/systm.h>
48 : #include <sys/uio.h>
49 :
50 : struct que *que_create(key_t, struct ucred *, int);
51 : struct que *que_lookup(int);
52 : struct que *que_key_lookup(key_t);
53 : void que_wakewriters(void);
54 : void que_free(struct que *);
55 : struct msg *msg_create(struct que *);
56 : void msg_free(struct msg *);
57 : void msg_enqueue(struct que *, struct msg *, struct proc *);
58 : void msg_dequeue(struct que *, struct msg *, struct proc *);
59 : struct msg *msg_lookup(struct que *, int);
60 : int msg_copyin(struct msg *, const char *, size_t, struct proc *);
61 : int msg_copyout(struct msg *, char *, size_t *, struct proc *);
62 :
63 : struct pool sysvmsgpl;
64 : struct msginfo msginfo;
65 :
66 : TAILQ_HEAD(, que) msg_queues;
67 :
68 : int num_ques;
69 : int num_msgs;
70 : int sequence;
71 : int maxmsgs;
72 :
73 : void
74 0 : msginit(void)
75 : {
76 0 : msginfo.msgmax = MSGMAX;
77 0 : msginfo.msgmni = MSGMNI;
78 0 : msginfo.msgmnb = MSGMNB;
79 0 : msginfo.msgtql = MSGTQL;
80 0 : msginfo.msgssz = MSGSSZ;
81 0 : msginfo.msgseg = MSGSEG;
82 :
83 0 : pool_init(&sysvmsgpl, sizeof(struct msg), 0, IPL_NONE, PR_WAITOK,
84 : "sysvmsgpl", NULL);
85 :
86 0 : TAILQ_INIT(&msg_queues);
87 :
88 0 : num_ques = 0;
89 0 : num_msgs = 0;
90 0 : sequence = 1;
91 0 : maxmsgs = 0;
92 0 : }
93 :
94 : int
95 0 : sys_msgctl(struct proc *p, void *v, register_t *retval)
96 : {
97 : struct sys_msgctl_args /* {
98 : syscallarg(int) msqid;
99 : syscallarg(int) cmd;
100 : syscallarg(struct msqid_ds *) buf;
101 0 : } */ *uap = v;
102 :
103 0 : return (msgctl1(p, SCARG(uap, msqid), SCARG(uap, cmd),
104 0 : (caddr_t)SCARG(uap, buf), copyin, copyout));
105 : }
106 :
107 : int
108 0 : msgctl1(struct proc *p, int msqid, int cmd, caddr_t buf,
109 : int (*ds_copyin)(const void *, void *, size_t),
110 : int (*ds_copyout)(const void *, void *, size_t))
111 : {
112 0 : struct msqid_ds tmp;
113 0 : struct ucred *cred = p->p_ucred;
114 : struct que *que;
115 : int error = 0;
116 :
117 0 : if ((que = que_lookup(msqid)) == NULL)
118 0 : return (EINVAL);
119 :
120 0 : QREF(que);
121 :
122 0 : switch (cmd) {
123 :
124 : case IPC_RMID:
125 0 : if ((error = ipcperm(cred, &que->msqid_ds.msg_perm, IPC_M)))
126 : goto out;
127 :
128 0 : TAILQ_REMOVE(&msg_queues, que, que_next);
129 0 : que->que_flags |= MSGQ_DYING;
130 :
131 : /* lose interest in the queue and wait for others to too */
132 0 : if (--que->que_references > 0) {
133 0 : wakeup(que);
134 0 : tsleep(&que->que_references, PZERO, "msgqrm", 0);
135 0 : }
136 :
137 0 : que_free(que);
138 :
139 0 : return (0);
140 :
141 : case IPC_SET:
142 0 : if ((error = ipcperm(cred, &que->msqid_ds.msg_perm, IPC_M)))
143 : goto out;
144 0 : if ((error = ds_copyin(buf, &tmp, sizeof(struct msqid_ds))))
145 : goto out;
146 :
147 : /* only superuser can bump max bytes in queue */
148 0 : if (tmp.msg_qbytes > que->msqid_ds.msg_qbytes &&
149 0 : cred->cr_uid != 0) {
150 : error = EPERM;
151 0 : goto out;
152 : }
153 :
154 : /* restrict max bytes in queue to system limit */
155 0 : if (tmp.msg_qbytes > msginfo.msgmnb)
156 0 : tmp.msg_qbytes = msginfo.msgmnb;
157 :
158 : /* can't reduce msg_bytes to 0 */
159 0 : if (tmp.msg_qbytes == 0) {
160 : error = EINVAL; /* non-standard errno! */
161 0 : goto out;
162 : }
163 :
164 0 : que->msqid_ds.msg_perm.uid = tmp.msg_perm.uid;
165 0 : que->msqid_ds.msg_perm.gid = tmp.msg_perm.gid;
166 0 : que->msqid_ds.msg_perm.mode =
167 0 : (que->msqid_ds.msg_perm.mode & ~0777) |
168 0 : (tmp.msg_perm.mode & 0777);
169 0 : que->msqid_ds.msg_qbytes = tmp.msg_qbytes;
170 0 : que->msqid_ds.msg_ctime = time_second;
171 0 : break;
172 :
173 : case IPC_STAT:
174 0 : if ((error = ipcperm(cred, &que->msqid_ds.msg_perm, IPC_R)))
175 : goto out;
176 0 : error = ds_copyout(&que->msqid_ds, buf,
177 : sizeof(struct msqid_ds));
178 0 : break;
179 :
180 : default:
181 : error = EINVAL;
182 0 : break;
183 : }
184 : out:
185 0 : QRELE(que);
186 :
187 0 : return (error);
188 0 : }
189 :
190 : int
191 0 : sys_msgget(struct proc *p, void *v, register_t *retval)
192 : {
193 : struct sys_msgget_args /* {
194 : syscallarg(key_t) key;
195 : syscallarg(int) msgflg;
196 0 : } */ *uap = v;
197 0 : struct ucred *cred = p->p_ucred;
198 : struct que *que;
199 0 : key_t key = SCARG(uap, key);
200 0 : int msgflg = SCARG(uap, msgflg);
201 0 : int error = 0;
202 :
203 : again:
204 0 : if (key != IPC_PRIVATE) {
205 0 : que = que_key_lookup(key);
206 0 : if (que) {
207 0 : if ((msgflg & IPC_CREAT) && (msgflg & IPC_EXCL))
208 0 : return (EEXIST);
209 0 : if ((error = ipcperm(cred, &que->msqid_ds.msg_perm,
210 0 : msgflg & 0700)))
211 0 : return (error);
212 : goto found;
213 : }
214 : }
215 :
216 : /* don't create a new message queue if the caller doesn't want to */
217 0 : if (key != IPC_PRIVATE && !(msgflg & IPC_CREAT))
218 0 : return (ENOENT);
219 :
220 : /* enforce limits on the maximum number of message queues */
221 0 : if (num_ques >= msginfo.msgmni)
222 0 : return (ENOSPC);
223 :
224 : /*
225 : * if que_create returns NULL, it means that a que with an identical
226 : * key was created while this process was sleeping, so start over
227 : */
228 0 : if ((que = que_create(key, cred, msgflg & 0777)) == NULL)
229 0 : goto again;
230 :
231 : found:
232 0 : *retval = IXSEQ_TO_IPCID(que->que_ix, que->msqid_ds.msg_perm);
233 0 : return (error);
234 0 : }
235 :
236 : #define MSGQ_SPACE(q) ((q)->msqid_ds.msg_qbytes - (q)->msqid_ds.msg_cbytes)
237 :
238 : int
239 0 : sys_msgsnd(struct proc *p, void *v, register_t *retval)
240 : {
241 : struct sys_msgsnd_args /* {
242 : syscallarg(int) msqid;
243 : syscallarg(const void *) msgp;
244 : syscallarg(size_t) msgsz;
245 : syscallarg(int) msgflg;
246 0 : } */ *uap = v;
247 0 : struct ucred *cred = p->p_ucred;
248 : struct que *que;
249 : struct msg *msg;
250 0 : size_t msgsz = SCARG(uap, msgsz);
251 : int error;
252 :
253 0 : if ((que = que_lookup(SCARG(uap, msqid))) == NULL)
254 0 : return (EINVAL);
255 :
256 0 : if (msgsz > que->msqid_ds.msg_qbytes || msgsz > msginfo.msgmax)
257 0 : return (EINVAL);
258 :
259 0 : if ((error = ipcperm(cred, &que->msqid_ds.msg_perm, IPC_W)))
260 0 : return (error);
261 :
262 0 : QREF(que);
263 :
264 0 : while (MSGQ_SPACE(que) < msgsz || num_msgs >= msginfo.msgtql) {
265 :
266 0 : if (SCARG(uap, msgflg) & IPC_NOWAIT) {
267 : error = EAGAIN;
268 0 : goto out;
269 : }
270 :
271 : /* notify world that process may wedge here */
272 0 : if (num_msgs >= msginfo.msgtql)
273 0 : maxmsgs = 1;
274 :
275 0 : que->que_flags |= MSGQ_WRITERS;
276 0 : if ((error = tsleep(que, PZERO|PCATCH, "msgwait", 0)))
277 : goto out;
278 :
279 0 : if (que->que_flags & MSGQ_DYING) {
280 : error = EIDRM;
281 0 : goto out;
282 : }
283 : }
284 :
285 : /* if msg_create returns NULL, the queue is being removed */
286 0 : if ((msg = msg_create(que)) == NULL) {
287 : error = EIDRM;
288 0 : goto out;
289 : }
290 :
291 : /* msg_copyin frees msg on error */
292 0 : if ((error = msg_copyin(msg, (const char *)SCARG(uap, msgp), msgsz, p)))
293 : goto out;
294 :
295 0 : msg_enqueue(que, msg, p);
296 :
297 0 : if (que->que_flags & MSGQ_READERS) {
298 0 : que->que_flags &= ~MSGQ_READERS;
299 0 : wakeup(que);
300 0 : }
301 :
302 0 : if (que->que_flags & MSGQ_DYING) {
303 : error = EIDRM;
304 0 : wakeup(que);
305 0 : }
306 : out:
307 0 : QRELE(que);
308 :
309 0 : return (error);
310 0 : }
311 :
312 : int
313 0 : sys_msgrcv(struct proc *p, void *v, register_t *retval)
314 : {
315 : struct sys_msgrcv_args /* {
316 : syscallarg(int) msqid;
317 : syscallarg(void *) msgp;
318 : syscallarg(size_t) msgsz;
319 : syscallarg(long) msgtyp;
320 : syscallarg(int) msgflg;
321 0 : } */ *uap = v;
322 0 : struct ucred *cred = p->p_ucred;
323 0 : char *msgp = SCARG(uap, msgp);
324 : struct que *que;
325 : struct msg *msg;
326 0 : size_t msgsz = SCARG(uap, msgsz);
327 0 : long msgtyp = SCARG(uap, msgtyp);
328 : int error;
329 :
330 0 : if ((que = que_lookup(SCARG(uap, msqid))) == NULL)
331 0 : return (EINVAL);
332 :
333 0 : if ((error = ipcperm(cred, &que->msqid_ds.msg_perm, IPC_R)))
334 0 : return (error);
335 :
336 0 : QREF(que);
337 :
338 : /* msg_lookup handles matching; sleeping gets handled here */
339 0 : while ((msg = msg_lookup(que, msgtyp)) == NULL) {
340 :
341 0 : if (SCARG(uap, msgflg) & IPC_NOWAIT) {
342 : error = ENOMSG;
343 0 : goto out;
344 : }
345 :
346 0 : que->que_flags |= MSGQ_READERS;
347 0 : if ((error = tsleep(que, PZERO|PCATCH, "msgwait", 0)))
348 : goto out;
349 :
350 : /* make sure the queue still alive */
351 0 : if (que->que_flags & MSGQ_DYING) {
352 : error = EIDRM;
353 0 : goto out;
354 : }
355 : }
356 :
357 : /* if msg_copyout fails, keep the message around so it isn't lost */
358 0 : if ((error = msg_copyout(msg, msgp, &msgsz, p)))
359 : goto out;
360 :
361 0 : msg_dequeue(que, msg, p);
362 0 : msg_free(msg);
363 :
364 0 : if (que->que_flags & MSGQ_WRITERS) {
365 0 : que->que_flags &= ~MSGQ_WRITERS;
366 0 : wakeup(que);
367 0 : }
368 :
369 : /* ensure processes waiting on the global limit don't wedge */
370 0 : if (maxmsgs) {
371 0 : maxmsgs = 0;
372 0 : que_wakewriters();
373 0 : }
374 :
375 0 : *retval = msgsz;
376 : out:
377 0 : QRELE(que);
378 :
379 0 : return (error);
380 0 : }
381 :
382 : /*
383 : * que management functions
384 : */
385 :
386 : struct que *
387 0 : que_create(key_t key, struct ucred *cred, int mode)
388 : {
389 : struct que *que, *que2;
390 : int nextix = 1;
391 :
392 0 : que = malloc(sizeof(*que), M_TEMP, M_WAIT|M_ZERO);
393 :
394 : /* if malloc slept, a queue with the same key may have been created */
395 0 : if (que_key_lookup(key)) {
396 0 : free(que, M_TEMP, sizeof *que);
397 0 : return (NULL);
398 : }
399 :
400 : /* find next available "index" */
401 0 : TAILQ_FOREACH(que2, &msg_queues, que_next) {
402 0 : if (nextix < que2->que_ix)
403 : break;
404 0 : nextix = que2->que_ix + 1;
405 : }
406 0 : que->que_ix = nextix;
407 :
408 0 : que->msqid_ds.msg_perm.key = key;
409 0 : que->msqid_ds.msg_perm.cuid = cred->cr_uid;
410 0 : que->msqid_ds.msg_perm.uid = cred->cr_uid;
411 0 : que->msqid_ds.msg_perm.cgid = cred->cr_gid;
412 0 : que->msqid_ds.msg_perm.gid = cred->cr_gid;
413 0 : que->msqid_ds.msg_perm.mode = mode & 0777;
414 0 : que->msqid_ds.msg_perm.seq = ++sequence & 0x7fff;
415 0 : que->msqid_ds.msg_qbytes = msginfo.msgmnb;
416 0 : que->msqid_ds.msg_ctime = time_second;
417 :
418 0 : TAILQ_INIT(&que->que_msgs);
419 :
420 : /* keep queues in "index" order */
421 0 : if (que2)
422 0 : TAILQ_INSERT_BEFORE(que2, que, que_next);
423 : else
424 0 : TAILQ_INSERT_TAIL(&msg_queues, que, que_next);
425 0 : num_ques++;
426 :
427 0 : return (que);
428 0 : }
429 :
430 : struct que *
431 0 : que_lookup(int id)
432 : {
433 : struct que *que;
434 :
435 0 : TAILQ_FOREACH(que, &msg_queues, que_next)
436 0 : if (que->que_ix == IPCID_TO_IX(id))
437 : break;
438 :
439 : /* don't return queues marked for removal */
440 0 : if (que && que->que_flags & MSGQ_DYING)
441 0 : return (NULL);
442 :
443 0 : return (que);
444 0 : }
445 :
446 : struct que *
447 0 : que_key_lookup(key_t key)
448 : {
449 : struct que *que;
450 :
451 0 : if (key == IPC_PRIVATE)
452 0 : return (NULL);
453 :
454 0 : TAILQ_FOREACH(que, &msg_queues, que_next)
455 0 : if (que->msqid_ds.msg_perm.key == key)
456 : break;
457 :
458 : /* don't return queues marked for removal */
459 0 : if (que && que->que_flags & MSGQ_DYING)
460 0 : return (NULL);
461 :
462 0 : return (que);
463 0 : }
464 :
465 : void
466 0 : que_wakewriters(void)
467 : {
468 : struct que *que;
469 :
470 0 : TAILQ_FOREACH(que, &msg_queues, que_next) {
471 0 : if (que->que_flags & MSGQ_WRITERS) {
472 0 : que->que_flags &= ~MSGQ_WRITERS;
473 0 : wakeup(que);
474 0 : }
475 : }
476 0 : }
477 :
478 : void
479 0 : que_free(struct que *que)
480 : {
481 : struct msg *msg;
482 : #ifdef DIAGNOSTIC
483 0 : if (que->que_references > 0)
484 0 : panic("freeing message queue with active references");
485 : #endif
486 :
487 0 : while ((msg = TAILQ_FIRST(&que->que_msgs))) {
488 0 : TAILQ_REMOVE(&que->que_msgs, msg, msg_next);
489 0 : msg_free(msg);
490 : }
491 0 : free(que, M_TEMP, sizeof *que);
492 0 : num_ques--;
493 0 : }
494 :
495 : /*
496 : * msg management functions
497 : */
498 :
499 : struct msg *
500 0 : msg_create(struct que *que)
501 : {
502 : struct msg *msg;
503 :
504 0 : msg = pool_get(&sysvmsgpl, PR_WAITOK|PR_ZERO);
505 :
506 : /* if the queue has died during allocation, return NULL */
507 0 : if (que->que_flags & MSGQ_DYING) {
508 0 : pool_put(&sysvmsgpl, msg);
509 0 : wakeup(que);
510 0 : return(NULL);
511 : }
512 :
513 0 : num_msgs++;
514 :
515 0 : return (msg);
516 0 : }
517 :
518 : struct msg *
519 0 : msg_lookup(struct que *que, int msgtyp)
520 : {
521 : struct msg *msg;
522 :
523 : /*
524 : * Three different matches are performed based on the value of msgtyp:
525 : * 1) msgtyp > 0 => match exactly
526 : * 2) msgtyp = 0 => match any
527 : * 3) msgtyp < 0 => match any up to absolute value of msgtyp
528 : */
529 0 : TAILQ_FOREACH(msg, &que->que_msgs, msg_next)
530 0 : if (msgtyp == 0 || msgtyp == msg->msg_type ||
531 0 : (msgtyp < 0 && -msgtyp <= msg->msg_type))
532 : break;
533 :
534 0 : return (msg);
535 : }
536 :
537 : void
538 0 : msg_free(struct msg *msg)
539 : {
540 0 : m_freem(msg->msg_data);
541 0 : pool_put(&sysvmsgpl, msg);
542 0 : num_msgs--;
543 0 : }
544 :
545 : void
546 0 : msg_enqueue(struct que *que, struct msg *msg, struct proc *p)
547 : {
548 0 : que->msqid_ds.msg_cbytes += msg->msg_len;
549 0 : que->msqid_ds.msg_qnum++;
550 0 : que->msqid_ds.msg_lspid = p->p_p->ps_pid;
551 0 : que->msqid_ds.msg_stime = time_second;
552 :
553 0 : TAILQ_INSERT_TAIL(&que->que_msgs, msg, msg_next);
554 0 : }
555 :
556 : void
557 0 : msg_dequeue(struct que *que, struct msg *msg, struct proc *p)
558 : {
559 0 : que->msqid_ds.msg_cbytes -= msg->msg_len;
560 0 : que->msqid_ds.msg_qnum--;
561 0 : que->msqid_ds.msg_lrpid = p->p_p->ps_pid;
562 0 : que->msqid_ds.msg_rtime = time_second;
563 :
564 0 : TAILQ_REMOVE(&que->que_msgs, msg, msg_next);
565 0 : }
566 :
567 : /*
568 : * The actual I/O routines. A note concerning the layout of SysV msg buffers:
569 : *
570 : * The data to be copied is laid out as a single userspace buffer, with a
571 : * long preceding an opaque buffer of len bytes. The long value ends
572 : * up being the message type, which needs to be copied separately from
573 : * the buffer data, which is stored in in mbufs.
574 : */
575 :
576 : int
577 0 : msg_copyin(struct msg *msg, const char *ubuf, size_t len, struct proc *p)
578 : {
579 : struct mbuf **mm, *m;
580 : size_t xfer;
581 : int error;
582 :
583 0 : if (msg == NULL)
584 0 : panic ("msg NULL");
585 :
586 0 : if ((error = copyin(ubuf, &msg->msg_type, sizeof(long)))) {
587 0 : msg_free(msg);
588 0 : return (error);
589 : }
590 :
591 0 : if (msg->msg_type < 0) {
592 0 : msg_free(msg);
593 0 : return (EINVAL);
594 : }
595 :
596 0 : ubuf += sizeof(long);
597 :
598 0 : msg->msg_len = 0;
599 0 : mm = &msg->msg_data;
600 :
601 0 : while (msg->msg_len < len) {
602 0 : m = m_get(M_WAIT, MT_DATA);
603 0 : if (len >= MINCLSIZE) {
604 0 : MCLGET(m, M_WAIT);
605 0 : xfer = min(len, MCLBYTES);
606 0 : } else {
607 0 : xfer = min(len, MLEN);
608 : }
609 0 : m->m_len = xfer;
610 0 : msg->msg_len += xfer;
611 0 : *mm = m;
612 0 : mm = &m->m_next;
613 : }
614 :
615 0 : for (m = msg->msg_data; m; m = m->m_next) {
616 0 : if ((error = copyin(ubuf, mtod(m, void *), m->m_len))) {
617 0 : msg_free(msg);
618 0 : return (error);
619 : }
620 0 : ubuf += m->m_len;
621 : }
622 :
623 0 : return (0);
624 0 : }
625 :
626 : int
627 0 : msg_copyout(struct msg *msg, char *ubuf, size_t *len, struct proc *p)
628 : {
629 : struct mbuf *m;
630 : size_t xfer;
631 : int error;
632 :
633 : #ifdef DIAGNOSTIC
634 0 : if (msg->msg_len > MSGMAX)
635 0 : panic("SysV message longer than MSGMAX");
636 : #endif
637 :
638 : /* silently truncate messages too large for user buffer */
639 0 : xfer = min(*len, msg->msg_len);
640 :
641 0 : if ((error = copyout(&msg->msg_type, ubuf, sizeof(long))))
642 0 : return (error);
643 :
644 0 : ubuf += sizeof(long);
645 0 : *len = xfer;
646 :
647 0 : for (m = msg->msg_data; m; m = m->m_next) {
648 0 : if ((error = copyout(mtod(m, void *), ubuf, m->m_len)))
649 0 : return (error);
650 0 : ubuf += m->m_len;
651 : }
652 :
653 0 : return (0);
654 0 : }
655 :
656 : int
657 0 : sysctl_sysvmsg(int *name, u_int namelen, void *where, size_t *sizep)
658 : {
659 : struct msg_sysctl_info *info;
660 : struct que *que;
661 : size_t infolen;
662 : int error;
663 :
664 0 : switch (*name) {
665 : case KERN_SYSVIPC_MSG_INFO:
666 :
667 0 : if (namelen != 1)
668 0 : return (ENOTDIR);
669 :
670 : /*
671 : * The userland ipcs(1) utility expects to be able
672 : * to iterate over at least msginfo.msgmni queues,
673 : * even if those queues don't exist. This is an
674 : * artifact of the previous implementation of
675 : * message queues; for now, emulate this behavior
676 : * until a more thorough fix can be made.
677 : */
678 0 : infolen = sizeof(msginfo) +
679 0 : msginfo.msgmni * sizeof(struct msqid_ds);
680 0 : if (where == NULL) {
681 0 : *sizep = infolen;
682 0 : return (0);
683 : }
684 :
685 : /*
686 : * More special-casing due to previous implementation:
687 : * if the caller just wants the msginfo struct, then
688 : * sizep will point to the value sizeof(struct msginfo).
689 : * In that case, only copy out the msginfo struct to
690 : * the caller.
691 : */
692 0 : if (*sizep == sizeof(struct msginfo))
693 0 : return (copyout(&msginfo, where, sizeof(msginfo)));
694 :
695 0 : info = malloc(infolen, M_TEMP, M_WAIT|M_ZERO);
696 :
697 : /* if the malloc slept, this may have changed */
698 0 : infolen = sizeof(msginfo) +
699 0 : msginfo.msgmni * sizeof(struct msqid_ds);
700 :
701 0 : if (*sizep < infolen) {
702 0 : free(info, M_TEMP, 0);
703 0 : return (ENOMEM);
704 : }
705 :
706 0 : memcpy(&info->msginfo, &msginfo, sizeof(struct msginfo));
707 :
708 : /*
709 : * Special case #3: the previous array-based implementation
710 : * exported the array indices and userland has come to rely
711 : * upon these indices, so keep behavior consisitent.
712 : */
713 0 : TAILQ_FOREACH(que, &msg_queues, que_next)
714 0 : memcpy(&info->msgids[que->que_ix], &que->msqid_ds,
715 : sizeof(struct msqid_ds));
716 :
717 0 : error = copyout(info, where, infolen);
718 :
719 0 : free(info, M_TEMP, 0);
720 :
721 0 : return (error);
722 :
723 : default:
724 0 : return (EINVAL);
725 : }
726 0 : }
|