2 * vim:ts=4:sw=4:expandtab
10 #include <sys/types.h>
18 * Reads /proc/<pid>/stat and returns (via pointers) the name and parent pid of
20 * When false is returned, parsing failed and the contents of outname and
21 * outpid are undefined.
24 static bool parse_proc_stat(pid_t pid, char **outname, pid_t *outppid) {
26 /* the relevant contents (for us) are:
27 * <pid> (<program name>) <status> <ppid>
28 * which should well fit into one page of 4096 bytes */
31 if (snprintf(path, sizeof(path), "/proc/%d/stat", pid) == -1 ||
32 !slurp(path, buffer, sizeof(buffer)))
35 char *leftbracket = strchr(buffer, '(');
36 char *rightbracket = strrchr(buffer, ')');
39 sscanf(rightbracket + 2, "%*c %d", outppid) != 1)
42 *outname = strdup(leftbracket + 1);
46 static char *format_for_process(const char *name) {
47 if (strcasecmp(name, "i3bar") == 0)
49 else if (strcasecmp(name, "dzen2") == 0)
51 else if (strcasecmp(name, "xmobar") == 0)
58 * This function tries to automatically find out where i3status is being piped
59 * to and choses the appropriate output format.
61 * It is a little hackish but should work for most setups :).
63 * By iterating through /proc/<number>/stat and finding out the parent process
64 * id (just like pstree(1) or ps(1) work), we can get all children of our
65 * parent. When the output of i3status is being piped somewhere, the shell
66 * (parent process) spawns i3status and the destination process, so we will
67 * find our own process and the pipe target.
69 * We then check whether the pipe target’s name is known and chose the format.
72 char *auto_detect_format(void) {
73 /* If stdout is a tty, we output directly to a terminal. */
74 if (isatty(STDOUT_FILENO)) {
78 pid_t myppid = getppid();
79 pid_t mypid = getpid();
89 if (!parse_proc_stat(myppid, &parentname, &parentpid))
92 if (strcmp(parentname, "sh") == 0) {
93 pid_t tmp_ppid = parentpid;
95 fprintf(stderr, "i3status: auto-detection: parent process is \"sh\", looking at its parent\n");
96 if (!parse_proc_stat(tmp_ppid, &parentname, &parentpid))
100 /* Some shells, for example zsh, open a pipe in a way which will make the
101 * pipe target the parent process of i3status. If we detect that, we set
102 * the format and we are done. */
103 if ((format = format_for_process(parentname)) != NULL)
106 if (!(dir = opendir("/proc")))
109 while ((entry = readdir(dir)) != NULL) {
110 pid_t pid = (pid_t)atoi(entry->d_name);
111 if (pid == 0 || pid == mypid)
117 /* Now we need to find out the name of the process.
118 * To avoid the possible race condition of the process existing already
119 * but not executing the destination (shell after fork() and before
120 * exec()), we check if the name equals its parent.
122 * We try this for up to 0.5 seconds, then we give up.
125 /* give the scheduler a chance between each iteration, don’t hog
126 * the CPU too much */
132 if (!parse_proc_stat(pid, &name, &ppid))
136 } while (strcmp(parentname, name) == 0 && loopcnt++ < 10000);
141 /* Check for known destination programs and set format */
142 char *newfmt = format_for_process(name);
145 if (newfmt && format && strcmp(newfmt, format) != 0) {
146 fprintf(stderr, "i3status: cannot auto-configure, situation ambiguous (format \"%s\" *and* \"%s\" detected)\n", newfmt, format);