diff options
author | Michael Stapelberg <michael@stapelberg.de> | 2013-11-09 14:34:12 +0100 |
---|---|---|
committer | Michael Stapelberg <michael@stapelberg.de> | 2013-11-09 14:37:05 +0100 |
commit | 14b43bdec2af2737457a00e65ccc129025e508c1 (patch) | |
tree | 27c491e5275925457e5bd9a82abd204af7d85ee1 | |
parent | 31509b0d5654baf2e2c3438cae90c30e3dd00c17 (diff) |
format detection: simplify code, handle "sh" processes in the hierarchy
i3 starts processes using /bin/sh now, not $SHELL. This increases the
likelihood with which we are started by dash, which tends to leave its
processes in the hierarchy, e.g.:
michael 1524 i3bar --bar_id=bar-0 --socket=/run/user/1000/i3/ipc-s
michael 1525 \_ /bin/sh -c i3status
michael 1526 \_ i3status
This case is now handled correctly — when the parent is “sh”, the parent
of sh will be used instead.
-rw-r--r-- | src/auto_detect_format.c | 124 |
1 files changed, 61 insertions, 63 deletions
diff --git a/src/auto_detect_format.c b/src/auto_detect_format.c index 524e2e9..5e17e17 100644 --- a/src/auto_detect_format.c +++ b/src/auto_detect_format.c @@ -15,6 +15,46 @@ #include "i3status.h" /* + * Reads /proc/<pid>/stat and returns (via pointers) the name and parent pid of + * the specified pid. + * When false is returned, parsing failed and the contents of outname and + * outpid are undefined. + * + */ +static bool parse_proc_stat(pid_t pid, char **outname, pid_t *outppid) { + char path[255]; + /* the relevant contents (for us) are: + * <pid> (<program name>) <status> <ppid> + * which should well fit into one page of 4096 bytes */ + char buffer[4096]; + + if (snprintf(path, sizeof(path), "/proc/%d/stat", pid) == -1 || + !slurp(path, buffer, sizeof(buffer))) + return false; + + char *leftbracket = strchr(buffer, '('); + char *rightbracket = strrchr(buffer, ')'); + if (!leftbracket || + !rightbracket || + sscanf(rightbracket + 2, "%*c %d", outppid) != 1) + return false; + *rightbracket = '\0'; + *outname = strdup(leftbracket + 1); + return true; +} + +static char *format_for_process(const char *name) { + if (strcasecmp(name, "i3bar") == 0) + return "i3bar"; + else if (strcasecmp(name, "dzen2") == 0) + return "dzen2"; + else if (strcasecmp(name, "xmobar") == 0) + return "xmobar"; + else + return NULL; +} + +/* * This function tries to automatically find out where i3status is being piped * to and choses the appropriate output format. * @@ -40,68 +80,37 @@ char *auto_detect_format(void) { DIR *dir; struct dirent *entry; - char path[255]; - /* the relevant contents (for us) are: - * <pid> (<program name>) <status> <ppid> - * which should well fit into one page of 4096 bytes */ - char buffer[4096]; char *format = NULL; - char *parentname = NULL; + char *parentname; + pid_t parentpid; - if (!(dir = opendir("/proc"))) + if (!parse_proc_stat(myppid, &parentname, &parentpid)) return NULL; - /* First pass: get the executable name of the parent. - * Upon error, we directly return NULL as we cannot continue without the - * name of our parent process. */ - while ((entry = readdir(dir)) != NULL) { - pid_t pid = (pid_t)atoi(entry->d_name); - if (pid != myppid) - continue; - - if (snprintf(path, sizeof(path), "/proc/%d/stat", pid) == -1 || - !slurp(path, buffer, 4095)) - goto out; - - buffer[4095] = '\0'; - char *leftbracket = strchr(buffer, '('); - char *rightbracket = strrchr(buffer, ')'); - if (!leftbracket || - !rightbracket || - !(parentname = malloc((rightbracket - leftbracket)))) - goto out; - *rightbracket = '\0'; - strcpy(parentname, leftbracket + 1); + if (strcmp(parentname, "sh") == 0) { + pid_t tmp_ppid = parentpid; + free(parentname); + fprintf(stderr, "i3status: auto-detection: parent process is \"sh\", looking at its parent\n"); + if (!parse_proc_stat(tmp_ppid, &parentname, &parentpid)) + return NULL; } - if (!parentname) - goto out; - /* Some shells, for example zsh, open a pipe in a way which will make the * pipe target the parent process of i3status. If we detect that, we set * the format and we are done. */ - if (strcasecmp(parentname, "i3bar") == 0) - format = "i3bar"; - else if (strcasecmp(parentname, "dzen2") == 0) - format = "dzen2"; - else if (strcasecmp(parentname, "xmobar") == 0) - format = "xmobar"; - - if (format) + if ((format = format_for_process(parentname)) != NULL) goto out; - rewinddir(dir); + if (!(dir = opendir("/proc"))) + goto out; while ((entry = readdir(dir)) != NULL) { pid_t pid = (pid_t)atoi(entry->d_name); if (pid == 0 || pid == mypid) continue; - if (snprintf(path, sizeof(path), "/proc/%d/stat", pid) == -1) - continue; - char *name = NULL; pid_t ppid; int loopcnt = 0; @@ -115,34 +124,23 @@ char *auto_detect_format(void) { do { /* give the scheduler a chance between each iteration, don’t hog * the CPU too much */ - if (name) + if (name) { usleep(50); + free(name); + } - if (!slurp(path, buffer, 4095)) + if (!parse_proc_stat(pid, &name, &ppid)) break; - buffer[4095] = '\0'; - char *leftbracket = strchr(buffer, '('); - char *rightbracket = strrchr(buffer, ')'); - if (!leftbracket || - !rightbracket || - sscanf(rightbracket + 2, "%*c %d", &ppid) != 1 || - ppid != myppid) + if (ppid != myppid) break; - *rightbracket = '\0'; - name = leftbracket + 1; } while (strcmp(parentname, name) == 0 && loopcnt++ < 10000); if (!name) continue; /* Check for known destination programs and set format */ - char *newfmt = NULL; - if (strcasecmp(name, "i3bar") == 0) - newfmt = "i3bar"; - else if (strcasecmp(name, "dzen2") == 0) - newfmt = "dzen2"; - else if (strcasecmp(name, "xmobar") == 0) - newfmt = "xmobar"; + char *newfmt = format_for_process(name); + free(name); if (newfmt && format && strcmp(newfmt, format) != 0) { fprintf(stderr, "i3status: cannot auto-configure, situation ambiguous (format \"%s\" *and* \"%s\" detected)\n", newfmt, format); @@ -153,11 +151,11 @@ char *auto_detect_format(void) { } } + closedir(dir); + out: if (parentname) free(parentname); - closedir(dir); - return format; } |