patch-1.3.57 linux/ipc/msg.c
Next file: linux/ipc/shm.c
Previous file: linux/ipc/Makefile
Back to the patch index
Back to the overall index
- Lines: 346
- Date:
Tue Jan 9 12:26:52 1996
- Orig file:
v1.3.56/linux/ipc/msg.c
- Orig date:
Tue Jul 11 10:03:00 1995
diff -u --recursive --new-file v1.3.56/linux/ipc/msg.c linux/ipc/msg.c
@@ -1,13 +1,18 @@
/*
* linux/ipc/msg.c
* Copyright (C) 1992 Krishna Balasubramanian
+ *
+ * Kerneld extensions by Bjorn Ekwall <bj0rn@blox.se> in May 1995
+ *
*/
+#include <linux/autoconf.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/msg.h>
#include <linux/stat.h>
#include <linux/malloc.h>
+#include <linux/kerneld.h>
#include <asm/segment.h>
@@ -24,6 +29,8 @@
static int used_queues = 0;
static int max_msqid = 0;
static struct wait_queue *msg_lock = NULL;
+static int kerneld_msqid = -1;
+static int kerneld_pid;
void msg_init (void)
{
@@ -36,7 +43,7 @@
return;
}
-asmlinkage int sys_msgsnd (int msqid, struct msgbuf *msgp, size_t msgsz, int msgflg)
+static int real_msgsnd (int msqid, struct msgbuf *msgp, size_t msgsz, int msgflg)
{
int id, err;
struct msqid_ds *msq;
@@ -48,11 +55,19 @@
return -EINVAL;
if (!msgp)
return -EFAULT;
- err = verify_area (VERIFY_READ, msgp->mtext, msgsz);
- if (err)
- return err;
- if ((mtype = get_user (&msgp->mtype)) < 1)
- return -EINVAL;
+ /*
+ * Calls from kernel level (IPC_KERNELD set)
+ * have the message somewhere in kernel space already!
+ */
+ if ((msgflg & IPC_KERNELD))
+ mtype = msgp->mtype;
+ else {
+ err = verify_area (VERIFY_READ, msgp->mtext, msgsz);
+ if (err)
+ return err;
+ if ((mtype = get_user (&msgp->mtype)) < 1)
+ return -EINVAL;
+ }
id = (unsigned int) msqid % MSGMNI;
msq = msgque [id];
if (msq == IPC_UNUSED || msq == IPC_NOID)
@@ -62,8 +77,14 @@
slept:
if (msq->msg_perm.seq != (unsigned int) msqid / MSGMNI)
return -EIDRM;
- if (ipcperms(ipcp, S_IWUGO))
- return -EACCES;
+ /*
+ * Non-root processes may send to kerneld!
+ * i.e. no permission check if called from the kernel
+ * otoh we don't want user level non-root snoopers...
+ */
+ if ((msgflg & IPC_KERNELD) == 0)
+ if (ipcperms(ipcp, S_IWUGO))
+ return -EACCES;
if (msgsz + msq->msg_cbytes > msq->msg_qbytes) {
/* no space in queue */
@@ -71,6 +92,11 @@
return -EAGAIN;
if (current->signal & ~current->blocked)
return -EINTR;
+ if (intr_count) {
+ /* Very unlikely, but better safe than sorry... */
+ printk("Ouch, kerneld:msgsnd wants to sleep at interrupt!\n");
+ return -EINTR;
+ }
interruptible_sleep_on (&msq->wwait);
goto slept;
}
@@ -80,7 +106,24 @@
if (!msgh)
return -ENOMEM;
msgh->msg_spot = (char *) (msgh + 1);
- memcpy_fromfs (msgh->msg_spot, msgp->mtext, msgsz);
+
+ /*
+ * Calls from kernel level (IPC_KERNELD set)
+ * have the message somewhere in kernel space already!
+ */
+ if (msgflg & IPC_KERNELD) {
+ struct kerneld_msg *kdmp = (struct kerneld_msg *)msgp;
+
+ /*
+ * Note that the kernel supplies a pointer
+ * but the user-level kerneld uses a char array...
+ */
+ memcpy(msgh->msg_spot, (char *)(&(kdmp->id)), sizeof(long));
+ memcpy(msgh->msg_spot + sizeof(long), kdmp->text,
+ msgsz - sizeof(long));
+ }
+ else
+ memcpy_fromfs (msgh->msg_spot, msgp->mtext, msgsz);
if (msgque[id] == IPC_UNUSED || msgque[id] == IPC_NOID
|| msq->msg_perm.seq != (unsigned int) msqid / MSGMNI) {
@@ -108,8 +151,7 @@
return 0;
}
-asmlinkage int sys_msgrcv (int msqid, struct msgbuf *msgp, size_t msgsz, long msgtyp,
- int msgflg)
+static int real_msgrcv (int msqid, struct msgbuf *msgp, size_t msgsz, long msgtyp, int msgflg)
{
struct msqid_ds *msq;
struct ipc_perm *ipcp;
@@ -121,9 +163,15 @@
return -EINVAL;
if (!msgp || !msgp->mtext)
return -EFAULT;
- err = verify_area (VERIFY_WRITE, msgp->mtext, msgsz);
- if (err)
- return err;
+ /*
+ * Calls from kernel level (IPC_KERNELD set)
+ * wants the message put in kernel space!
+ */
+ if ((msgflg & IPC_KERNELD) == 0) {
+ err = verify_area (VERIFY_WRITE, msgp->mtext, msgsz);
+ if (err)
+ return err;
+ }
id = (unsigned int) msqid % MSGMNI;
msq = msgque [id];
@@ -140,6 +188,12 @@
while (!nmsg) {
if (msq->msg_perm.seq != (unsigned int) msqid / MSGMNI)
return -EIDRM;
+ if ((msgflg & IPC_KERNELD) == 0)
+ /*
+ * Non-root processes may recieve from kerneld!
+ * i.e. no permission check if called from the kernel
+ * otoh we don't want user level non-root snoopers...
+ */
if (ipcperms (ipcp, S_IRUGO))
return -EACCES;
if (msgtyp == 0)
@@ -192,8 +246,29 @@
msq->msg_cbytes -= nmsg->msg_ts;
if (msq->wwait)
wake_up (&msq->wwait);
- put_user (nmsg->msg_type, &msgp->mtype);
- memcpy_tofs (msgp->mtext, nmsg->msg_spot, msgsz);
+ /*
+ * Calls from kernel level (IPC_KERNELD set)
+ * wants the message copied to kernel space!
+ */
+ if (msgflg & IPC_KERNELD) {
+ struct kerneld_msg *kdmp = (struct kerneld_msg *) msgp;
+
+ memcpy((char *)(&(kdmp->id)),
+ nmsg->msg_spot,
+ sizeof(long));
+ /*
+ * Note that kdmp->text is a pointer
+ * when called from kernel space!
+ */
+ if ((msgsz > sizeof(long)) && kdmp->text)
+ memcpy(kdmp->text,
+ nmsg->msg_spot + sizeof(long),
+ msgsz - sizeof(long));
+ }
+ else {
+ put_user (nmsg->msg_type, &msgp->mtype);
+ memcpy_tofs (msgp->mtext, nmsg->msg_spot, msgsz);
+ }
kfree(nmsg);
return msgsz;
} else { /* did not find a message */
@@ -201,12 +276,29 @@
return -ENOMSG;
if (current->signal & ~current->blocked)
return -EINTR;
+ if (intr_count) {
+ /* Won't happen... */
+ printk("Ouch, kerneld:msgrcv wants to sleep at interrupt!\n");
+ return -EINTR;
+ }
interruptible_sleep_on (&msq->rwait);
}
} /* end while */
return -1;
}
+asmlinkage int sys_msgsnd (int msqid, struct msgbuf *msgp, size_t msgsz, int msgflg)
+{
+ /* IPC_KERNELD is used as a marker for kernel calls */
+ return real_msgsnd(msqid, msgp, msgsz, msgflg & ~IPC_KERNELD);
+}
+
+asmlinkage int sys_msgrcv (int msqid, struct msgbuf *msgp, size_t msgsz,
+ long msgtyp, int msgflg)
+{
+ /* IPC_KERNELD is used as a marker for kernel calls */
+ return real_msgrcv (msqid, msgp, msgsz, msgtyp, msgflg & ~IPC_KERNELD);
+}
static int findkey (key_t key)
{
@@ -272,6 +364,19 @@
int id;
struct msqid_ds *msq;
+ /*
+ * If the IPC_KERNELD flag is set, the key is forced to IPC_PRIVATE,
+ * and a designated kerneld message queue is created/refered to
+ */
+ if ((msgflg & IPC_KERNELD)) {
+ if (!suser())
+ return -EPERM;
+ if ((kerneld_msqid == -1) && (kerneld_msqid =
+ newque(IPC_PRIVATE, msgflg & S_IRWXU)) >= 0)
+ kerneld_pid = current->pid;
+ return kerneld_msqid;
+ }
+ /* else it is a "normal" request */
if (key == IPC_PRIVATE)
return newque(key, msgflg);
if ((id = findkey (key)) == -1) { /* key not used */
@@ -433,9 +538,104 @@
if (!suser() && current->euid != ipcp->cuid &&
current->euid != ipcp->uid)
return -EPERM;
+ /*
+ * There is only one kerneld message queue,
+ * mark it as non-existant
+ */
+ if ((kerneld_msqid >= 0) && (msqid == kerneld_msqid))
+ kerneld_pid = kerneld_msqid = -1;
freeque (id);
return 0;
default:
return -EINVAL;
}
+}
+
+/*
+ * We do perhaps need a "flush" for waiting processes,
+ * so that if they are terminated, a call from do_exit
+ * will minimize the possibility of orphaned recieved
+ * messages in the queue. For now we just make sure
+ * that the queue is shut down whenever kerneld dies.
+ */
+void kerneld_exit(void)
+{
+ if ((current->pid == kerneld_pid) && (kerneld_msqid != -1))
+ sys_msgctl(kerneld_msqid, IPC_RMID, NULL);
+}
+
+/*
+ * Kerneld internal message format/syntax:
+ *
+ * The message type from the kernel to kerneld is used to specify _what_
+ * function we want kerneld to perform.
+ *
+ * The "normal" message area is divided into a long, followed by a char array.
+ * The long is used to hold the sequence number of the request, which will
+ * be used as the return message type from kerneld back to the kernel.
+ * In the return message, the long will be used to store the exit status
+ * of the kerneld "job", or task.
+ * The character array is used to pass parameters to kerneld and (optional)
+ * return information from kerneld back to the kernel.
+ * It is the responsibility of kerneld and the kernel level caller
+ * to set usable sizes on the parameter/return value array, since
+ * that information is _not_ included in the message format
+ */
+
+/*
+ * The basic kernel level entry point to kerneld.
+ * msgtype should correspond to a task type for (a) kerneld
+ * ret_size is the size of the (optional) return _value,
+ * OR-ed with KERNELD_WAIT if we want an answer
+ * msgsize is the size (in bytes) of the message, not including
+ * the long that is always sent first in a kerneld message
+ * text is the parameter for the kerneld specific task
+ * ret_val is NULL or the kernel address where an expected answer
+ * from kerneld should be placed.
+ *
+ * See <linux/kerneld.h> for usage (inline convenience functions)
+ *
+ */
+int kerneld_send(int msgtype, int ret_size, int msgsz,
+ const char *text, const char *ret_val)
+{
+ int status = -ENOSYS;
+#ifdef CONFIG_KERNELD
+ static int id = KERNELD_MINSEQ;
+ struct kerneld_msg kmsp = { msgtype, 0, (char *)text };
+ int msgflg = S_IRUSR | S_IWUSR | IPC_KERNELD | MSG_NOERROR;
+
+ msgsz += sizeof(long);
+ if (ret_size & KERNELD_WAIT) {
+ if (++id <= 0)
+ id = KERNELD_MINSEQ;
+ kmsp.id = id;
+ }
+
+ if (kerneld_msqid == -1)
+ return -ENODEV;
+
+ status = real_msgsnd(kerneld_msqid, (struct msgbuf *)&kmsp, msgsz, msgflg);
+ if ((status >= 0) && (ret_size & KERNELD_WAIT)) {
+ ret_size &= ~KERNELD_WAIT;
+ if (intr_count) {
+ /*
+ * Do not wait for an answer at interrupt-time!
+ * OK, so fake it...
+ * If the kerneld request failed in user-space
+ * we will find out eventually, and retry again!
+ */
+ return 0; /* i.e. say that it worked... */
+ }
+ /* else */
+ kmsp.text = (char *)ret_val;
+ status = real_msgrcv(kerneld_msqid, (struct msgbuf *)&kmsp,
+ sizeof(long) + ((ret_val)?ret_size:0),
+ kmsp.id, msgflg);
+ if (status > 0) /* a valid answer contains at least a long */
+ status = kmsp.id;
+ }
+
+#endif /* CONFIG_KERNELD */
+ return status;
}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov
with Sam's (original) version of this