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