home *** CD-ROM | disk | FTP | other *** search
- /*
- * linux/ipc/msg.c
- * Copyright (C) 1992 Krishna Balasubramanian
- */
-
- #include <linux/errno.h>
- #include <linux/sched.h>
- #include <linux/msg.h>
- #include <linux/stat.h>
- #include <linux/malloc.h>
-
- #include <asm/segment.h>
-
- extern int ipcperms (struct ipc_perm *ipcp, short msgflg);
-
- static void freeque (int id);
- static int newque (key_t key, int msgflg);
- static int findkey (key_t key);
-
- static struct msqid_ds *msgque[MSGMNI];
- static int msgbytes = 0;
- static int msghdrs = 0;
- static unsigned short msg_seq = 0;
- static int used_queues = 0;
- static int max_msqid = 0;
- static struct wait_queue *msg_lock = NULL;
-
- void msg_init (void)
- {
- int id;
-
- for (id=0; id < MSGMNI; id++)
- msgque[id] = (struct msqid_ds *) IPC_UNUSED;
- msgbytes = msghdrs = msg_seq = max_msqid = used_queues = 0;
- msg_lock = NULL;
- return;
- }
-
- int sys_msgsnd (int msqid, struct msgbuf *msgp, int msgsz, int msgflg)
- {
- int id, err;
- struct msqid_ds *msq;
- struct ipc_perm *ipcp;
- struct msg *msgh;
- long mtype;
-
- if (msgsz > MSGMAX || msgsz < 0 || msqid < 0)
- return -EINVAL;
- if (!msgp)
- return -EFAULT;
- err = verify_area (VERIFY_READ, msgp->mtext, msgsz);
- if (err)
- return err;
- if ((mtype = get_fs_long (&msgp->mtype)) < 1)
- return -EINVAL;
- id = msqid % MSGMNI;
- msq = msgque [id];
- if (msq == IPC_UNUSED || msq == IPC_NOID)
- return -EINVAL;
- ipcp = &msq->msg_perm;
-
- slept:
- if (ipcp->seq != (msqid / MSGMNI))
- return -EIDRM;
- if (ipcperms(ipcp, S_IWUGO))
- return -EACCES;
-
- if (msgsz + msq->msg_cbytes > msq->msg_qbytes) {
- /* no space in queue */
- if (msgflg & IPC_NOWAIT)
- return -EAGAIN;
- if (current->signal & ~current->blocked)
- return -EINTR;
- interruptible_sleep_on (&msq->wwait);
- goto slept;
- }
-
- /* allocate message header and text space*/
- msgh = (struct msg *) kmalloc (sizeof(*msgh) + msgsz, GFP_USER);
- if (!msgh)
- return -ENOMEM;
- msgh->msg_spot = (char *) (msgh + 1);
- memcpy_fromfs (msgh->msg_spot, msgp->mtext, msgsz);
-
- if (msgque[id] == IPC_UNUSED || msgque[id] == IPC_NOID
- || ipcp->seq != msqid / MSGMNI) {
- kfree_s (msgh, sizeof(*msgh) + msgsz);
- return -EIDRM;
- }
-
- msgh->msg_next = NULL;
- if (!msq->msg_first)
- msq->msg_first = msq->msg_last = msgh;
- else {
- msq->msg_last->msg_next = msgh;
- msq->msg_last = msgh;
- }
- msgh->msg_ts = msgsz;
- msgh->msg_type = mtype;
- msq->msg_cbytes += msgsz;
- msgbytes += msgsz;
- msghdrs++;
- msq->msg_qnum++;
- msq->msg_lspid = current->pid;
- msq->msg_stime = CURRENT_TIME;
- if (msq->rwait)
- wake_up (&msq->rwait);
- return msgsz;
- }
-
- int sys_msgrcv (int msqid, struct msgbuf *msgp, int msgsz, long msgtyp,
- int msgflg)
- {
- struct msqid_ds *msq;
- struct ipc_perm *ipcp;
- struct msg *tmsg, *leastp = NULL;
- struct msg *nmsg = NULL;
- int id, err;
-
- if (msqid < 0 || msgsz < 0)
- return -EINVAL;
- if (!msgp || !msgp->mtext)
- return -EFAULT;
- err = verify_area (VERIFY_WRITE, msgp->mtext, msgsz);
- if (err)
- return err;
-
- id = msqid % MSGMNI;
- msq = msgque [id];
- if (msq == IPC_NOID || msq == IPC_UNUSED)
- return -EINVAL;
- ipcp = &msq->msg_perm;
-
- /*
- * find message of correct type.
- * msgtyp = 0 => get first.
- * msgtyp > 0 => get first message of matching type.
- * msgtyp < 0 => get message with least type must be < abs(msgtype).
- */
- while (!nmsg) {
- if(ipcp->seq != msqid / MSGMNI)
- return -EIDRM;
- if (ipcperms (ipcp, S_IRUGO))
- return -EACCES;
- if (msgtyp == 0)
- nmsg = msq->msg_first;
- else if (msgtyp > 0) {
- if (msgflg & MSG_EXCEPT) {
- for (tmsg = msq->msg_first; tmsg;
- tmsg = tmsg->msg_next)
- if (tmsg->msg_type != msgtyp)
- break;
- nmsg = tmsg;
- } else {
- for (tmsg = msq->msg_first; tmsg;
- tmsg = tmsg->msg_next)
- if (tmsg->msg_type == msgtyp)
- break;
- nmsg = tmsg;
- }
- } else {
- for (leastp = tmsg = msq->msg_first; tmsg;
- tmsg = tmsg->msg_next)
- if (tmsg->msg_type < leastp->msg_type)
- leastp = tmsg;
- if (leastp && leastp->msg_type <= - msgtyp)
- nmsg = leastp;
- }
-
- if (nmsg) { /* done finding a message */
- if ((msgsz < nmsg->msg_ts) && !(msgflg & MSG_NOERROR))
- return -E2BIG;
- msgsz = (msgsz > nmsg->msg_ts)? nmsg->msg_ts : msgsz;
- if (nmsg == msq->msg_first)
- msq->msg_first = nmsg->msg_next;
- else {
- for (tmsg= msq->msg_first; tmsg;
- tmsg = tmsg->msg_next)
- if (tmsg->msg_next == nmsg)
- break;
- tmsg->msg_next = nmsg->msg_next;
- if (nmsg == msq->msg_last)
- msq->msg_last = tmsg;
- }
- if (!(--msq->msg_qnum))
- msq->msg_last = msq->msg_first = NULL;
-
- msq->msg_rtime = CURRENT_TIME;
- msq->msg_lrpid = current->pid;
- msgbytes -= nmsg->msg_ts;
- msghdrs--;
- msq->msg_cbytes -= nmsg->msg_ts;
- if (msq->wwait)
- wake_up (&msq->wwait);
- put_fs_long (nmsg->msg_type, &msgp->mtype);
- memcpy_tofs (msgp->mtext, nmsg->msg_spot, msgsz);
- kfree_s (nmsg, sizeof(*nmsg) + msgsz);
- return msgsz;
- } else { /* did not find a message */
- if (msgflg & IPC_NOWAIT)
- return -ENOMSG;
- if (current->signal & ~current->blocked)
- return -EINTR;
- interruptible_sleep_on (&msq->rwait);
- }
- } /* end while */
- return -1;
- }
-
-
- static int findkey (key_t key)
- {
- int id;
- struct msqid_ds *msq;
-
- for (id=0; id <= max_msqid; id++) {
- while ((msq = msgque[id]) == IPC_NOID)
- interruptible_sleep_on (&msg_lock);
- if (msq == IPC_UNUSED)
- continue;
- if (key == msq->msg_perm.key)
- return id;
- }
- return -1;
- }
-
- static int newque (key_t key, int msgflg)
- {
- int id;
- struct msqid_ds *msq;
- struct ipc_perm *ipcp;
-
- for (id=0; id < MSGMNI; id++)
- if (msgque[id] == IPC_UNUSED) {
- msgque[id] = (struct msqid_ds *) IPC_NOID;
- goto found;
- }
- return -ENOSPC;
-
- found:
- msq = (struct msqid_ds *) kmalloc (sizeof (*msq), GFP_KERNEL);
- if (!msq) {
- msgque[id] = (struct msqid_ds *) IPC_UNUSED;
- if (msg_lock)
- wake_up (&msg_lock);
- return -ENOMEM;
- }
- ipcp = &msq->msg_perm;
- ipcp->mode = (msgflg & S_IRWXUGO);
- ipcp->key = key;
- ipcp->cuid = ipcp->uid = current->euid;
- ipcp->gid = ipcp->cgid = current->egid;
- ipcp->seq = msg_seq;
- msq->msg_first = msq->msg_last = NULL;
- msq->rwait = msq->wwait = NULL;
- msq->msg_cbytes = msq->msg_qnum = 0;
- msq->msg_lspid = msq->msg_lrpid = 0;
- msq->msg_stime = msq->msg_rtime = 0;
- msq->msg_qbytes = MSGMNB;
- msq->msg_ctime = CURRENT_TIME;
- if (id > max_msqid)
- max_msqid = id;
- msgque[id] = msq;
- used_queues++;
- if (msg_lock)
- wake_up (&msg_lock);
- return (int) msg_seq * MSGMNI + id;
- }
-
- int sys_msgget (key_t key, int msgflg)
- {
- int id;
- struct msqid_ds *msq;
-
- if (key == IPC_PRIVATE)
- return newque(key, msgflg);
- if ((id = findkey (key)) == -1) { /* key not used */
- if (!(msgflg & IPC_CREAT))
- return -ENOENT;
- return newque(key, msgflg);
- }
- if (msgflg & IPC_CREAT && msgflg & IPC_EXCL)
- return -EEXIST;
- msq = msgque[id];
- if (msq == IPC_UNUSED || msq == IPC_NOID)
- return -EIDRM;
- if (ipcperms(&msq->msg_perm, msgflg))
- return -EACCES;
- return msq->msg_perm.seq * MSGMNI +id;
- }
-
- static void freeque (int id)
- {
- struct msqid_ds *msq = msgque[id];
- struct msg *msgp, *msgh;
-
- msq->msg_perm.seq++;
- msg_seq++;
- msgbytes -= msq->msg_cbytes;
- if (id == max_msqid)
- while (max_msqid && (msgque[--max_msqid] == IPC_UNUSED));
- msgque[id] = (struct msqid_ds *) IPC_UNUSED;
- used_queues--;
- while (msq->rwait || msq->wwait) {
- if (msq->rwait)
- wake_up (&msq->rwait);
- if (msq->wwait)
- wake_up (&msq->wwait);
- schedule();
- }
- for (msgp = msq->msg_first; msgp; msgp = msgh ) {
- msgh = msgp->msg_next;
- msghdrs--;
- kfree_s (msgp, sizeof(*msgp) + msgp->msg_ts);
- }
- kfree_s (msq, sizeof (*msq));
- }
-
- int sys_msgctl (int msqid, int cmd, struct msqid_ds *buf)
- {
- int id, err;
- struct msqid_ds *msq, tbuf;
- struct ipc_perm *ipcp;
-
- if (msqid < 0 || cmd < 0)
- return -EINVAL;
- switch (cmd) {
- case IPC_INFO:
- case MSG_INFO:
- if (!buf)
- return -EFAULT;
- {
- struct msginfo msginfo;
- msginfo.msgmni = MSGMNI;
- msginfo.msgmax = MSGMAX;
- msginfo.msgmnb = MSGMNB;
- msginfo.msgmap = MSGMAP;
- msginfo.msgpool = MSGPOOL;
- msginfo.msgtql = MSGTQL;
- msginfo.msgssz = MSGSSZ;
- msginfo.msgseg = MSGSEG;
- if (cmd == MSG_INFO) {
- msginfo.msgpool = used_queues;
- msginfo.msgmap = msghdrs;
- msginfo.msgtql = msgbytes;
- }
- err = verify_area (VERIFY_WRITE, buf, sizeof (struct msginfo));
- if (err)
- return err;
- memcpy_tofs (buf, &msginfo, sizeof(struct msginfo));
- return max_msqid;
- }
- case MSG_STAT:
- if (!buf)
- return -EFAULT;
- err = verify_area (VERIFY_WRITE, buf, sizeof (*msq));
- if (err)
- return err;
- if (msqid > max_msqid)
- return -EINVAL;
- msq = msgque[msqid];
- if (msq == IPC_UNUSED || msq == IPC_NOID)
- return -EINVAL;
- if (ipcperms (&msq->msg_perm, S_IRUGO))
- return -EACCES;
- id = msqid + msq->msg_perm.seq * MSGMNI;
- memcpy_tofs (buf, msq, sizeof(*msq));
- return id;
- case IPC_SET:
- if (!buf)
- return -EFAULT;
- memcpy_fromfs (&tbuf, buf, sizeof (*buf));
- break;
- case IPC_STAT:
- if (!buf)
- return -EFAULT;
- err = verify_area (VERIFY_WRITE, buf, sizeof(*msq));
- if (err)
- return err;
- break;
- }
-
- id = msqid % MSGMNI;
- msq = msgque [id];
- if (msq == IPC_UNUSED || msq == IPC_NOID)
- return -EINVAL;
- ipcp = &msq->msg_perm;
- if (ipcp->seq != msqid / MSGMNI)
- return -EIDRM;
-
- switch (cmd) {
- case IPC_STAT:
- if (ipcperms (ipcp, S_IRUGO))
- return -EACCES;
- memcpy_tofs (buf, msq, sizeof (*msq));
- return 0;
- break;
- case IPC_RMID: case IPC_SET:
- if (!suser() && current->euid != ipcp->cuid &&
- current->euid != ipcp->uid)
- return -EPERM;
- if (cmd == IPC_RMID) {
- freeque (id);
- return 0;
- }
- if (tbuf.msg_qbytes > MSGMNB && !suser())
- return -EPERM;
- msq->msg_qbytes = tbuf.msg_qbytes;
- ipcp->uid = tbuf.msg_perm.uid;
- ipcp->gid = tbuf.msg_perm.gid;
- ipcp->mode = (ipcp->mode & ~S_IRWXUGO) |
- (S_IRWXUGO & tbuf.msg_perm.mode);
- msq->msg_ctime = CURRENT_TIME;
- break;
- default:
- return -EINVAL;
- break;
- }
- return 0;
- }
-