From 404007be1a80ab2ab75f64195fb692d520df0623 Mon Sep 17 00:00:00 2001 From: Ken'ichi Ohmichi Date: Fri, 26 Jun 2009 14:49:16 +0900 Subject: 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 Signed-off-by: Dhaval Giani --- src/api.c | 188 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 188 insertions(+) (limited to 'src/api.c') 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//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//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//cmdline. This function gets each argument and + * compares it to a process name taken from /proc//status. + * @param pid: The process id + * @param pname_status : The process name taken from /proc//status + * @param pname_cmdline: The process name taken from /proc//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//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//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//status. + */ + *procname = pname_status; + return 0; + } + if (!strncmp(pname_status, basename(buf), TASK_COMM_LEN - 1)) { + /* + * The taken process name from /proc//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//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//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; -- cgit