[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