writable /proc/XXX/cmdline to change ps name (patch)

Alan Cox (peeter_joot@VNET.IBM.COM)
Sat, 14 Nov 1998 12:55:32 -0500 (EST)


Hello,

Attached below is a patch that allows /proc/<pid>/cmdline to be writable.
Some other operating systems have mechanisms that allow you to change the
name that shows up when you do a ps (AIX -- write into argv[0], HP -- via
pstat()). I didn't think that it would be too hard to allow the
same thing on linux via /proc/, and gave it a try.

This is useful when you have a process that forks a lot of children, and
you want to tell what is what so you can attach the debugger to the right
process.

Would anybody interested take a look and give me any comments. It works
but I wanted to know if anybody sees any potential problems here. Note that
the string that you write into cmdline will be limited to the size of the
entire original argument list.

I have tried backporting this to the 2.0 kernels and it doesn't
work there. I had to address the vfs differences in write_array,
and the fact that an explicit memcpy_fromfs was required to get the string,
but after handling this stuff I get a trap when I try to write to the
address where the cmdline strings are stored (put_arg). Does this data get put
into a read-only page on 2.0 or something like that?

Peeter

--
Peeter Joot                                          peeterj@ca.ibm.com

--- fs/proc/base.c 1998/11/07 22:17:26 1.1 +++ fs/proc/base.c 1998/11/07 22:17:47 @@ -134,7 +134,7 @@ }; static struct proc_dir_entry proc_pid_cmdline = { PROC_PID_CMDLINE, 7, "cmdline", - S_IFREG | S_IRUGO, 1, 0, 0, + S_IFREG | S_IRUGO | S_IWUSR, 1, 0, 0, 0, &proc_array_inode_operations, NULL, proc_pid_fill_inode, }; --- fs/proc/array.c 1998/11/07 22:17:26 1.1 +++ fs/proc/array.c 1998/11/07 22:17:47 @@ -446,6 +446,37 @@ return result; }

+static void put_arg(struct task_struct *p, unsigned long start, + unsigned long end, const char * buffer, size_t count) +{ + unsigned long addr; + int size = 0; + + if (start >= end) + return; + + end--; // don't go into mm->env_start + if (count > end-start) + count = end-start; // null terminate + + for (;;) { + addr = get_phys_addr(p, start); + if (!addr) + return; + do { + if (size < count) + *(char *) addr = buffer[size]; + else + *(char *) addr = '\0'; + size++; + addr++; + start++; + if (start >= end) + return; + } while (addr & ~PAGE_MASK); + } +} + static int get_env(int pid, char * buffer) { struct task_struct *p; @@ -1408,10 +1439,50 @@ return count; }

+static ssize_t array_write( struct file *filp, const char *buf, size_t count, + loff_t *ppos) +{ + struct inode * inode = filp->f_dentry->d_inode; + int pid; + int type; + struct task_struct *p; + + if (!count) + return 0; + + if (!inode) + return -EINVAL; + + type = inode->i_ino; + pid = type >> 16; + type &= 0x0000ffff; + + if (!S_ISREG(inode->i_mode)) + return -EINVAL; + + if (!inode->i_mode & S_IWUSR) + return -EINVAL; + + // the only entry we want to be able to modify is cmdline. + if (type != PROC_PID_CMDLINE) + return -EINVAL; + + read_lock(&tasklist_lock); + p = find_task_by_pid(pid); + + if (p && p->mm) + put_arg(p, p->mm->arg_start, p->mm->arg_end, buf, count); + read_unlock(&tasklist_lock); + + *ppos = count; + + return count; +} + static struct file_operations proc_array_operations = { NULL, /* array_lseek */ array_read, - NULL, /* array_write */ + array_write, NULL, /* array_readdir */ NULL, /* array_poll */ NULL, /* array_ioctl */

- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.rutgers.edu Please read the FAQ at http://www.tux.org/lkml/