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.
This commit is contained in:
parent
31509b0d56
commit
14b43bdec2
@ -14,6 +14,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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user