2 * vim:ts=4:sw=4:expandtab
10 #include <sys/types.h>
18 * This function tries to automatically find out where i3status is being piped
19 * to and choses the appropriate output format.
21 * It is a little hackish but should work for most setups :).
23 * By iterating through /proc/<number>/stat and finding out the parent process
24 * id (just like pstree(1) or ps(1) work), we can get all children of our
25 * parent. When the output of i3status is being piped somewhere, the shell
26 * (parent process) spawns i3status and the destination process, so we will
27 * find our own process and the pipe target.
29 * We then check whether the pipe target’s name is known and chose the format.
32 char *auto_detect_format(void) {
33 /* If stdout is a tty, we output directly to a terminal. */
34 if (isatty(STDOUT_FILENO)) {
38 pid_t myppid = getppid();
39 pid_t mypid = getpid();
44 /* the relevant contents (for us) are:
45 * <pid> (<program name>) <status> <ppid>
46 * which should well fit into one page of 4096 bytes */
51 char *parentname = NULL;
53 if (!(dir = opendir("/proc")))
56 /* First pass: get the executable name of the parent.
57 * Upon error, we directly return NULL as we cannot continue without the
58 * name of our parent process. */
59 while ((entry = readdir(dir)) != NULL) {
60 pid_t pid = (pid_t)atoi(entry->d_name);
64 if (snprintf(path, sizeof(path), "/proc/%d/stat", pid) == -1 ||
65 !slurp(path, buffer, 4095))
69 char *leftbracket = strchr(buffer, '(');
70 char *rightbracket = strrchr(buffer, ')');
73 !(parentname = malloc((rightbracket - leftbracket))))
76 strcpy(parentname, leftbracket + 1);
82 /* Some shells, for example zsh, open a pipe in a way which will make the
83 * pipe target the parent process of i3status. If we detect that, we set
84 * the format and we are done. */
85 if (strcasecmp(parentname, "i3bar") == 0)
87 else if (strcasecmp(parentname, "dzen2") == 0)
89 else if (strcasecmp(parentname, "xmobar") == 0)
97 while ((entry = readdir(dir)) != NULL) {
98 pid_t pid = (pid_t)atoi(entry->d_name);
99 if (pid == 0 || pid == mypid)
102 if (snprintf(path, sizeof(path), "/proc/%d/stat", pid) == -1)
108 /* Now we need to find out the name of the process.
109 * To avoid the possible race condition of the process existing already
110 * but not executing the destination (shell after fork() and before
111 * exec()), we check if the name equals its parent.
113 * We try this for up to 0.5 seconds, then we give up.
116 /* give the scheduler a chance between each iteration, don’t hog
117 * the CPU too much */
121 if (!slurp(path, buffer, 4095))
124 char *leftbracket = strchr(buffer, '(');
125 char *rightbracket = strrchr(buffer, ')');
128 sscanf(rightbracket + 2, "%*c %d", &ppid) != 1 ||
131 *rightbracket = '\0';
132 name = leftbracket + 1;
133 } while (strcmp(parentname, name) == 0 && loopcnt++ < 10000);
138 /* Check for known destination programs and set format */
140 if (strcasecmp(name, "i3bar") == 0)
142 else if (strcasecmp(name, "dzen2") == 0)
144 else if (strcasecmp(name, "xmobar") == 0)
147 if (newfmt && format && strcmp(newfmt, format) != 0) {
148 fprintf(stderr, "i3status: cannot auto-configure, situation ambiguous (format \"%s\" *and* \"%s\" detected)\n", newfmt, format);