]> git.sur5r.net Git - i3/i3/blob - i3bar/src/child.c
i3bar: check exit code of pipe()
[i3/i3] / i3bar / src / child.c
1 /*
2  * vim:ts=4:sw=4:expandtab
3  *
4  * i3bar - an xcb-based status- and ws-bar for i3
5  *
6  * © 2010-2011 Axel Wagner and contributors
7  *
8  * See file LICNSE for license information
9  *
10  * src/child.c: Getting Input for the statusline
11  *
12  */
13 #include <stdlib.h>
14 #include <unistd.h>
15 #include <sys/types.h>
16 #include <sys/wait.h>
17 #include <signal.h>
18 #include <stdio.h>
19 #include <fcntl.h>
20 #include <string.h>
21 #include <errno.h>
22 #include <err.h>
23 #include <ev.h>
24
25 #include "common.h"
26
27 /* Global variables for child_*() */
28 pid_t child_pid;
29
30 /* stdin- and sigchild-watchers */
31 ev_io    *stdin_io;
32 ev_child *child_sig;
33
34 /* The buffer statusline points to */
35 char *statusline_buffer = NULL;
36
37 /*
38  * Stop and free() the stdin- and sigchild-watchers
39  *
40  */
41 void cleanup() {
42     if (stdin_io != NULL) {
43         ev_io_stop(main_loop, stdin_io);
44         FREE(stdin_io);
45         FREE(statusline_buffer);
46         /* statusline pointed to memory within statusline_buffer */
47         statusline = NULL;
48     }
49
50     if (child_sig != NULL) {
51         ev_child_stop(main_loop, child_sig);
52         FREE(child_sig);
53     }
54 }
55
56 /*
57  * Callbalk for stdin. We read a line from stdin and store the result
58  * in statusline
59  *
60  */
61 void stdin_io_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
62     int fd = watcher->fd;
63     int n = 0;
64     int rec = 0;
65     int buffer_len = STDIN_CHUNK_SIZE;
66     char *buffer = smalloc(buffer_len);
67     buffer[0] = '\0';
68     while(1) {
69         n = read(fd, buffer + rec, buffer_len - rec);
70         if (n == -1) {
71             if (errno == EAGAIN) {
72                 /* remove trailing newline and finish up */
73                 buffer[rec-1] = '\0';
74                 break;
75             }
76             ELOG("read() failed!: %s\n", strerror(errno));
77             exit(EXIT_FAILURE);
78         }
79         if (n == 0) {
80             if (rec != 0) {
81                 /* remove trailing newline and finish up */
82                 buffer[rec-1] = '\0';
83             }
84
85             /* end of file, kill the watcher */
86             ELOG("stdin: received EOF\n");
87             cleanup();
88             draw_bars();
89             return;
90         }
91         rec += n;
92
93         if (rec == buffer_len) {
94             buffer_len += STDIN_CHUNK_SIZE;
95             buffer = srealloc(buffer, buffer_len);
96         }
97     }
98     if (*buffer == '\0') {
99         FREE(buffer);
100         return;
101     }
102     FREE(statusline_buffer);
103     statusline = statusline_buffer = buffer;
104     for (n = 0; buffer[n] != '\0'; ++n) {
105         if (buffer[n] == '\n')
106             statusline = &buffer[n + 1];
107     }
108     DLOG("%s\n", statusline);
109     draw_bars();
110 }
111
112 /*
113  * We received a sigchild, meaning, that the child-process terminated.
114  * We simply free the respective data-structures and don't care for input
115  * anymore
116  *
117  */
118 void child_sig_cb(struct ev_loop *loop, ev_child *watcher, int revents) {
119     ELOG("Child (pid: %d) unexpectedly exited with status %d\n",
120            child_pid,
121            watcher->rstatus);
122     cleanup();
123 }
124
125 /*
126  * Start a child-process with the specified command and reroute stdin.
127  * We actually start a $SHELL to execute the command so we don't have to care
128  * about arguments and such
129  *
130  */
131 void start_child(char *command) {
132     child_pid = 0;
133     if (command != NULL) {
134         int fd[2];
135         if (pipe(fd) == -1)
136             err(EXIT_FAILURE, "pipe(fd)");
137
138         child_pid = fork();
139         switch (child_pid) {
140             case -1:
141                 ELOG("Couldn't fork(): %s\n", strerror(errno));
142                 exit(EXIT_FAILURE);
143             case 0:
144                 /* Child-process. Reroute stdout and start shell */
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                 /* Parent-process. Rerout stdin */
158                 close(fd[1]);
159
160                 dup2(fd[0], STDIN_FILENO);
161
162                 /* If hide-on-modifier is set, we start of by sending the
163                  * child a SIGSTOP, because the bars aren't mapped at start */
164                 if (config.hide_on_modifier) {
165                     stop_child();
166                 }
167
168                 break;
169         }
170     }
171
172     /* We set O_NONBLOCK because blocking is evil in event-driven software */
173     fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK);
174
175     stdin_io = smalloc(sizeof(ev_io));
176     ev_io_init(stdin_io, &stdin_io_cb, STDIN_FILENO, EV_READ);
177     ev_io_start(main_loop, stdin_io);
178
179     /* We must cleanup, if the child unexpectedly terminates */
180     child_sig = smalloc(sizeof(ev_child));
181     ev_child_init(child_sig, &child_sig_cb, child_pid, 0);
182     ev_child_start(main_loop, child_sig);
183
184 }
185
186 /*
187  * kill()s the child-process (if existent) and closes and
188  * free()s the stdin- and sigchild-watchers
189  *
190  */
191 void kill_child() {
192     if (child_pid != 0) {
193         kill(child_pid, SIGCONT);
194         kill(child_pid, SIGTERM);
195         int status;
196         waitpid(child_pid, &status, 0);
197         child_pid = 0;
198         cleanup();
199     }
200 }
201
202 /*
203  * Sends a SIGSTOP to the child-process (if existent)
204  *
205  */
206 void stop_child() {
207     if (child_pid != 0) {
208         kill(child_pid, SIGSTOP);
209     }
210 }
211
212 /*
213  * Sends a SIGCONT to the child-process (if existent)
214  *
215  */
216 void cont_child() {
217     if (child_pid != 0) {
218         kill(child_pid, SIGCONT);
219     }
220 }