]> git.sur5r.net Git - i3/i3/blob - i3bar/src/child.c
Add licensing information
[i3/i3] / i3bar / src / child.c
1 /*
2  * i3bar - an xcb-based status- and ws-bar for i3
3  *
4  * © 2010 Axel Wagner and contributors
5  *
6  * See file LICNSE for license information
7  *
8  * src/child.c: Getting Input for the statusline
9  *
10  */
11 #include <stdlib.h>
12 #include <unistd.h>
13 #include <sys/types.h>
14 #include <signal.h>
15 #include <stdio.h>
16 #include <fcntl.h>
17 #include <string.h>
18 #include <errno.h>
19 #include <ev.h>
20
21 #include "common.h"
22
23 /* stdin- and sigchild-watchers */
24 ev_io    *stdin_io;
25 ev_child *child_sig;
26
27 /*
28  * Stop and free() the stdin- and sigchild-watchers
29  *
30  */
31 void cleanup() {
32     ev_io_stop(main_loop, stdin_io);
33     ev_child_stop(main_loop, child_sig);
34     FREE(stdin_io);
35     FREE(child_sig);
36     FREE(statusline);
37 }
38
39 /*
40  * Since we don't use colors and stuff, we strip the dzen-formatstrings
41  *
42  */
43 void strip_dzen_formats(char *buffer) {
44     char *src = buffer;
45     char *dest = buffer;
46     while (*src != '\0') {
47         /* ^ starts a format-string, ) ends it */
48         if (*src == '^') {
49             /* We replace the seperators from i3status by pipe-symbols */
50             if (!strncmp(src, "^ro", strlen("^ro"))) {
51                 *(dest++) = ' ';
52                 *(dest++) = '|';
53                 *(dest++) = ' ';
54             }
55             while (*src != ')') {
56                 src++;
57             }
58             src++;
59         } else {
60             *dest = *src;
61             src++;
62             dest++;
63         }
64     }
65     /* The last character is \n, which xcb cannot display */
66     *(--dest) = '\0';
67 }
68
69 /*
70  * Callbalk for stdin. We read a line from stdin, strip dzen-formats and store
71  * the result in statusline
72  *
73  */
74 void stdin_io_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
75     int fd = watcher->fd;
76     int n = 0;
77     int rec = 0;
78     int buffer_len = STDIN_CHUNK_SIZE;
79     char *buffer = malloc(buffer_len);
80     memset(buffer, '\0', buffer_len);
81     while(1) {
82         n = read(fd, buffer + rec, buffer_len - rec);
83         if (n == -1) {
84             if (errno == EAGAIN) {
85                 break;
86             }
87             printf("ERROR: read() failed!");
88             exit(EXIT_FAILURE);
89         }
90         if (n == 0) {
91             if (rec == buffer_len) {
92                 char *tmp = buffer;
93                 buffer = malloc(buffer_len + STDIN_CHUNK_SIZE);
94                 memset(buffer, '\0', buffer_len);
95                 strncpy(buffer, tmp, buffer_len);
96                 buffer_len += STDIN_CHUNK_SIZE;
97                 FREE(tmp);
98             } else {
99                 break;
100             }
101         }
102         rec += n;
103     }
104     if (strlen(buffer) == 0) {
105         FREE(buffer);
106         return;
107     }
108     strip_dzen_formats(buffer);
109     FREE(statusline);
110     statusline = buffer;
111     printf("%s\n", buffer);
112     draw_bars();
113 }
114
115 /*
116  * We received a sigchild, meaning, that the child-process terminated.
117  * We simply free the respective data-structures and don't care for input
118  * anymore
119  *
120  */
121 void child_sig_cb(struct ev_loop *loop, ev_child *watcher, int revents) {
122     printf("Child (pid: %d) unexpectedly exited with status %d\n",
123            child_pid,
124            watcher->rstatus);
125     cleanup();
126 }
127
128 /*
129  * Start a child-process with the specified command and reroute stdin.
130  * We actually start a $SHELL to execute the command so we don't have to care
131  * about arguments and such
132  *
133  */
134 void start_child(char *command) {
135     child_pid = 0;
136     if (command != NULL) {
137         int fd[2];
138         pipe(fd);
139         child_pid = fork();
140         switch (child_pid) {
141             case -1:
142                 printf("ERROR: Couldn't fork()");
143                 exit(EXIT_FAILURE);
144             case 0:
145                 close(fd[0]);
146
147                 dup2(fd[1], STDOUT_FILENO);
148
149                 static const char *shell = NULL;
150
151                 if ((shell = getenv("SHELL")) == NULL)
152                     shell = "/bin/sh";
153
154                 execl(shell, shell, "-c", command, (char*) NULL);
155                 return;
156             default:
157                 close(fd[1]);
158
159                 dup2(fd[0], STDIN_FILENO);
160
161                 break;
162         }
163     }
164
165     fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK);
166
167     stdin_io = malloc(sizeof(ev_io));
168     ev_io_init(stdin_io, &stdin_io_cb, STDIN_FILENO, EV_READ);
169     ev_io_start(main_loop, stdin_io);
170
171     /* We must cleanup, if the child unexpectedly terminates */
172     child_sig = malloc(sizeof(ev_io));
173     ev_child_init(child_sig, &child_sig_cb, child_pid, 0);
174     ev_child_start(main_loop, child_sig);
175
176 }
177
178 /*
179  * kill()s the child-prozess (if existend) and closes and
180  * free()s the stdin- and sigchild-watchers
181  *
182  */
183 void kill_child() {
184     if (child_pid != 0) {
185         kill(child_pid, SIGQUIT);
186     }
187     cleanup();
188 }