diff options
author | Ken'ichi Ohmichi <oomichi@mxs.nes.nec.co.jp> | 2009-06-26 14:49:16 +0900 |
---|---|---|
committer | Dhaval Giani <dhaval@linux.vnet.ibm.com> | 2009-06-29 16:28:38 +0530 |
commit | 404007be1a80ab2ab75f64195fb692d520df0623 (patch) | |
tree | 33ac4d08d27a91188028ba857949d306e314cbba | |
parent | 7620ff3e067b8d77ddf321d81b4b19adc2729f6b (diff) | |
download | libcg-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.c | 188 | ||||
-rw-r--r-- | src/libcgroup-internal.h | 1 | ||||
-rw-r--r-- | src/libcgroup.map | 1 |
3 files changed, 190 insertions, 0 deletions
@@ -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; |