[Devel] [PATCH 2/2] signal c/r: implement /proc/pid/sig writing
Serge E. Hallyn
serue at us.ibm.com
Fri Jun 8 08:54:58 PDT 2007
>From 0fc34dcb54fe80ea720225825ad1dcb1b847d8ab Mon Sep 17 00:00:00 2001
From: Serge E. Hallyn <serue at us.ibm.com>
Date: Thu, 17 May 2007 17:28:07 -0400
Subject: [PATCH 2/2] signal c/r: implement /proc/pid/sig writing
Allow restore through writes to /proc/pid/sig/*, except for
the waiters file. Waits must be restored by the waiter actually
running wait.
To test writes to the action file I use the included handlertest.c.
Start it up in one terminal as
./handlertest 1
It should print "using handler 1"
in another terminal
pid=`ps -ef | grep handlertest | grep -v grep | awk '{ print $2 '}`
cat /proc/$pid/sig/action > action
kill -USR1 $pid
handlertest should print "handler 1 called"
You can repeat this with
./handlertest 2
to make sure it does what you expect with the second signal handler.
Restart handlertest with a different signal handler:
./handlertest 2
It prints "using handler 2"
Overwrite it's sigactions from another terminal
pid=`ps -ef | grep handlertest | grep -v grep | awk '{ print $2 '}`
cat action > /proc/$pid/sig/action
and send it a USR1
kill -USR1 $pid
and it should say "handler 1 called".
=====================================================================
handlertest.c
=====================================================================
void handler1(int sig)
{
printf("handler 1 called\n");
}
void handler2(int sig)
{
printf("handler 2 called\n");
}
int main(int argc, char *argv[])
{
int hnum;
__sighandler_t h = handler1;
if (argc<2) {
printf("usage: %s [1|2]\n", argv[0]);
exit(2);
}
hnum = atoi(argv[1]);
if (hnum == 1) {
printf("using handler 1\n");
} else {
h = handler2;
printf("using handler 2\n");
}
signal(SIGUSR1, h);
sleep(100);
}
Signed-off-by: Serge E. Hallyn <serue at us.ibm.com>
---
fs/proc/base.c | 97 +++++++++++++++++++++-
include/linux/signal.h | 2 +
kernel/signal.c | 218 +++++++++++++++++++++++++++++++++++++++++++++++-
3 files changed, 313 insertions(+), 4 deletions(-)
diff --git a/fs/proc/base.c b/fs/proc/base.c
index 8e0dd7a..22817bb 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -1701,9 +1701,104 @@ static ssize_t proc_pid_sig_read(struct file * file, char __user * buf,
return length;
}
+struct proc_pid_sig_partial {
+ char *buf;
+ loff_t pos;
+ size_t count;
+};
+
+static struct proc_pid_sig_partial *make_partial(char *page, int start, int total_count)
+{
+ struct proc_pid_sig_partial *partial;
+
+ partial = kzalloc(sizeof(*partial), GFP_KERNEL);
+ if (!partial)
+ return NULL;
+ partial->buf = page;
+ partial->pos = start;
+ partial->count = total_count;
+ return partial;
+}
+
+static ssize_t proc_pid_sig_write(struct file * file, const char __user * buf,
+ size_t count, loff_t *ppos)
+{
+ struct dentry * dentry = file->f_path.dentry;
+ struct inode * inode = dentry->d_inode;
+ char *page;
+ ssize_t length;
+ struct task_struct *task = get_proc_task(inode);
+ struct proc_pid_sig_partial *partial;
+ int partial_size = 0, total_count;
+
+ length = -ESRCH;
+ if (!task)
+ goto out_no_task;
+
+ partial = file->private_data;
+ file->private_data = NULL;
+ if (partial)
+ partial_size = partial->count - partial->pos;
+ total_count = count + partial_size;
+
+ printk(KERN_NOTICE "%s: 1\n", __FUNCTION__);
+ length = -ENOMEM;
+ page = kmalloc(total_count, GFP_USER);
+ if (!page)
+ goto out_put_task;
+ printk(KERN_NOTICE "%s: 2\n", __FUNCTION__);
+
+ if (partial) {
+ memcpy(page, partial->buf + partial->pos, partial_size);
+ kfree(partial->buf);
+ kfree(partial);
+ }
+
+ printk(KERN_NOTICE "%s: 3\n", __FUNCTION__);
+ length = -EFAULT;
+ if (copy_from_user(page+partial_size, buf, count))
+ goto out_free_page;
+
+ length = task_write_procsig(task,
+ (char*)file->f_path.dentry->d_name.name,
+ (void*)page, total_count);
+
+ printk(KERN_NOTICE "%s: 4, length was %d, count %d totcnt %d\n", __FUNCTION__,
+ length, count, total_count);
+ if (length >= 0 && length < total_count) {
+ /* should i just allocate a new buffer for just the unread portion? */
+ file->private_data = make_partial(page, length+1, total_count);
+ if (!file->private_data)
+ length = -ENOMEM;
+ else
+ goto out_put_task; /* don't free page, partial points to it */
+ }
+
+out_free_page:
+ kfree(page);
+out_put_task:
+ put_task_struct(task);
+out_no_task:
+ if (length >= 0)
+ return count;
+ return length;
+}
+
+static int proc_pid_sig_release(struct inode *inode, struct file *file)
+{
+ struct proc_pid_sig_partial *partial = file->private_data;
+
+ if (partial) {
+ kfree(partial->buf);
+ kfree(partial);
+ }
+ return 0;
+}
+
static const struct file_operations proc_sig_attr_operations = {
.read = proc_pid_sig_read,
-// .write = proc_pid_sig_write,
+ .write = proc_pid_sig_write,
+ .release = proc_pid_sig_release,
};
diff --git a/include/linux/signal.h b/include/linux/signal.h
index 6863543..2e6d64c 100644
--- a/include/linux/signal.h
+++ b/include/linux/signal.h
@@ -244,6 +244,8 @@ extern int get_signal_to_deliver(siginfo_t *info, struct k_sigaction *return_ka,
extern struct kmem_cache *sighand_cachep;
extern int task_read_procsig(struct task_struct *p, char *name, char **value);
+extern int task_write_procsig(struct task_struct *p, char *name, void *value,
+ size_t size);
/*
* In POSIX a signal is sent either to a specific thread (Linux task)
diff --git a/kernel/signal.c b/kernel/signal.c
index a2a3ebe..dc50e1b 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -2614,7 +2614,7 @@ static int print_sigpending_alloc(char **bufp, struct sigpending *pending)
list_for_each_entry(q, &pending->list, list) {
info = &q->info;
- if (p-buf+215 > alloced) {
+ if (p-buf+221 > alloced) {
int len=p-buf;
char *buf2;
alloced += PAGE_SIZE;
@@ -2629,8 +2629,8 @@ static int print_sigpending_alloc(char **bufp, struct sigpending *pending)
p = buf+len;
}
- p += sprintf(p, "sig %d: user %d flags %d",
- info->si_signo, (int)q->user->uid, q->flags);
+ p += sprintf(p, "sig %d: uid %d pid %d flags %d",
+ info->si_signo, (int)q->user->uid, info->si_pid, q->flags);
p += sprintf(p, " errno %d code %d\n",
info->si_errno, info->si_code);
@@ -2739,3 +2739,215 @@ int task_read_procsig(struct task_struct *p, char *name, char **value)
return ret;
}
+
+static int set_sigset(sigset_t *sig, char *value, int size)
+{
+ int i;
+
+ if (size < _NSIG)
+ return -EINVAL;
+ sigemptyset(sig);
+ for (i=0; i<_NSIG; i++)
+ if (value[i] == '1')
+ sigaddset(sig, i);
+ return 0;
+}
+
+static char *next_eol(char *s, int maxlen)
+{
+ int len=0;
+ while (len<maxlen && *s!='\0' && *s !='\n')
+ s++, len++;
+ if (len < maxlen)
+ return s;
+ return NULL;
+}
+
+static int set_sigpending(struct task_struct *t, struct sigpending *pending,
+ char *value, int size)
+{
+ struct sigqueue *q;
+ int ret;
+#ifdef __ARCH_SI_TRAPNO
+ int trapno;
+#endif
+
+ if (set_sigset(&pending->signal, value, size))
+ return -EINVAL;
+
+ size -= _NSIG+1;
+ value += _NSIG+1;
+ while (size) {
+ int signum, uid, pid, flags, errno, code, status, fd;
+ unsigned long utime, stime, addr, band;
+ char *start, *eol = next_eol(value, size);
+
+ if (!eol)
+ return 0;
+
+ size -= (eol - value) + 1;
+ start = eol+1;
+ ret = sscanf(value, "sig %d: uid %d pid %d flags %d errno %d code %d\n",
+ &signum, &uid, &pid, &flags, &errno, &code);
+ if (ret != 6)
+ goto out;
+ q = __sigqueue_alloc(t, GFP_KERNEL, 1);
+ if (!q)
+ goto out;
+ q->info.si_signo = signum;
+ q->info.si_errno = errno;
+ q->info.si_code = code;
+ q->info.si_uid = uid;
+ q->info.si_pid = pid;
+
+ switch(signum) {
+ /* XXX skipping posix1b timers and signals for now */
+ default: break;
+ case SIGKILL:
+ eol = next_eol(start, size);
+ if (!eol)
+ goto out;
+ size -= (eol - start) + 1;
+ ret = sscanf(start, " pid %d uid %d\n", &pid, &uid);
+ if (ret != 2)
+ goto out;
+ q->info._sifields._kill._pid = pid;
+ q->info._sifields._kill._uid = uid;
+ break;
+
+ case SIGCHLD:
+ eol = next_eol(start, size);
+ if (!eol)
+ goto out;
+ size -= (eol - start) + 1;
+ ret = sscanf(start, "pid %d uid %d status %d utime %lu stime %lu\n",
+ &pid, &uid, &status, &utime, &stime);
+ if (ret != 5)
+ goto out;
+ q->info._sifields._sigchld._pid = pid;
+ q->info._sifields._sigchld._uid = uid;
+ q->info._sifields._sigchld._status = status;
+ q->info._sifields._sigchld._utime = utime;
+ q->info._sifields._sigchld._stime = stime;
+ break;
+
+ case SIGILL:
+ case SIGFPE:
+ case SIGSEGV:
+ case SIGBUS:
+ eol = next_eol(start, size);
+ if (!eol)
+ goto out;
+ size -= (eol - start) + 1;
+#ifdef __ARCH_SI_TRAPNO
+ ret = sscanf(start, " addr %lu trapno %d\n", &addr, &trapno);
+ if (ret != 2)
+ goto out;
+ q->info._sifields._sigfault._trapno = trapno;
+#else
+ ret = sscanf(start, " addr %lu\n", &addr);
+ if (ret != 1)
+ goto out;
+#endif
+ q->info._sifields._sigfault._addr = (void __user *)addr;
+ break;
+
+ case SIGPOLL:
+ eol = next_eol(start, size);
+ if (!eol)
+ goto out;
+ size -= (eol - start) + 1;
+ ret = sscanf(start, " band %ld fd %d\n", &band, &fd);
+ if (ret != 2)
+ goto out;
+ q->info._sifields._sigpoll._band = band;
+ q->info._sifields._sigpoll._fd = fd;
+ break;
+ }
+ start = eol+1;
+ list_add_tail(&q->list, &pending->list);
+ }
+
+out:
+ return size;
+}
+
+static int set_sigaction_list(struct sighand_struct *sighand, char *value,
+ int size)
+{
+ struct k_sigaction *action;
+ char *eol;
+ int num, ret;
+ int origsize = size;
+ //char sa_mask[_NSIG+1];
+ char sa_mask[65];
+ unsigned long sa_flags, sa_handler;
+
+ while (size > 0 && (eol=next_eol(value, size))) {
+ sa_mask[64] = '\0';
+ ret = sscanf(value, "%d %lu %64s %lu\n",
+ &num, &sa_flags, sa_mask, &sa_handler);
+ if (ret != 4)
+ break;
+ if (num < 0 || num > _NSIG)
+ return -EINVAL;
+ action = &sighand->action[num];
+ action->sa.sa_flags = sa_flags;
+ set_sigset(&action->sa.sa_mask, sa_mask, _NSIG);
+ action->sa.sa_handler = (__sighandler_t) sa_handler;
+ size -= eol-value+1;
+ value = eol+1;
+ }
+
+ return origsize - size;
+}
+
+static int set_altstack(struct task_struct *p, char *value, int size)
+{
+ unsigned long sas_ss_sp;
+ size_t sas_ss_size;
+ int ret;
+
+ ret = sscanf(value, "%lu %zd\n", &sas_ss_sp, &sas_ss_size);
+ if (ret != 2)
+ return -EINVAL;
+ p->sas_ss_sp = sas_ss_sp;
+ p->sas_ss_size = sas_ss_size;
+
+ return size;
+}
+
+int task_write_procsig(struct task_struct *p, char *name, void *value,
+ size_t size)
+{
+ int ret;
+
+ if (current != p) {
+ ret = security_ptrace(current, p);
+ if (ret)
+ return ret;
+ }
+
+ ret = -EINVAL;
+
+ if (strcmp(name, "pending") == 0) {
+ /* not masking out blocked signals yet */
+ ret = set_sigpending(p, &p->pending, value, size);
+ } else if (strcmp(name, "shared_pending") == 0) {
+ ret = set_sigpending(p, &p->signal->shared_pending, value, size);
+ } else if (strcmp(name, "blocked") == 0) {
+ /* not masking out blocked signals yet */
+ ret = set_sigset(&p->blocked, value, size);
+ } else if (strcmp(name, "action") == 0) {
+ ret = set_sigaction_list(p->sighand, value, size);
+ } else if (strcmp(name, "altstack") == 0) {
+ ret = set_altstack(p, value, size);
+#if 0
+ } else if (strcmp(name, "waiters") == 0) {
+ /* reset this at the waiter's restart using do_wait */
+ ret = set_wait_info(p->signal, value, size);
+#endif
+ }
+
+ return ret;
+}
--
1.5.1.1.GIT
_______________________________________________
Containers mailing list
Containers at lists.linux-foundation.org
https://lists.linux-foundation.org/mailman/listinfo/containers
More information about the Devel
mailing list