diff options
| author | Michael Stapelberg | 2013-11-09 14:34:12 +0100 | 
|---|---|---|
| committer | Michael Stapelberg | 2013-11-09 14:37:05 +0100 | 
| commit | 14b43bdec2af2737457a00e65ccc129025e508c1 (patch) | |
| tree | 27c491e5275925457e5bd9a82abd204af7d85ee1 /src | |
| parent | fix slurp(), it needs to read size-1 for the trailing NUL (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.
Diffstat (limited to 'src')
| -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;  } | 
