123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727 |
- /* $OpenBSD: sysv_msg.c,v 1.30 2014/12/19 05:59:21 tedu Exp $ */
- /* $NetBSD: sysv_msg.c,v 1.19 1996/02/09 19:00:18 christos Exp $ */
- /*
- * Copyright (c) 2009 Bret S. Lambert <blambert@openbsd.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
- /*
- * Implementation of SVID messages
- *
- * Author: Daniel Boulet
- *
- * Copyright 1993 Daniel Boulet and RTMX Inc.
- *
- * This system call was implemented by Daniel Boulet under contract from RTMX.
- *
- * Redistribution and use in source forms, with and without modification,
- * are permitted provided that this entire comment appears intact.
- *
- * Redistribution in binary form may occur without any restrictions.
- * Obviously, it would be nice if you gave credit where credit is due
- * but requiring it would be too onerous.
- *
- * This software is provided ``AS IS'' without any warranties of any kind.
- */
- #include <sys/param.h>
- #include <sys/malloc.h>
- #include <sys/mbuf.h>
- #include <sys/mount.h>
- #include <sys/msg.h>
- #include <sys/pool.h>
- #include <sys/proc.h>
- #include <sys/queue.h>
- #include <sys/syscallargs.h>
- #include <sys/sysctl.h>
- #include <sys/systm.h>
- #include <sys/uio.h>
- struct que *que_create(key_t, struct ucred *, int);
- struct que *que_lookup(int);
- struct que *que_key_lookup(key_t);
- void que_wakewriters(void);
- void que_free(struct que *);
- struct msg *msg_create(struct que *);
- void msg_free(struct msg *);
- void msg_enqueue(struct que *, struct msg *, struct proc *);
- void msg_dequeue(struct que *, struct msg *, struct proc *);
- struct msg *msg_lookup(struct que *, int);
- int msg_copyin(struct msg *, const char *, size_t, struct proc *);
- int msg_copyout(struct msg *, char *, size_t *, struct proc *);
- struct pool sysvmsgpl;
- struct msginfo msginfo;
- TAILQ_HEAD(, que) msg_queues;
- int num_ques;
- int num_msgs;
- int sequence;
- int maxmsgs;
- void
- msginit(void)
- {
- msginfo.msgmax = MSGMAX;
- msginfo.msgmni = MSGMNI;
- msginfo.msgmnb = MSGMNB;
- msginfo.msgtql = MSGTQL;
- msginfo.msgssz = MSGSSZ;
- msginfo.msgseg = MSGSEG;
- pool_init(&sysvmsgpl, sizeof(struct msg), 0, 0, PR_WAITOK, "sysvmsgpl",
- NULL);
- TAILQ_INIT(&msg_queues);
- num_ques = 0;
- num_msgs = 0;
- sequence = 1;
- maxmsgs = 0;
- }
- int
- sys_msgctl(struct proc *p, void *v, register_t *retval)
- {
- struct sys_msgctl_args /* {
- syscallarg(int) msqid;
- syscallarg(int) cmd;
- syscallarg(struct msqid_ds *) buf;
- } */ *uap = v;
- return (msgctl1(p, SCARG(uap, msqid), SCARG(uap, cmd),
- (caddr_t)SCARG(uap, buf), copyin, copyout));
- }
- int
- msgctl1(struct proc *p, int msqid, int cmd, caddr_t buf,
- int (*ds_copyin)(const void *, void *, size_t),
- int (*ds_copyout)(const void *, void *, size_t))
- {
- struct msqid_ds tmp;
- struct ucred *cred = p->p_ucred;
- struct que *que;
- int error = 0;
- if ((que = que_lookup(msqid)) == NULL)
- return (EINVAL);
- QREF(que);
- switch (cmd) {
- case IPC_RMID:
- if ((error = ipcperm(cred, &que->msqid_ds.msg_perm, IPC_M)))
- goto out;
- TAILQ_REMOVE(&msg_queues, que, que_next);
- que->que_flags |= MSGQ_DYING;
- /* lose interest in the queue and wait for others to too */
- if (--que->que_references > 0) {
- wakeup(que);
- tsleep(&que->que_references, PZERO, "msgqrm", 0);
- }
- que_free(que);
- return (0);
- case IPC_SET:
- if ((error = ipcperm(cred, &que->msqid_ds.msg_perm, IPC_M)))
- goto out;
- if ((error = ds_copyin(buf, &tmp, sizeof(struct msqid_ds))))
- goto out;
- /* only superuser can bump max bytes in queue */
- if (tmp.msg_qbytes > que->msqid_ds.msg_qbytes &&
- cred->cr_uid != 0) {
- error = EPERM;
- goto out;
- }
- /* restrict max bytes in queue to system limit */
- if (tmp.msg_qbytes > msginfo.msgmnb)
- tmp.msg_qbytes = msginfo.msgmnb;
- /* can't reduce msg_bytes to 0 */
- if (tmp.msg_qbytes == 0) {
- error = EINVAL; /* non-standard errno! */
- goto out;
- }
- que->msqid_ds.msg_perm.uid = tmp.msg_perm.uid;
- que->msqid_ds.msg_perm.gid = tmp.msg_perm.gid;
- que->msqid_ds.msg_perm.mode =
- (que->msqid_ds.msg_perm.mode & ~0777) |
- (tmp.msg_perm.mode & 0777);
- que->msqid_ds.msg_qbytes = tmp.msg_qbytes;
- que->msqid_ds.msg_ctime = time_second;
- break;
- case IPC_STAT:
- if ((error = ipcperm(cred, &que->msqid_ds.msg_perm, IPC_R)))
- goto out;
- error = ds_copyout(&que->msqid_ds, buf,
- sizeof(struct msqid_ds));
- break;
- default:
- error = EINVAL;
- break;
- }
- out:
- QRELE(que);
- return (error);
- }
- int
- sys_msgget(struct proc *p, void *v, register_t *retval)
- {
- struct sys_msgget_args /* {
- syscallarg(key_t) key;
- syscallarg(int) msgflg;
- } */ *uap = v;
- struct ucred *cred = p->p_ucred;
- struct que *que;
- key_t key = SCARG(uap, key);
- int msgflg = SCARG(uap, msgflg);
- int error = 0;
- again:
- if (key != IPC_PRIVATE) {
- que = que_key_lookup(key);
- if (que) {
- if ((msgflg & IPC_CREAT) && (msgflg & IPC_EXCL))
- return (EEXIST);
- if ((error = ipcperm(cred, &que->msqid_ds.msg_perm,
- msgflg & 0700)))
- return (error);
- goto found;
- }
- }
- /* don't create a new message queue if the caller doesn't want to */
- if (key != IPC_PRIVATE && !(msgflg & IPC_CREAT))
- return (ENOENT);
- /* enforce limits on the maximum number of message queues */
- if (num_ques >= msginfo.msgmni)
- return (ENOSPC);
- /*
- * if que_create returns NULL, it means that a que with an identical
- * key was created while this process was sleeping, so start over
- */
- if ((que = que_create(key, cred, msgflg & 0777)) == NULL)
- goto again;
- found:
- *retval = IXSEQ_TO_IPCID(que->que_ix, que->msqid_ds.msg_perm);
- return (error);
- }
- #define MSGQ_SPACE(q) ((q)->msqid_ds.msg_qbytes - (q)->msqid_ds.msg_cbytes)
- int
- sys_msgsnd(struct proc *p, void *v, register_t *retval)
- {
- struct sys_msgsnd_args /* {
- syscallarg(int) msqid;
- syscallarg(const void *) msgp;
- syscallarg(size_t) msgsz;
- syscallarg(int) msgflg;
- } */ *uap = v;
- struct ucred *cred = p->p_ucred;
- struct que *que;
- struct msg *msg;
- size_t msgsz = SCARG(uap, msgsz);
- int error;
- if ((que = que_lookup(SCARG(uap, msqid))) == NULL)
- return (EINVAL);
- if (msgsz > que->msqid_ds.msg_qbytes || msgsz > msginfo.msgmax)
- return (EINVAL);
- if ((error = ipcperm(cred, &que->msqid_ds.msg_perm, IPC_W)))
- return (error);
- QREF(que);
- while (MSGQ_SPACE(que) < msgsz || num_msgs >= msginfo.msgtql) {
- if (SCARG(uap, msgflg) & IPC_NOWAIT) {
- error = EAGAIN;
- goto out;
- }
- /* notify world that process may wedge here */
- if (num_msgs >= msginfo.msgtql)
- maxmsgs = 1;
- que->que_flags |= MSGQ_WRITERS;
- if ((error = tsleep(que, PZERO|PCATCH, "msgwait", 0)))
- goto out;
- if (que->que_flags & MSGQ_DYING) {
- error = EIDRM;
- goto out;
- }
- }
- /* if msg_create returns NULL, the queue is being removed */
- if ((msg = msg_create(que)) == NULL) {
- error = EIDRM;
- goto out;
- }
- /* msg_copyin frees msg on error */
- if ((error = msg_copyin(msg, (const char *)SCARG(uap, msgp), msgsz, p)))
- goto out;
- msg_enqueue(que, msg, p);
- if (que->que_flags & MSGQ_READERS) {
- que->que_flags &= ~MSGQ_READERS;
- wakeup(que);
- }
- if (que->que_flags & MSGQ_DYING) {
- error = EIDRM;
- wakeup(que);
- }
- out:
- QRELE(que);
- return (error);
- }
- int
- sys_msgrcv(struct proc *p, void *v, register_t *retval)
- {
- struct sys_msgrcv_args /* {
- syscallarg(int) msqid;
- syscallarg(void *) msgp;
- syscallarg(size_t) msgsz;
- syscallarg(long) msgtyp;
- syscallarg(int) msgflg;
- } */ *uap = v;
- struct ucred *cred = p->p_ucred;
- char *msgp = SCARG(uap, msgp);
- struct que *que;
- struct msg *msg;
- size_t msgsz = SCARG(uap, msgsz);
- long msgtyp = SCARG(uap, msgtyp);
- int error;
- if ((que = que_lookup(SCARG(uap, msqid))) == NULL)
- return (EINVAL);
- if ((error = ipcperm(cred, &que->msqid_ds.msg_perm, IPC_R)))
- return (error);
- QREF(que);
- /* msg_lookup handles matching; sleeping gets handled here */
- while ((msg = msg_lookup(que, msgtyp)) == NULL) {
- if (SCARG(uap, msgflg) & IPC_NOWAIT) {
- error = ENOMSG;
- goto out;
- }
- que->que_flags |= MSGQ_READERS;
- if ((error = tsleep(que, PZERO|PCATCH, "msgwait", 0)))
- goto out;
- /* make sure the queue still alive */
- if (que->que_flags & MSGQ_DYING) {
- error = EIDRM;
- goto out;
- }
- }
- /* if msg_copyout fails, keep the message around so it isn't lost */
- if ((error = msg_copyout(msg, msgp, &msgsz, p)))
- goto out;
- msg_dequeue(que, msg, p);
- msg_free(msg);
- if (que->que_flags & MSGQ_WRITERS) {
- que->que_flags &= ~MSGQ_WRITERS;
- wakeup(que);
- }
- /* ensure processes waiting on the global limit don't wedge */
- if (maxmsgs) {
- maxmsgs = 0;
- que_wakewriters();
- }
- *retval = msgsz;
- out:
- QRELE(que);
- return (error);
- }
- /*
- * que management functions
- */
- struct que *
- que_create(key_t key, struct ucred *cred, int mode)
- {
- struct que *que, *que2;
- int nextix = 1;
- que = malloc(sizeof(*que), M_TEMP, M_WAIT|M_ZERO);
- /* if malloc slept, a queue with the same key may have been created */
- if (que_key_lookup(key)) {
- free(que, M_TEMP, 0);
- return (NULL);
- }
- /* find next available "index" */
- TAILQ_FOREACH(que2, &msg_queues, que_next) {
- if (nextix < que2->que_ix)
- break;
- nextix = que2->que_ix + 1;
- }
- que->que_ix = nextix;
- que->msqid_ds.msg_perm.key = key;
- que->msqid_ds.msg_perm.cuid = cred->cr_uid;
- que->msqid_ds.msg_perm.uid = cred->cr_uid;
- que->msqid_ds.msg_perm.cgid = cred->cr_gid;
- que->msqid_ds.msg_perm.gid = cred->cr_gid;
- que->msqid_ds.msg_perm.mode = mode & 0777;
- que->msqid_ds.msg_perm.seq = ++sequence & 0x7fff;
- que->msqid_ds.msg_qbytes = msginfo.msgmnb;
- que->msqid_ds.msg_ctime = time_second;
- TAILQ_INIT(&que->que_msgs);
- /* keep queues in "index" order */
- if (que2)
- TAILQ_INSERT_BEFORE(que2, que, que_next);
- else
- TAILQ_INSERT_TAIL(&msg_queues, que, que_next);
- num_ques++;
- return (que);
- }
- struct que *
- que_lookup(int id)
- {
- struct que *que;
- TAILQ_FOREACH(que, &msg_queues, que_next)
- if (que->que_ix == IPCID_TO_IX(id))
- break;
- /* don't return queues marked for removal */
- if (que && que->que_flags & MSGQ_DYING)
- return (NULL);
- return (que);
- }
- struct que *
- que_key_lookup(key_t key)
- {
- struct que *que;
- if (key == IPC_PRIVATE)
- return (NULL);
- TAILQ_FOREACH(que, &msg_queues, que_next)
- if (que->msqid_ds.msg_perm.key == key)
- break;
- /* don't return queues marked for removal */
- if (que && que->que_flags & MSGQ_DYING)
- return (NULL);
- return (que);
- }
- void
- que_wakewriters(void)
- {
- struct que *que;
- TAILQ_FOREACH(que, &msg_queues, que_next) {
- if (que->que_flags & MSGQ_WRITERS) {
- que->que_flags &= ~MSGQ_WRITERS;
- wakeup(que);
- }
- }
- }
- void
- que_free(struct que *que)
- {
- struct msg *msg;
- #ifdef DIAGNOSTIC
- if (que->que_references > 0)
- panic("freeing message queue with active references");
- #endif
- while ((msg = TAILQ_FIRST(&que->que_msgs))) {
- TAILQ_REMOVE(&que->que_msgs, msg, msg_next);
- msg_free(msg);
- }
- free(que, M_TEMP, 0);
- num_ques--;
- }
- /*
- * msg management functions
- */
- struct msg *
- msg_create(struct que *que)
- {
- struct msg *msg;
- msg = pool_get(&sysvmsgpl, PR_WAITOK|PR_ZERO);
- /* if the queue has died during allocation, return NULL */
- if (que->que_flags & MSGQ_DYING) {
- pool_put(&sysvmsgpl, msg);
- wakeup(que);
- return(NULL);
- }
- num_msgs++;
- return (msg);
- }
- struct msg *
- msg_lookup(struct que *que, int msgtyp)
- {
- struct msg *msg;
- /*
- * Three different matches are performed based on the value of msgtyp:
- * 1) msgtyp > 0 => match exactly
- * 2) msgtyp = 0 => match any
- * 3) msgtyp < 0 => match any up to absolute value of msgtyp
- */
- TAILQ_FOREACH(msg, &que->que_msgs, msg_next)
- if (msgtyp == 0 || msgtyp == msg->msg_type ||
- (msgtyp < 0 && -msgtyp <= msg->msg_type))
- break;
- return (msg);
- }
- void
- msg_free(struct msg *msg)
- {
- m_freem(msg->msg_data);
- pool_put(&sysvmsgpl, msg);
- num_msgs--;
- }
- void
- msg_enqueue(struct que *que, struct msg *msg, struct proc *p)
- {
- que->msqid_ds.msg_cbytes += msg->msg_len;
- que->msqid_ds.msg_qnum++;
- que->msqid_ds.msg_lspid = p->p_p->ps_pid;
- que->msqid_ds.msg_stime = time_second;
- TAILQ_INSERT_TAIL(&que->que_msgs, msg, msg_next);
- }
- void
- msg_dequeue(struct que *que, struct msg *msg, struct proc *p)
- {
- que->msqid_ds.msg_cbytes -= msg->msg_len;
- que->msqid_ds.msg_qnum--;
- que->msqid_ds.msg_lrpid = p->p_p->ps_pid;
- que->msqid_ds.msg_rtime = time_second;
- TAILQ_REMOVE(&que->que_msgs, msg, msg_next);
- }
- /*
- * The actual I/O routines. A note concerning the layout of SysV msg buffers:
- *
- * The data to be copied is laid out as a single userspace buffer, with a
- * long preceding an opaque buffer of len bytes. The long value ends
- * up being the message type, which needs to be copied separately from
- * the buffer data, which is stored in in mbufs.
- */
- int
- msg_copyin(struct msg *msg, const char *ubuf, size_t len, struct proc *p)
- {
- struct mbuf **mm, *m;
- size_t xfer;
- int error;
- if (msg == NULL)
- panic ("msg NULL");
- if ((error = copyin(ubuf, &msg->msg_type, sizeof(long)))) {
- msg_free(msg);
- return (error);
- }
- if (msg->msg_type < 0) {
- msg_free(msg);
- return (EINVAL);
- }
- ubuf += sizeof(long);
- msg->msg_len = 0;
- mm = &msg->msg_data;
- while (msg->msg_len < len) {
- m = m_get(M_WAIT, MT_DATA);
- if (len >= MINCLSIZE) {
- MCLGET(m, M_WAIT);
- xfer = min(len, MCLBYTES);
- } else {
- xfer = min(len, MLEN);
- }
- m->m_len = xfer;
- msg->msg_len += xfer;
- *mm = m;
- mm = &m->m_next;
- }
- for (m = msg->msg_data; m; m = m->m_next) {
- if ((error = copyin(ubuf, mtod(m, void *), m->m_len))) {
- msg_free(msg);
- return (error);
- }
- ubuf += m->m_len;
- }
- return (0);
- }
- int
- msg_copyout(struct msg *msg, char *ubuf, size_t *len, struct proc *p)
- {
- struct mbuf *m;
- size_t xfer;
- int error;
- #ifdef DIAGNOSTIC
- if (msg->msg_len > MSGMAX)
- panic("SysV message longer than MSGMAX");
- #endif
- /* silently truncate messages too large for user buffer */
- xfer = min(*len, msg->msg_len);
- if ((error = copyout(&msg->msg_type, ubuf, sizeof(long))))
- return (error);
- ubuf += sizeof(long);
- *len = xfer;
- for (m = msg->msg_data; m; m = m->m_next) {
- if ((error = copyout(mtod(m, void *), ubuf, m->m_len)))
- return (error);
- ubuf += m->m_len;
- }
- return (0);
- }
- int
- sysctl_sysvmsg(int *name, u_int namelen, void *where, size_t *sizep)
- {
- struct msg_sysctl_info *info;
- struct que *que;
- size_t infolen;
- int error;
- switch (*name) {
- case KERN_SYSVIPC_MSG_INFO:
- if (namelen != 1)
- return (ENOTDIR);
- /*
- * The userland ipcs(1) utility expects to be able
- * to iterate over at least msginfo.msgmni queues,
- * even if those queues don't exist. This is an
- * artifact of the previous implementation of
- * message queues; for now, emulate this behavior
- * until a more thorough fix can be made.
- */
- infolen = sizeof(msginfo) +
- msginfo.msgmni * sizeof(struct msqid_ds);
- if (where == NULL) {
- *sizep = infolen;
- return (0);
- }
- /*
- * More special-casing due to previous implementation:
- * if the caller just wants the msginfo struct, then
- * sizep will point to the value sizeof(struct msginfo).
- * In that case, only copy out the msginfo struct to
- * the caller.
- */
- if (*sizep == sizeof(struct msginfo))
- return (copyout(&msginfo, where, sizeof(msginfo)));
- info = malloc(infolen, M_TEMP, M_WAIT|M_ZERO);
- /* if the malloc slept, this may have changed */
- infolen = sizeof(msginfo) +
- msginfo.msgmni * sizeof(struct msqid_ds);
- if (*sizep < infolen) {
- free(info, M_TEMP, 0);
- return (ENOMEM);
- }
- memcpy(&info->msginfo, &msginfo, sizeof(struct msginfo));
- /*
- * Special case #3: the previous array-based implementation
- * exported the array indices and userland has come to rely
- * upon these indices, so keep behavior consisitent.
- */
- TAILQ_FOREACH(que, &msg_queues, que_next)
- memcpy(&info->msgids[que->que_ix], &que->msqid_ds,
- sizeof(struct msqid_ds));
- error = copyout(info, where, infolen);
- free(info, M_TEMP, 0);
- return (error);
- default:
- return (EINVAL);
- }
- }
|