Hey there-
Currently, there exists no method for a process to query the resource
limits of another process. They can be inferred via some mechanisms but they
cannot be explicitly determined. Given that this information can be usefull to
know during the debugging of an application, I've written this patch which
exports all of a processes limits via /proc/<pid>/limits. Tested successfully
by myself on x86 on top of 2.6.23-rc2-mm1.
Thanks & Regards
Neil
Signed-off-by: Neil Horman <[email protected]>
base.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 65 insertions(+)
diff --git a/fs/proc/base.c b/fs/proc/base.c
index ed2b224..b3ddf08 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -74,6 +74,7 @@
#include <linux/nsproxy.h>
#include <linux/oom.h>
#include <linux/elf.h>
+#include <asm/resource.h>
#include "internal.h"
/* NOTE:
@@ -323,6 +324,68 @@ static int proc_oom_score(struct task_struct *task, char *buffer)
return sprintf(buffer, "%lu\n", points);
}
+struct limit_names {
+ char *name;
+ char *unit;
+};
+
+static struct limit_names lnames[RLIM_NLIMITS] = {
+ [RLIMIT_CPU] = {"Max cpu time", "ms"},
+ [RLIMIT_FSIZE] = {"Max file size", "bytes"},
+ [RLIMIT_DATA] = {"Max data size", "bytes"},
+ [RLIMIT_STACK] = {"Max stack size", "bytes"},
+ [RLIMIT_CORE] = {"Max core file size", "bytes"},
+ [RLIMIT_RSS] = {"Max resident set", "bytes"},
+ [RLIMIT_NPROC] = {"Max processes", "processes"},
+ [RLIMIT_NOFILE] = {"Max open files", "files"},
+ [RLIMIT_MEMLOCK] = {"Max locked memory", "bytes"},
+ [RLIMIT_AS] = {"Max address space", "bytes"},
+ [RLIMIT_LOCKS] = {"Max file locks", "locks"},
+ [RLIMIT_SIGPENDING] = {"Max pending signals", "signals"},
+ [RLIMIT_MSGQUEUE] = {"Max msgqueue size", "bytes"},
+ [RLIMIT_NICE] = {"Max nice priority", NULL},
+ [RLIMIT_RTPRIO] = {"Max realtime priority", NULL},
+};
+
+/* Display limits for a process */
+static int proc_pid_limits(struct task_struct *task, char *buffer)
+{
+ unsigned int i;
+ int count = 0;
+ char *bufptr = buffer;
+
+ struct rlimit rlim[RLIM_NLIMITS];
+
+ read_lock(&tasklist_lock);
+ memcpy(rlim, task->signal->rlim, (sizeof(struct rlimit) * RLIM_NLIMITS));
+ read_unlock(&tasklist_lock);
+
+ /*
+ * print the file header
+ */
+ count += sprintf(&bufptr[count], "%-25s %-20s %-20s %-10s\n",
+ "Limit","Soft Limit","Hard Limit","Units");
+
+ for (i=0; i < RLIM_NLIMITS; i++) {
+ if (rlim[i].rlim_cur == RLIM_INFINITY)
+ count += sprintf(&bufptr[count], "%-25s %-20s ", lnames[i].name,"unlimited");
+ else
+ count += sprintf(&bufptr[count], "%-25s %-20lu ", lnames[i].name, rlim[i].rlim_cur);
+
+ if (rlim[i].rlim_max == RLIM_INFINITY)
+ count += sprintf(&bufptr[count], "%-20s ","unlimited");
+ else
+ count += sprintf(&bufptr[count], "%-20lu ", rlim[i].rlim_max);
+
+ if (lnames[i].unit)
+ count += sprintf(&bufptr[count],"%-10s\n", lnames[i].unit);
+ else
+ count += sprintf(&bufptr[count],"\n");
+ }
+
+ return count;
+}
+
/************************************************************************/
/* Here the fs part begins */
/************************************************************************/
@@ -2017,6 +2080,7 @@ static const struct pid_entry tgid_base_stuff[] = {
INF("environ", S_IRUSR, pid_environ),
INF("auxv", S_IRUSR, pid_auxv),
INF("status", S_IRUGO, pid_status),
+ INF("limits", S_IRUGO, pid_limits),
#ifdef CONFIG_SCHED_DEBUG
REG("sched", S_IRUGO|S_IWUSR, pid_sched),
#endif
@@ -2310,6 +2374,7 @@ static const struct pid_entry tid_base_stuff[] = {
INF("environ", S_IRUSR, pid_environ),
INF("auxv", S_IRUSR, pid_auxv),
INF("status", S_IRUGO, pid_status),
+ INF("limits", S_IRUGO, pid_limits),
#ifdef CONFIG_SCHED_DEBUG
REG("sched", S_IRUGO|S_IWUSR, pid_sched),
#endif
--
/***************************************************
*Neil Horman
*Software Engineer
*Red Hat, Inc.
*[email protected]
*gpg keyid: 1024D / 0x92A74FA1
*http://pgp.mit.edu
***************************************************/
On Mon, 2007-08-13 at 10:00 -0400, Neil Horman wrote:
> Hey there-
> Currently, there exists no method for a process to query the resource
> limits of another process. They can be inferred via some mechanisms but they
> cannot be explicitly determined. Given that this information can be usefull to
> know during the debugging of an application, I've written this patch which
> exports all of a processes limits via /proc/<pid>/limits. Tested successfully
> by myself on x86 on top of 2.6.23-rc2-mm1.
since this information, by it's nature, is security sensitive, I would
really really strongly suggest that you make this restricted to those
processes that can ptrace the victim only... (which is basically "same
user or root")
Making this world readable is very much a bad thing to do
On Mon, 13 Aug 2007 10:00:44 EDT, Neil Horman said:
> Hey there-
> Currently, there exists no method for a process to query the resource
> limits of another process. They can be inferred via some mechanisms but they
> cannot be explicitly determined. Given that this information can be usefull to
> know during the debugging of an application, I've written this patch which
> exports all of a processes limits via /proc/<pid>/limits. Tested successfully
> by myself on x86 on top of 2.6.23-rc2-mm1.
> /************************************************************************/
> /* Here the fs part begins */
> /************************************************************************/
> @@ -2017,6 +2080,7 @@ static const struct pid_entry tgid_base_stuff[] = {
> INF("environ", S_IRUSR, pid_environ),
> INF("auxv", S_IRUSR, pid_auxv),
> INF("status", S_IRUGO, pid_status),
> + INF("limits", S_IRUGO, pid_limits),
Any takers for S_IRUSR instead? Either that, or lay out the use case for
making it S_IRUGO. (I'm OK on it being world-visible *if* there's a good
and sane reason for it)
> #ifdef CONFIG_SCHED_DEBUG
> REG("sched", S_IRUGO|S_IWUSR, pid_sched),
> #endif
> @@ -2310,6 +2374,7 @@ static const struct pid_entry tid_base_stuff[] = {
> INF("environ", S_IRUSR, pid_environ),
> INF("auxv", S_IRUSR, pid_auxv),
> INF("status", S_IRUGO, pid_status),
> + INF("limits", S_IRUGO, pid_limits),
Here too.
On Mon, Aug 13, 2007 at 12:47:38PM -0400, [email protected] wrote:
> On Mon, 13 Aug 2007 10:00:44 EDT, Neil Horman said:
> > Hey there-
> > Currently, there exists no method for a process to query the resource
> > limits of another process. They can be inferred via some mechanisms but they
> > cannot be explicitly determined. Given that this information can be usefull to
> > know during the debugging of an application, I've written this patch which
> > exports all of a processes limits via /proc/<pid>/limits. Tested successfully
> > by myself on x86 on top of 2.6.23-rc2-mm1.
>
> > /************************************************************************/
> > /* Here the fs part begins */
> > /************************************************************************/
> > @@ -2017,6 +2080,7 @@ static const struct pid_entry tgid_base_stuff[] = {
> > INF("environ", S_IRUSR, pid_environ),
> > INF("auxv", S_IRUSR, pid_auxv),
> > INF("status", S_IRUGO, pid_status),
> > + INF("limits", S_IRUGO, pid_limits),
>
> Any takers for S_IRUSR instead? Either that, or lay out the use case for
> making it S_IRUGO. (I'm OK on it being world-visible *if* there's a good
> and sane reason for it)
>
> > #ifdef CONFIG_SCHED_DEBUG
> > REG("sched", S_IRUGO|S_IWUSR, pid_sched),
> > #endif
> > @@ -2310,6 +2374,7 @@ static const struct pid_entry tid_base_stuff[] = {
> > INF("environ", S_IRUSR, pid_environ),
> > INF("auxv", S_IRUSR, pid_auxv),
> > INF("status", S_IRUGO, pid_status),
> > + INF("limits", S_IRUGO, pid_limits),
>
> Here too.
Given that making it S_IRUSR would still accomplish the goals that I set out
for, I certainly have no objections. New patch attached with permissions
changed.
Regards
Neil
Signed-off-by: Neil Horman <[email protected]>
base.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 65 insertions(+)
diff --git a/fs/proc/base.c b/fs/proc/base.c
index ed2b224..b3ddf08 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -74,6 +74,7 @@
#include <linux/nsproxy.h>
#include <linux/oom.h>
#include <linux/elf.h>
+#include <asm/resource.h>
#include "internal.h"
/* NOTE:
@@ -323,6 +324,68 @@ static int proc_oom_score(struct task_struct *task, char *buffer)
return sprintf(buffer, "%lu\n", points);
}
+struct limit_names {
+ char *name;
+ char *unit;
+};
+
+static struct limit_names lnames[RLIM_NLIMITS] = {
+ [RLIMIT_CPU] = {"Max cpu time", "ms"},
+ [RLIMIT_FSIZE] = {"Max file size", "bytes"},
+ [RLIMIT_DATA] = {"Max data size", "bytes"},
+ [RLIMIT_STACK] = {"Max stack size", "bytes"},
+ [RLIMIT_CORE] = {"Max core file size", "bytes"},
+ [RLIMIT_RSS] = {"Max resident set", "bytes"},
+ [RLIMIT_NPROC] = {"Max processes", "processes"},
+ [RLIMIT_NOFILE] = {"Max open files", "files"},
+ [RLIMIT_MEMLOCK] = {"Max locked memory", "bytes"},
+ [RLIMIT_AS] = {"Max address space", "bytes"},
+ [RLIMIT_LOCKS] = {"Max file locks", "locks"},
+ [RLIMIT_SIGPENDING] = {"Max pending signals", "signals"},
+ [RLIMIT_MSGQUEUE] = {"Max msgqueue size", "bytes"},
+ [RLIMIT_NICE] = {"Max nice priority", NULL},
+ [RLIMIT_RTPRIO] = {"Max realtime priority", NULL},
+};
+
+/* Display limits for a process */
+static int proc_pid_limits(struct task_struct *task, char *buffer)
+{
+ unsigned int i;
+ int count = 0;
+ char *bufptr = buffer;
+
+ struct rlimit rlim[RLIM_NLIMITS];
+
+ read_lock(&tasklist_lock);
+ memcpy(rlim, task->signal->rlim, (sizeof(struct rlimit) * RLIM_NLIMITS));
+ read_unlock(&tasklist_lock);
+
+ /*
+ * print the file header
+ */
+ count += sprintf(&bufptr[count], "%-25s %-20s %-20s %-10s\n",
+ "Limit","Soft Limit","Hard Limit","Units");
+
+ for (i=0; i < RLIM_NLIMITS; i++) {
+ if (rlim[i].rlim_cur == RLIM_INFINITY)
+ count += sprintf(&bufptr[count], "%-25s %-20s ", lnames[i].name,"unlimited");
+ else
+ count += sprintf(&bufptr[count], "%-25s %-20lu ", lnames[i].name, rlim[i].rlim_cur);
+
+ if (rlim[i].rlim_max == RLIM_INFINITY)
+ count += sprintf(&bufptr[count], "%-20s ","unlimited");
+ else
+ count += sprintf(&bufptr[count], "%-20lu ", rlim[i].rlim_max);
+
+ if (lnames[i].unit)
+ count += sprintf(&bufptr[count],"%-10s\n", lnames[i].unit);
+ else
+ count += sprintf(&bufptr[count],"\n");
+ }
+
+ return count;
+}
+
/************************************************************************/
/* Here the fs part begins */
/************************************************************************/
@@ -2017,6 +2080,7 @@ static const struct pid_entry tgid_base_stuff[] = {
INF("environ", S_IRUSR, pid_environ),
INF("auxv", S_IRUSR, pid_auxv),
INF("status", S_IRUGO, pid_status),
+ INF("limits", S_IRUSR, pid_limits),
#ifdef CONFIG_SCHED_DEBUG
REG("sched", S_IRUGO|S_IWUSR, pid_sched),
#endif
@@ -2310,6 +2374,7 @@ static const struct pid_entry tid_base_stuff[] = {
INF("environ", S_IRUSR, pid_environ),
INF("auxv", S_IRUSR, pid_auxv),
INF("status", S_IRUGO, pid_status),
+ INF("limits", S_IRUSR, pid_limits),
#ifdef CONFIG_SCHED_DEBUG
REG("sched", S_IRUGO|S_IWUSR, pid_sched),
#endif
--
/***************************************************
*Neil Horman
*Software Engineer
*Red Hat, Inc.
*[email protected]
*gpg keyid: 1024D / 0x92A74FA1
*http://pgp.mit.edu
***************************************************/
Hi Neil,
> +static struct limit_names lnames[RLIM_NLIMITS] = {
static const ...
may be better here.
Best Regards
Ingo Oeser
On Mon, Aug 13, 2007 at 09:25:45PM +0200, Ingo Oeser wrote:
> Hi Neil,
>
> > +static struct limit_names lnames[RLIM_NLIMITS] = {
> static const ...
>
> may be better here.
>
> Best Regards
>
> Ingo Oeser
No, objections, thats all read only data anyway. New patch attached
Regards
Neil
Signed-off-by: Neil Horman <[email protected]>
base.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 65 insertions(+)
diff --git a/fs/proc/base.c b/fs/proc/base.c
index ed2b224..b3ddf08 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -74,6 +74,7 @@
#include <linux/nsproxy.h>
#include <linux/oom.h>
#include <linux/elf.h>
+#include <asm/resource.h>
#include "internal.h"
/* NOTE:
@@ -323,6 +324,68 @@ static int proc_oom_score(struct task_struct *task, char *buffer)
return sprintf(buffer, "%lu\n", points);
}
+struct limit_names {
+ char *name;
+ char *unit;
+};
+
+static const struct limit_names lnames[RLIM_NLIMITS] = {
+ [RLIMIT_CPU] = {"Max cpu time", "ms"},
+ [RLIMIT_FSIZE] = {"Max file size", "bytes"},
+ [RLIMIT_DATA] = {"Max data size", "bytes"},
+ [RLIMIT_STACK] = {"Max stack size", "bytes"},
+ [RLIMIT_CORE] = {"Max core file size", "bytes"},
+ [RLIMIT_RSS] = {"Max resident set", "bytes"},
+ [RLIMIT_NPROC] = {"Max processes", "processes"},
+ [RLIMIT_NOFILE] = {"Max open files", "files"},
+ [RLIMIT_MEMLOCK] = {"Max locked memory", "bytes"},
+ [RLIMIT_AS] = {"Max address space", "bytes"},
+ [RLIMIT_LOCKS] = {"Max file locks", "locks"},
+ [RLIMIT_SIGPENDING] = {"Max pending signals", "signals"},
+ [RLIMIT_MSGQUEUE] = {"Max msgqueue size", "bytes"},
+ [RLIMIT_NICE] = {"Max nice priority", NULL},
+ [RLIMIT_RTPRIO] = {"Max realtime priority", NULL},
+};
+
+/* Display limits for a process */
+static int proc_pid_limits(struct task_struct *task, char *buffer)
+{
+ unsigned int i;
+ int count = 0;
+ char *bufptr = buffer;
+
+ struct rlimit rlim[RLIM_NLIMITS];
+
+ read_lock(&tasklist_lock);
+ memcpy(rlim, task->signal->rlim, (sizeof(struct rlimit) * RLIM_NLIMITS));
+ read_unlock(&tasklist_lock);
+
+ /*
+ * print the file header
+ */
+ count += sprintf(&bufptr[count], "%-25s %-20s %-20s %-10s\n",
+ "Limit","Soft Limit","Hard Limit","Units");
+
+ for (i=0; i < RLIM_NLIMITS; i++) {
+ if (rlim[i].rlim_cur == RLIM_INFINITY)
+ count += sprintf(&bufptr[count], "%-25s %-20s ", lnames[i].name,"unlimited");
+ else
+ count += sprintf(&bufptr[count], "%-25s %-20lu ", lnames[i].name, rlim[i].rlim_cur);
+
+ if (rlim[i].rlim_max == RLIM_INFINITY)
+ count += sprintf(&bufptr[count], "%-20s ","unlimited");
+ else
+ count += sprintf(&bufptr[count], "%-20lu ", rlim[i].rlim_max);
+
+ if (lnames[i].unit)
+ count += sprintf(&bufptr[count],"%-10s\n", lnames[i].unit);
+ else
+ count += sprintf(&bufptr[count],"\n");
+ }
+
+ return count;
+}
+
/************************************************************************/
/* Here the fs part begins */
/************************************************************************/
@@ -2017,6 +2080,7 @@ static const struct pid_entry tgid_base_stuff[] = {
INF("environ", S_IRUSR, pid_environ),
INF("auxv", S_IRUSR, pid_auxv),
INF("status", S_IRUGO, pid_status),
+ INF("limits", S_IRUSR, pid_limits),
#ifdef CONFIG_SCHED_DEBUG
REG("sched", S_IRUGO|S_IWUSR, pid_sched),
#endif
@@ -2310,6 +2374,7 @@ static const struct pid_entry tid_base_stuff[] = {
INF("environ", S_IRUSR, pid_environ),
INF("auxv", S_IRUSR, pid_auxv),
INF("status", S_IRUGO, pid_status),
+ INF("limits", S_IRUSR, pid_limits),
#ifdef CONFIG_SCHED_DEBUG
REG("sched", S_IRUGO|S_IWUSR, pid_sched),
#endif
--
/***************************************************
*Neil Horman
*Software Engineer
*Red Hat, Inc.
*[email protected]
*gpg keyid: 1024D / 0x92A74FA1
*http://pgp.mit.edu
***************************************************/
On Mon, Aug 13, 2007 at 04:11:30PM -0400, Neil Horman wrote:
> --- a/fs/proc/base.c
> +++ b/fs/proc/base.c
> @@ -323,6 +324,68 @@ static int proc_oom_score(struct task_struct *task, char *buffer)
> return sprintf(buffer, "%lu\n", points);
> }
>
> +struct limit_names {
> + char *name;
> + char *unit;
> +};
> +
> +static const struct limit_names lnames[RLIM_NLIMITS] = {
> + [RLIMIT_CPU] = {"Max cpu time", "ms"},
> + [RLIMIT_FSIZE] = {"Max file size", "bytes"},
> + [RLIMIT_DATA] = {"Max data size", "bytes"},
> + [RLIMIT_STACK] = {"Max stack size", "bytes"},
> + [RLIMIT_CORE] = {"Max core file size", "bytes"},
> + [RLIMIT_RSS] = {"Max resident set", "bytes"},
> + [RLIMIT_NPROC] = {"Max processes", "processes"},
> + [RLIMIT_NOFILE] = {"Max open files", "files"},
> + [RLIMIT_MEMLOCK] = {"Max locked memory", "bytes"},
> + [RLIMIT_AS] = {"Max address space", "bytes"},
> + [RLIMIT_LOCKS] = {"Max file locks", "locks"},
> + [RLIMIT_SIGPENDING] = {"Max pending signals", "signals"},
this and processes should be left empty methinks, because max signals is
in fact unitless.
> + [RLIMIT_MSGQUEUE] = {"Max msgqueue size", "bytes"},
> + [RLIMIT_NICE] = {"Max nice priority", NULL},
> + [RLIMIT_RTPRIO] = {"Max realtime priority", NULL},
> +};
aha! trailing whitespace!
> +static int proc_pid_limits(struct task_struct *task, char *buffer)
> +{
> + unsigned int i;
> + int count = 0;
> + char *bufptr = buffer;
> +
> + struct rlimit rlim[RLIM_NLIMITS];
> +
> + read_lock(&tasklist_lock);
> + memcpy(rlim, task->signal->rlim, (sizeof(struct rlimit) * RLIM_NLIMITS));
unneeded (, )
> + read_unlock(&tasklist_lock);
hmm, fork copies this under task lock of group leader and system calls
in sys.c too. What's up?
I'm sure it will give you nonsensical output because of wrong locks but
I haven't checked.
FWIW, it survived ~1 hour of cat /proc/*/limits, mini fork bombs, LTP,
gdb testsuite and one sooper sikrit proggie. on core2
On Tue, Aug 14, 2007 at 01:04:02AM +0400, Alexey Dobriyan wrote:
> On Mon, Aug 13, 2007 at 04:11:30PM -0400, Neil Horman wrote:
> > --- a/fs/proc/base.c
> > +++ b/fs/proc/base.c
> > @@ -323,6 +324,68 @@ static int proc_oom_score(struct task_struct *task, char *buffer)
> > return sprintf(buffer, "%lu\n", points);
> > }
> >
> > +struct limit_names {
> > + char *name;
> > + char *unit;
> > +};
> > +
> > +static const struct limit_names lnames[RLIM_NLIMITS] = {
> > + [RLIMIT_CPU] = {"Max cpu time", "ms"},
> > + [RLIMIT_FSIZE] = {"Max file size", "bytes"},
> > + [RLIMIT_DATA] = {"Max data size", "bytes"},
> > + [RLIMIT_STACK] = {"Max stack size", "bytes"},
> > + [RLIMIT_CORE] = {"Max core file size", "bytes"},
> > + [RLIMIT_RSS] = {"Max resident set", "bytes"},
> > + [RLIMIT_NPROC] = {"Max processes", "processes"},
> > + [RLIMIT_NOFILE] = {"Max open files", "files"},
> > + [RLIMIT_MEMLOCK] = {"Max locked memory", "bytes"},
> > + [RLIMIT_AS] = {"Max address space", "bytes"},
> > + [RLIMIT_LOCKS] = {"Max file locks", "locks"},
> > + [RLIMIT_SIGPENDING] = {"Max pending signals", "signals"},
>
> this and processes should be left empty methinks, because max signals is
> in fact unitless.
>
not sure I agree with that. According to the man page, RLIMIT_SIGPENDING is
counted in number of signals and RLIMIT_NPROC is counted as a number of
processes.
> > + [RLIMIT_MSGQUEUE] = {"Max msgqueue size", "bytes"},
> > + [RLIMIT_NICE] = {"Max nice priority", NULL},
> > + [RLIMIT_RTPRIO] = {"Max realtime priority", NULL},
> > +};
>
> aha! trailing whitespace!
>
Fixed :)
> > +static int proc_pid_limits(struct task_struct *task, char *buffer)
> > +{
> > + unsigned int i;
> > + int count = 0;
> > + char *bufptr = buffer;
> > +
> > + struct rlimit rlim[RLIM_NLIMITS];
> > +
> > + read_lock(&tasklist_lock);
> > + memcpy(rlim, task->signal->rlim, (sizeof(struct rlimit) * RLIM_NLIMITS));
>
> unneeded (, )
>
I prefer to explicitly show order of operations, but I'm wierd that way, so I'll
change it :)
> > + read_unlock(&tasklist_lock);
>
> hmm, fork copies this under task lock of group leader and system calls
> in sys.c too. What's up?
>
> I'm sure it will give you nonsensical output because of wrong locks but
> I haven't checked.
>
I don't think so, I borrowed the code there from sys_getrlimit, which also reds
task->signal->rlim (only for one element instead of the whole array, so if its
broken here it should be broken there, but I don't see how.
> FWIW, it survived ~1 hour of cat /proc/*/limits, mini fork bombs, LTP,
> gdb testsuite and one sooper sikrit proggie. on core2
>
Its survived for a few days for me (while true; do cat /proc/*/limits;done),
with not noticible side effects (other than a slightly higher CPU load :)).
New patch with the ()'s and whitespace fixed.
Regards
Neil
Signed-off-by: Neil Horman <[email protected]>
base.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 65 insertions(+)
diff --git a/fs/proc/base.c b/fs/proc/base.c
index ed2b224..b3ddf08 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -74,6 +74,7 @@
#include <linux/nsproxy.h>
#include <linux/oom.h>
#include <linux/elf.h>
+#include <asm/resource.h>
#include "internal.h"
/* NOTE:
@@ -323,6 +324,68 @@ static int proc_oom_score(struct task_struct *task, char *buffer)
return sprintf(buffer, "%lu\n", points);
}
+struct limit_names {
+ char *name;
+ char *unit;
+};
+
+static const struct limit_names lnames[RLIM_NLIMITS] = {
+ [RLIMIT_CPU] = {"Max cpu time", "ms"},
+ [RLIMIT_FSIZE] = {"Max file size", "bytes"},
+ [RLIMIT_DATA] = {"Max data size", "bytes"},
+ [RLIMIT_STACK] = {"Max stack size", "bytes"},
+ [RLIMIT_CORE] = {"Max core file size", "bytes"},
+ [RLIMIT_RSS] = {"Max resident set", "bytes"},
+ [RLIMIT_NPROC] = {"Max processes", "processes"},
+ [RLIMIT_NOFILE] = {"Max open files", "files"},
+ [RLIMIT_MEMLOCK] = {"Max locked memory", "bytes"},
+ [RLIMIT_AS] = {"Max address space", "bytes"},
+ [RLIMIT_LOCKS] = {"Max file locks", "locks"},
+ [RLIMIT_SIGPENDING] = {"Max pending signals", "signals"},
+ [RLIMIT_MSGQUEUE] = {"Max msgqueue size", "bytes"},
+ [RLIMIT_NICE] = {"Max nice priority", NULL},
+ [RLIMIT_RTPRIO] = {"Max realtime priority", NULL},
+};
+
+/* Display limits for a process */
+static int proc_pid_limits(struct task_struct *task, char *buffer)
+{
+ unsigned int i;
+ int count = 0;
+ char *bufptr = buffer;
+
+ struct rlimit rlim[RLIM_NLIMITS];
+
+ read_lock(&tasklist_lock);
+ memcpy(rlim, task->signal->rlim, sizeof(struct rlimit) * RLIM_NLIMITS);
+ read_unlock(&tasklist_lock);
+
+ /*
+ * print the file header
+ */
+ count += sprintf(&bufptr[count], "%-25s %-20s %-20s %-10s\n",
+ "Limit","Soft Limit","Hard Limit","Units");
+
+ for (i=0; i < RLIM_NLIMITS; i++) {
+ if (rlim[i].rlim_cur == RLIM_INFINITY)
+ count += sprintf(&bufptr[count], "%-25s %-20s ", lnames[i].name,"unlimited");
+ else
+ count += sprintf(&bufptr[count], "%-25s %-20lu ", lnames[i].name, rlim[i].rlim_cur);
+
+ if (rlim[i].rlim_max == RLIM_INFINITY)
+ count += sprintf(&bufptr[count], "%-20s ","unlimited");
+ else
+ count += sprintf(&bufptr[count], "%-20lu ", rlim[i].rlim_max);
+
+ if (lnames[i].unit)
+ count += sprintf(&bufptr[count],"%-10s\n", lnames[i].unit);
+ else
+ count += sprintf(&bufptr[count],"\n");
+ }
+
+ return count;
+}
+
/************************************************************************/
/* Here the fs part begins */
/************************************************************************/
@@ -2017,6 +2080,7 @@ static const struct pid_entry tgid_base_stuff[] = {
INF("environ", S_IRUSR, pid_environ),
INF("auxv", S_IRUSR, pid_auxv),
INF("status", S_IRUGO, pid_status),
+ INF("limits", S_IRUSR, pid_limits),
#ifdef CONFIG_SCHED_DEBUG
REG("sched", S_IRUGO|S_IWUSR, pid_sched),
#endif
@@ -2310,6 +2374,7 @@ static const struct pid_entry tid_base_stuff[] = {
INF("environ", S_IRUSR, pid_environ),
INF("auxv", S_IRUSR, pid_auxv),
INF("status", S_IRUGO, pid_status),
+ INF("limits", S_IRUSR, pid_limits),
#ifdef CONFIG_SCHED_DEBUG
REG("sched", S_IRUGO|S_IWUSR, pid_sched),
#endif
--
/***************************************************
*Neil Horman
*Software Engineer
*Red Hat, Inc.
*[email protected]
*gpg keyid: 1024D / 0x92A74FA1
*http://pgp.mit.edu
***************************************************/
Hey again-
Andrew requested that I repost this cleanly, after running the patch
through checkpatch. As requested here it is with the changelog.
Currently, there exists no method for a process to query the resource
limits of another process. They can be inferred via some mechanisms but they
cannot be explicitly determined. Given that this information can be usefull to
know during the debugging of an application, I've written this patch which
exports all of a processes limits via /proc/<pid>/limits.
Tested successfully by myself on x86 on top of 2.6.23-rc2-mm1.
Thanks & Regards
Neil
Signed-off-by: Neil Horman <[email protected]>
base.c | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 69 insertions(+)
diff --git a/fs/proc/base.c b/fs/proc/base.c
index ed2b224..4fb34a5 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -74,6 +74,7 @@
#include <linux/nsproxy.h>
#include <linux/oom.h>
#include <linux/elf.h>
+#include <linux/resource.h>
#include "internal.h"
/* NOTE:
@@ -323,6 +324,72 @@ static int proc_oom_score(struct task_struct *task, char *buffer)
return sprintf(buffer, "%lu\n", points);
}
+struct limit_names {
+ char *name;
+ char *unit;
+};
+
+static const struct limit_names lnames[RLIM_NLIMITS] = {
+ [RLIMIT_CPU] = {"Max cpu time", "ms"},
+ [RLIMIT_FSIZE] = {"Max file size", "bytes"},
+ [RLIMIT_DATA] = {"Max data size", "bytes"},
+ [RLIMIT_STACK] = {"Max stack size", "bytes"},
+ [RLIMIT_CORE] = {"Max core file size", "bytes"},
+ [RLIMIT_RSS] = {"Max resident set", "bytes"},
+ [RLIMIT_NPROC] = {"Max processes", "processes"},
+ [RLIMIT_NOFILE] = {"Max open files", "files"},
+ [RLIMIT_MEMLOCK] = {"Max locked memory", "bytes"},
+ [RLIMIT_AS] = {"Max address space", "bytes"},
+ [RLIMIT_LOCKS] = {"Max file locks", "locks"},
+ [RLIMIT_SIGPENDING] = {"Max pending signals", "signals"},
+ [RLIMIT_MSGQUEUE] = {"Max msgqueue size", "bytes"},
+ [RLIMIT_NICE] = {"Max nice priority", NULL},
+ [RLIMIT_RTPRIO] = {"Max realtime priority", NULL},
+};
+
+/* Display limits for a process */
+static int proc_pid_limits(struct task_struct *task, char *buffer)
+{
+ unsigned int i;
+ int count = 0;
+ char *bufptr = buffer;
+
+ struct rlimit rlim[RLIM_NLIMITS];
+
+ read_lock(&tasklist_lock);
+ memcpy(rlim, task->signal->rlim, sizeof(struct rlimit) * RLIM_NLIMITS);
+ read_unlock(&tasklist_lock);
+
+ /*
+ * print the file header
+ */
+ count += sprintf(&bufptr[count], "%-25s %-20s %-20s %-10s\n",
+ "Limit", "Soft Limit", "Hard Limit", "Units");
+
+ for (i = 0; i < RLIM_NLIMITS; i++) {
+ if (rlim[i].rlim_cur == RLIM_INFINITY)
+ count += sprintf(&bufptr[count], "%-25s %-20s ",
+ lnames[i].name, "unlimited");
+ else
+ count += sprintf(&bufptr[count], "%-25s %-20lu ",
+ lnames[i].name, rlim[i].rlim_cur);
+
+ if (rlim[i].rlim_max == RLIM_INFINITY)
+ count += sprintf(&bufptr[count], "%-20s ", "unlimited");
+ else
+ count += sprintf(&bufptr[count], "%-20lu ",
+ rlim[i].rlim_max);
+
+ if (lnames[i].unit)
+ count += sprintf(&bufptr[count], "%-10s\n",
+ lnames[i].unit);
+ else
+ count += sprintf(&bufptr[count], "\n");
+ }
+
+ return count;
+}
+
/************************************************************************/
/* Here the fs part begins */
/************************************************************************/
@@ -2017,6 +2084,7 @@ static const struct pid_entry tgid_base_stuff[] = {
INF("environ", S_IRUSR, pid_environ),
INF("auxv", S_IRUSR, pid_auxv),
INF("status", S_IRUGO, pid_status),
+ INF("limits", S_IRUSR, pid_limits),
#ifdef CONFIG_SCHED_DEBUG
REG("sched", S_IRUGO|S_IWUSR, pid_sched),
#endif
@@ -2310,6 +2378,7 @@ static const struct pid_entry tid_base_stuff[] = {
INF("environ", S_IRUSR, pid_environ),
INF("auxv", S_IRUSR, pid_auxv),
INF("status", S_IRUGO, pid_status),
+ INF("limits", S_IRUSR, pid_limits),
#ifdef CONFIG_SCHED_DEBUG
REG("sched", S_IRUGO|S_IWUSR, pid_sched),
#endif
--
/***************************************************
*Neil Horman
*Software Engineer
*Red Hat, Inc.
*[email protected]
*gpg keyid: 1024D / 0x92A74FA1
*http://pgp.mit.edu
***************************************************/
On Thu, 16 Aug 2007 08:35:38 EDT, Neil Horman said:
> Hey again-
> Andrew requested that I repost this cleanly, after running the patch
> through checkpatch. As requested here it is with the changelog.
>
> Currently, there exists no method for a process to query the resource
> limits of another process. They can be inferred via some mechanisms but they
> cannot be explicitly determined. Given that this information can be usefull
to
> know during the debugging of an application, I've written this patch which
> exports all of a processes limits via /proc/<pid>/limits.
>
> Tested successfully by myself on x86 on top of 2.6.23-rc2-mm1.
I had only one comment the first time around, and Neil addressed it.
I've also tested on x86_64 23-rc2-mm1, and it works here too. I saw where this
uses units of 'bytes' while the shell 'ulimit' uses 1024-byte units in some
places, but (a) this lists the units and (b) it's consistent with setrlimit().
Testing with values >4G show it's 64-bit clean as well.
One question: Is the units milliseconds, or seconds here:
+ [RLIMIT_CPU] = {"Max cpu time", "ms"},
Other than that, feel free to stick either/both of these on:
Reviewed-By: Valdis Kletnieks <[email protected]>
Tested-By: Valdis Kletnieks <[email protected]>
On Fri, Aug 17, 2007 at 04:09:26AM -0400, [email protected] wrote:
> On Thu, 16 Aug 2007 08:35:38 EDT, Neil Horman said:
> > Hey again-
> > Andrew requested that I repost this cleanly, after running the patch
> > through checkpatch. As requested here it is with the changelog.
> >
> > Currently, there exists no method for a process to query the resource
> > limits of another process. They can be inferred via some mechanisms but they
> > cannot be explicitly determined. Given that this information can be usefull
> to
> > know during the debugging of an application, I've written this patch which
> > exports all of a processes limits via /proc/<pid>/limits.
> >
> > Tested successfully by myself on x86 on top of 2.6.23-rc2-mm1.
>
> I had only one comment the first time around, and Neil addressed it.
>
> I've also tested on x86_64 23-rc2-mm1, and it works here too. I saw where this
> uses units of 'bytes' while the shell 'ulimit' uses 1024-byte units in some
> places, but (a) this lists the units and (b) it's consistent with setrlimit().
> Testing with values >4G show it's 64-bit clean as well.
>
> One question: Is the units milliseconds, or seconds here:
>
> + [RLIMIT_CPU] = {"Max cpu time", "ms"},
>
> Other than that, feel free to stick either/both of these on:
>
> Reviewed-By: Valdis Kletnieks <[email protected]>
> Tested-By: Valdis Kletnieks <[email protected]>
Oops, your right, Thanks. New patch
Currently, there exists no method for a process to query the resource
limits of another process. They can be inferred via some mechanisms but they
cannot be explicitly determined. Given that this information can be usefull to
know during the debugging of an application, I've written this patch which
exports all of a processes limits via /proc/<pid>/limits.
Thanks
Neil
Signed-off-by: Neil Horman <[email protected]>
base.c | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 69 insertions(+)
commit 95e91e3102d7963e63d68969b8db5694b83a6684
Author: Neil Horman <[email protected]>
Date: Thu Aug 16 08:25:59 2007 -0400
Readding proc_pid_limits patch with checkpatch.pl run
diff --git a/fs/proc/base.c b/fs/proc/base.c
index ed2b224..4fb34a5 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -74,6 +74,7 @@
#include <linux/nsproxy.h>
#include <linux/oom.h>
#include <linux/elf.h>
+#include <linux/resource.h>
#include "internal.h"
/* NOTE:
@@ -323,6 +324,72 @@ static int proc_oom_score(struct task_struct *task, char *buffer)
return sprintf(buffer, "%lu\n", points);
}
+struct limit_names {
+ char *name;
+ char *unit;
+};
+
+static const struct limit_names lnames[RLIM_NLIMITS] = {
+ [RLIMIT_CPU] = {"Max cpu time", "seconds"},
+ [RLIMIT_FSIZE] = {"Max file size", "bytes"},
+ [RLIMIT_DATA] = {"Max data size", "bytes"},
+ [RLIMIT_STACK] = {"Max stack size", "bytes"},
+ [RLIMIT_CORE] = {"Max core file size", "bytes"},
+ [RLIMIT_RSS] = {"Max resident set", "bytes"},
+ [RLIMIT_NPROC] = {"Max processes", "processes"},
+ [RLIMIT_NOFILE] = {"Max open files", "files"},
+ [RLIMIT_MEMLOCK] = {"Max locked memory", "bytes"},
+ [RLIMIT_AS] = {"Max address space", "bytes"},
+ [RLIMIT_LOCKS] = {"Max file locks", "locks"},
+ [RLIMIT_SIGPENDING] = {"Max pending signals", "signals"},
+ [RLIMIT_MSGQUEUE] = {"Max msgqueue size", "bytes"},
+ [RLIMIT_NICE] = {"Max nice priority", NULL},
+ [RLIMIT_RTPRIO] = {"Max realtime priority", NULL},
+};
+
+/* Display limits for a process */
+static int proc_pid_limits(struct task_struct *task, char *buffer)
+{
+ unsigned int i;
+ int count = 0;
+ char *bufptr = buffer;
+
+ struct rlimit rlim[RLIM_NLIMITS];
+
+ read_lock(&tasklist_lock);
+ memcpy(rlim, task->signal->rlim, sizeof(struct rlimit) * RLIM_NLIMITS);
+ read_unlock(&tasklist_lock);
+
+ /*
+ * print the file header
+ */
+ count += sprintf(&bufptr[count], "%-25s %-20s %-20s %-10s\n",
+ "Limit", "Soft Limit", "Hard Limit", "Units");
+
+ for (i = 0; i < RLIM_NLIMITS; i++) {
+ if (rlim[i].rlim_cur == RLIM_INFINITY)
+ count += sprintf(&bufptr[count], "%-25s %-20s ",
+ lnames[i].name, "unlimited");
+ else
+ count += sprintf(&bufptr[count], "%-25s %-20lu ",
+ lnames[i].name, rlim[i].rlim_cur);
+
+ if (rlim[i].rlim_max == RLIM_INFINITY)
+ count += sprintf(&bufptr[count], "%-20s ", "unlimited");
+ else
+ count += sprintf(&bufptr[count], "%-20lu ",
+ rlim[i].rlim_max);
+
+ if (lnames[i].unit)
+ count += sprintf(&bufptr[count], "%-10s\n",
+ lnames[i].unit);
+ else
+ count += sprintf(&bufptr[count], "\n");
+ }
+
+ return count;
+}
+
/************************************************************************/
/* Here the fs part begins */
/************************************************************************/
@@ -2017,6 +2084,7 @@ static const struct pid_entry tgid_base_stuff[] = {
INF("environ", S_IRUSR, pid_environ),
INF("auxv", S_IRUSR, pid_auxv),
INF("status", S_IRUGO, pid_status),
+ INF("limits", S_IRUSR, pid_limits),
#ifdef CONFIG_SCHED_DEBUG
REG("sched", S_IRUGO|S_IWUSR, pid_sched),
#endif
@@ -2310,6 +2378,7 @@ static const struct pid_entry tid_base_stuff[] = {
INF("environ", S_IRUSR, pid_environ),
INF("auxv", S_IRUSR, pid_auxv),
INF("status", S_IRUGO, pid_status),
+ INF("limits", S_IRUSR, pid_limits),
#ifdef CONFIG_SCHED_DEBUG
REG("sched", S_IRUGO|S_IWUSR, pid_sched),
#endif
--
/***************************************************
*Neil Horman
*Software Engineer
*Red Hat, Inc.
*[email protected]
*gpg keyid: 1024D / 0x92A74FA1
*http://pgp.mit.edu
***************************************************/
On Fri, 17 Aug 2007 06:59:18 -0400
Neil Horman <[email protected]> wrote:
> Currently, there exists no method for a process to query the resource
> limits of another process. They can be inferred via some mechanisms but they
> cannot be explicitly determined. Given that this information can be usefull to
> know during the debugging of an application, I've written this patch which
> exports all of a processes limits via /proc/<pid>/limits.
I'm struggling with this a bit. Sure, it _might_ be handy on some
occasions to be able to get at this information. But I've never seen
anyone ask for it before, and it _is_ determinable by other means, if only
strace.
How do we justify adding yet more stuff to the kernel?
On Fri, 17 Aug 2007 12:45:47 PDT, Andrew Morton said:
> On Fri, 17 Aug 2007 06:59:18 -0400
> Neil Horman <[email protected]> wrote:
>
> > Currently, there exists no method for a process to query the resource
> > limits of another process. They can be inferred via some mechanisms but they
> > cannot be explicitly determined. Given that this information can be usefull to
> > know during the debugging of an application, I've written this patch which
> > exports all of a processes limits via /proc/<pid>/limits.
>
> I'm struggling with this a bit. Sure, it _might_ be handy on some
> occasions to be able to get at this information. But I've never seen
> anyone ask for it before, and it _is_ determinable by other means, if only
> strace.
Most of the times *I*'ve struggled with this, it's been a case of "this program
forks that one that calls a PAM module that does this and then double-forks
yadda yadda". So I know where Neil is coming from.
> How do we justify adding yet more stuff to the kernel?
I looked the code over, and *if* we want to do it, the code looks good.
Making that final call is why they pay you and Linus the big bucks. :)
On Fri, Aug 17, 2007 at 12:45:47PM -0700, Andrew Morton wrote:
> On Fri, 17 Aug 2007 06:59:18 -0400
> Neil Horman <[email protected]> wrote:
>
> > Currently, there exists no method for a process to query the resource
> > limits of another process. They can be inferred via some mechanisms but they
> > cannot be explicitly determined. Given that this information can be usefull to
> > know during the debugging of an application, I've written this patch which
> > exports all of a processes limits via /proc/<pid>/limits.
>
> I'm struggling with this a bit. Sure, it _might_ be handy on some
> occasions to be able to get at this information. But I've never seen
> anyone ask for it before, and it _is_ determinable by other means, if only
> strace.
>
> How do we justify adding yet more stuff to the kernel?
Well, In the past yar and a half I've been asked for it twice. Granted thats
not alot of demand, but neither is the code particularly invasive or complex.
If your concerned about bloat, my first thought would be to add a compile time
config option, which I'm certainly willing to add if the consensus is to do so,
but honestly I thought this was small enough that it didnt' really need it.
As previously mentioned though, the final say as to what is important enough to
make it into the kernel is up to you and Linus. I think this is worthwhile, but
if I'm in the minority on that, I'll certainly understand
Thanks & Regards
Neil
--
/***************************************************
*Neil Horman
*Software Engineer
*Red Hat, Inc.
*[email protected]
*gpg keyid: 1024D / 0x92A74FA1
*http://pgp.mit.edu
***************************************************/
On Fri, Aug 17, 2007 at 12:45:47PM -0700, Andrew Morton wrote:
> On Fri, 17 Aug 2007 06:59:18 -0400
> Neil Horman <[email protected]> wrote:
> > Currently, there exists no method for a process to query the resource
> > limits of another process. They can be inferred via some mechanisms
> > but they cannot be explicitly determined. Given that this
> > information can be usefull to know during the debugging of an
> > application, I've written this patch which exports all of a
> > processes limits via /proc/<pid>/limits.
>
> I'm struggling with this a bit. Sure, it _might_ be handy on some
> occasions to be able to get at this information. But I've never seen
> anyone ask for it before, and it _is_ determinable by other means, if only
> strace.
I've wanted this information on multiple occasions in the past and was
mystified that there was no way to determine it. And no, I don't feel
that strace is an answer -- given a running process, how do I use strace
to find out what its current ulimits are?
-andy
On Tue, Aug 21, 2007 at 11:56:11AM -0700, Andy Isaacson wrote:
> On Fri, Aug 17, 2007 at 12:45:47PM -0700, Andrew Morton wrote:
> > On Fri, 17 Aug 2007 06:59:18 -0400
> > Neil Horman <[email protected]> wrote:
> > > Currently, there exists no method for a process to query the resource
> > > limits of another process. They can be inferred via some mechanisms
> > > but they cannot be explicitly determined. Given that this
> > > information can be usefull to know during the debugging of an
> > > application, I've written this patch which exports all of a
> > > processes limits via /proc/<pid>/limits.
> >
> > I'm struggling with this a bit. Sure, it _might_ be handy on some
> > occasions to be able to get at this information. But I've never seen
> > anyone ask for it before, and it _is_ determinable by other means, if only
> > strace.
>
> I've wanted this information on multiple occasions in the past and was
> mystified that there was no way to determine it. And no, I don't feel
> that strace is an answer -- given a running process, how do I use strace
> to find out what its current ulimits are?
You stop it and force it to execute rlimit(2) in its context, of
course! What could be simpler?
The reason we never see questions about this is because relatively few
people are using limits. Instead we see weekly questions about fork
bombs.
Frankly, I'd rather see new syscalls to get and set limits on other
processes in the same way we can set priorities, affinities, etc.. But
there are a couple dragons lurking there..
--
Mathematics is the supreme nostalgia of our time.
On Wed, Aug 22, 2007 at 09:40:37PM -0500, Matt Mackall wrote:
> On Tue, Aug 21, 2007 at 11:56:11AM -0700, Andy Isaacson wrote:
> > On Fri, Aug 17, 2007 at 12:45:47PM -0700, Andrew Morton wrote:
> > > On Fri, 17 Aug 2007 06:59:18 -0400
> > > Neil Horman <[email protected]> wrote:
> > > > Currently, there exists no method for a process to query the resource
> > > > limits of another process. They can be inferred via some mechanisms
> > > > but they cannot be explicitly determined. Given that this
> > > > information can be usefull to know during the debugging of an
> > > > application, I've written this patch which exports all of a
> > > > processes limits via /proc/<pid>/limits.
> > >
> > > I'm struggling with this a bit. Sure, it _might_ be handy on some
> > > occasions to be able to get at this information. But I've never seen
> > > anyone ask for it before, and it _is_ determinable by other means, if only
> > > strace.
> >
> > I've wanted this information on multiple occasions in the past and was
> > mystified that there was no way to determine it. And no, I don't feel
> > that strace is an answer -- given a running process, how do I use strace
> > to find out what its current ulimits are?
>
> You stop it and force it to execute rlimit(2) in its context, of
> course! What could be simpler?
>
I would think reading a file in /proc would be simpler :), especially if you're
in a position where attaching to the process through ptrace isnt feasible. For
instance, if you were an application running as the result of a core dump when
/proc/sys/kernel/core_pattern were a pipe, preforming your operation wouldn't be
particularly possible. Likewise, if your a sysadmin and you want to check what
your processes limits were, you could follow your method, but I would think you
would much rather avoid needing to send several ptrace commands to your
commands, interrupting its work if you didn't have to.
> The reason we never see questions about this is because relatively few
> people are using limits. Instead we see weekly questions about fork
> bombs.
>
But we do see questions about this. I've had a few requests, and at least two
more people have popped up on this thread supporting it.
Regards
Neil
--
/***************************************************
*Neil Horman
*Software Engineer
*Red Hat, Inc.
*[email protected]
*gpg keyid: 1024D / 0x92A74FA1
*http://pgp.mit.edu
***************************************************/