summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKen'ichi Ohmichi <oomichi@mxs.nes.nec.co.jp>2009-06-26 14:49:16 +0900
committerDhaval Giani <dhaval@linux.vnet.ibm.com>2009-06-29 16:28:38 +0530
commit404007be1a80ab2ab75f64195fb692d520df0623 (patch)
tree33ac4d08d27a91188028ba857949d306e314cbba
parent7620ff3e067b8d77ddf321d81b4b19adc2729f6b (diff)
downloadlibcg-404007be1a80ab2ab75f64195fb692d520df0623.tar.gz
libcg-404007be1a80ab2ab75f64195fb692d520df0623.tar.xz
libcg-404007be1a80ab2ab75f64195fb692d520df0623.zip
Add cgroup_get_procname_from_procfs() for getting a process name.
Hi, Changelog of v6: ================ * Change the returning values of *_get_procname_from_proc*() to integer from charactor pointer. * Clarify the number meaning of string length in cg_get_procname_from_ ~proc_status() Changelog of v5: ================ * Rebase the patch to the latest code. Changelog of v4: ================ * Add the error handling for strdup()'s error. * Reduce strlen() calls. * Make the check code of a process name simple. Changelog of v3: ================ * Move cgroup_get_procname_from_procfs() to libcgroup-internal.h. * Fix unclear buffer of buf_cwd by memset(). * Get a real path of script file by realpath(). Changelog of v2: ================ * It is possible to handle a process, which name length is over than 16 characters, also. Description: ============ This patch adds a new function cgroup_get_procname_from_procfs() for getting a process name. This function allocates the memory for a process name, and writes the name to the memory, and returns the pointer of the memory. So a caller should free the memory if unusing it. The process name, which is wrotten by this function, depends on the specified process: If a command process) the full path of command. If a shell script process) the full path of shell script. If a kernel thread) the process name of kernel thread. Thanks Ken'ichi Ohmichi Signed-off-by: Ken'ichi Ohmichi <oomichi@mxs.nes.nec.co.jp> Signed-off-by: Dhaval Giani <dhaval@linux.vnet.ibm.com>
-rw-r--r--src/api.c188
-rw-r--r--src/libcgroup-internal.h1
-rw-r--r--src/libcgroup.map1
3 files changed, 190 insertions, 0 deletions
diff --git a/src/api.c b/src/api.c
index 03d7520..2d69162 100644
--- a/src/api.c
+++ b/src/api.c
@@ -65,6 +65,9 @@ __thread int last_errno;
/* the value have to be thread specific */
__thread char errtext[MAXLEN];
+/* Task command name length */
+#define TASK_COMM_LEN 16
+
/*
* Remember to bump this up for major API changes.
*/
@@ -2668,6 +2671,191 @@ int cgroup_get_uid_gid_from_procfs(pid_t pid, uid_t *euid, gid_t *egid)
return 0;
}
+/**
+ * Get process name from /proc/<pid>/status file.
+ * @param pid: The process id
+ * @param pname_status : The process name
+ * @return 0 on success, > 0 on error.
+ */
+static int cg_get_procname_from_proc_status(pid_t pid, char **procname_status)
+{
+ int ret = ECGFAIL;
+ int len;
+ FILE *f;
+ char path[FILENAME_MAX];
+ char buf[4092];
+
+ sprintf(path, "/proc/%d/status", pid);
+ f = fopen(path, "r");
+ if (!f)
+ return ECGROUPNOTEXIST;
+
+ while (fgets(buf, sizeof(buf), f)) {
+ if (!strncmp(buf, "Name:", 5)) {
+ len = strlen(buf);
+ if (buf[len - 1] == '\n')
+ buf[len - 1] = '\0';
+ *procname_status = strdup(buf + strlen("Name:") + 1);
+ if (*procname_status == NULL) {
+ last_errno = errno;
+ ret = ECGOTHER;
+ break;
+ }
+ ret = 0;
+ break;
+ }
+ }
+ fclose(f);
+ return ret;
+}
+
+/**
+ * Get process name from /proc/<pid>/cmdline file.
+ * This function is mainly for getting a script name (shell, perl,
+ * etc). A script name is written into the second or later argument
+ * of /proc/<pid>/cmdline. This function gets each argument and
+ * compares it to a process name taken from /proc/<pid>/status.
+ * @param pid: The process id
+ * @param pname_status : The process name taken from /proc/<pid>/status
+ * @param pname_cmdline: The process name taken from /proc/<pid>/cmdline
+ * @return 0 on success, > 0 on error.
+ */
+static int cg_get_procname_from_proc_cmdline(pid_t pid, char *pname_status,
+ char **pname_cmdline)
+{
+ FILE *f;
+ int ret = ECGFAIL;
+ int c = 0;
+ int len = 0;
+ char path[FILENAME_MAX];
+ char buf_pname[FILENAME_MAX];
+ char buf_cwd[FILENAME_MAX];
+
+ memset(buf_cwd, '\0', sizeof(buf_cwd));
+ sprintf(path, "/proc/%d/cwd", pid);
+ if (readlink(path, buf_cwd, sizeof(buf_cwd)) < 0)
+ return ECGROUPNOTEXIST;
+
+ sprintf(path, "/proc/%d/cmdline", pid);
+ f = fopen(path, "r");
+ if (!f)
+ return ECGROUPNOTEXIST;
+
+ while (c != EOF) {
+ c = fgetc(f);
+ if ((c != EOF) && (c != '\0')) {
+ buf_pname[len] = c;
+ len++;
+ continue;
+ }
+ buf_pname[len] = '\0';
+
+ /*
+ * The taken process name from /proc/<pid>/status is
+ * shortened to 15 characters if it is over. So the
+ * name should be compared by its length.
+ */
+ if (strncmp(pname_status, basename(buf_pname),
+ TASK_COMM_LEN - 1)) {
+ len = 0;
+ continue;
+ }
+ if (buf_pname[0] == '/') {
+ *pname_cmdline = strdup(buf_pname);
+ if (*pname_cmdline == NULL) {
+ last_errno = errno;
+ ret = ECGOTHER;
+ break;
+ }
+ ret = 0;
+ break;
+ } else {
+ strcat(buf_cwd, "/");
+ strcat(buf_cwd, buf_pname);
+ if (!realpath(buf_cwd, path)) {
+ last_errno = errno;
+ ret = ECGOTHER;
+ break;
+ }
+ *pname_cmdline = strdup(path);
+ if (*pname_cmdline == NULL) {
+ last_errno = errno;
+ ret = ECGOTHER;
+ break;
+ }
+ ret = 0;
+ break;
+ }
+ len = 0;
+ }
+ fclose(f);
+ return ret;
+}
+
+/**
+ * Get a process name from /proc file system.
+ * This function allocates memory for a process name, writes a process
+ * name onto it. So a caller should free the memory when unusing it.
+ * @param pid: The process id
+ * @param procname: The process name
+ * @return 0 on success, > 0 on error.
+ */
+int cgroup_get_procname_from_procfs(pid_t pid, char **procname)
+{
+ int ret;
+ char *pname_status;
+ char *pname_cmdline;
+ char path[FILENAME_MAX];
+ char buf[FILENAME_MAX];
+
+ ret = cg_get_procname_from_proc_status(pid, &pname_status);
+ if (ret)
+ return ret;
+
+ /*
+ * Get the full patch of process name from /proc/<pid>/exe.
+ */
+ memset(buf, '\0', sizeof(buf));
+ sprintf(path, "/proc/%d/exe", pid);
+ if (readlink(path, buf, sizeof(buf)) < 0) {
+ /*
+ * readlink() fails if a kernel thread, and a process
+ * name is taken from /proc/<pid>/status.
+ */
+ *procname = pname_status;
+ return 0;
+ }
+ if (!strncmp(pname_status, basename(buf), TASK_COMM_LEN - 1)) {
+ /*
+ * The taken process name from /proc/<pid>/status is
+ * shortened to 15 characters if it is over. So the
+ * name should be compared by its length.
+ */
+ free(pname_status);
+ *procname = strdup(buf);
+ if (*procname == NULL) {
+ last_errno = errno;
+ return ECGOTHER;
+ }
+ return 0;
+ }
+
+ /*
+ * The above strncmp() is not 0 if a shell script, because
+ * /proc/<pid>/exe links a shell command (/bin/bash etc.)
+ * and the pname_status represents a shell script name.
+ * Then the full path of a shell script is taken from
+ * /proc/<pid>/cmdline.
+ */
+ ret = cg_get_procname_from_proc_cmdline(pid, pname_status,
+ &pname_cmdline);
+ if (!ret)
+ *procname = pname_cmdline;
+
+ free(pname_status);
+ return ret;
+}
+
int cgroup_get_subsys_mount_point(char *controller, char **mount_point)
{
int i;
diff --git a/src/libcgroup-internal.h b/src/libcgroup-internal.h
index 95ff76c..fac82a1 100644
--- a/src/libcgroup-internal.h
+++ b/src/libcgroup-internal.h
@@ -100,6 +100,7 @@ struct cgroup_tree_handle {
/* Internal API */
char *cg_build_path(char *name, char *path, char *type);
int cgroup_get_uid_gid_from_procfs(pid_t pid, uid_t *euid, gid_t *egid);
+int cgroup_get_procname_from_procfs(pid_t pid, char **procname);
int cg_mkdir_p(const char *path);
diff --git a/src/libcgroup.map b/src/libcgroup.map
index 5359892..a101ca1 100644
--- a/src/libcgroup.map
+++ b/src/libcgroup.map
@@ -70,4 +70,5 @@ global:
cgroup_get_controller;
cgroup_get_uid_gid_from_procfs;
cgroup_get_subsys_mount_point;
+ cgroup_get_procname_from_procfs;
} CGROUP_0.33;