]> git.sur5r.net Git - i3/i3/blob - i3bar/src/child.c
Merge branch 'master' into next
[i3/i3] / i3bar / src / child.c
1 /*
2  * i3bar - an xcb-based status- and ws-bar for i3
3  *
4  * © 2010-2011 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 <sys/wait.h>
15 #include <signal.h>
16 #include <stdio.h>
17 #include <fcntl.h>
18 #include <string.h>
19 #include <errno.h>
20 #include <ev.h>
21
22 #include "common.h"
23
24 /* Global variables for child_*() */
25 pid_t child_pid;
26
27 /* stdin- and sigchild-watchers */
28 ev_io    *stdin_io;
29 ev_child *child_sig;
30
31 /* The buffer statusline points to */
32 char *statusline_buffer = NULL;
33
34 /*
35  * Stop and free() the stdin- and sigchild-watchers
36  *
37  */
38 void cleanup() {
39     if (stdin_io != NULL) {
40         ev_io_stop(main_loop, stdin_io);
41         FREE(stdin_io);
42         FREE(statusline_buffer);
43     }
44
45     if (child_sig != NULL) {
46         ev_child_stop(main_loop, child_sig);
47         FREE(child_sig);
48     }
49 }
50
51 /*
52  * Callbalk for stdin. We read a line from stdin and store the result
53  * in statusline
54  *
55  */
56 void stdin_io_cb(struct ev_loop *loop, ev_io *watcher, int revents) {
57     int fd = watcher->fd;
58     int n = 0;
59     int rec = 0;
60     int buffer_len = STDIN_CHUNK_SIZE;
61     char *buffer = malloc(buffer_len);
62     buffer[0] = '\0';
63     while(1) {
64         n = read(fd, buffer + rec, buffer_len - rec);
65         if (n == -1) {
66             if (errno == EAGAIN) {
67                 /* remove trailing newline and finish up */
68                 buffer[rec-1] = '\0';
69                 break;
70             }
71             ELOG("read() failed!: %s\n", strerror(errno));
72             exit(EXIT_FAILURE);
73         }
74         if (n == 0) {
75             if (rec != 0) {
76                 /* remove trailing newline and finish up */
77                 buffer[rec-1] = '\0';
78             }
79
80             /* end of file, kill the watcher */
81             DLOG("stdin: EOF\n");
82             cleanup();
83             break;
84         }
85         rec += n;
86
87         if (rec == buffer_len) {
88             buffer_len += STDIN_CHUNK_SIZE;
89             buffer = realloc(buffer, buffer_len);
90         }
91     }
92     if (*buffer == '\0') {
93         FREE(buffer);
94         return;
95     }
96     FREE(statusline_buffer);
97     statusline = statusline_buffer = buffer;
98     for (n = 0; buffer[n] != '\0'; ++n) {
99         if (buffer[n] == '\n')
100             statusline = &buffer[n + 1];
101     }
102     DLOG("%s\n", statusline);
103     draw_bars();
104 }
105
106 /*
107  * We received a sigchild, meaning, that the child-process terminated.
108  * We simply free the respective data-structures and don't care for input
109  * anymore
110  *
111  */
112 void child_sig_cb(struct ev_loop *loop, ev_child *watcher, int revents) {
113     DLOG("Child (pid: %d) unexpectedly exited with status %d\n",
114            child_pid,
115            watcher->rstatus);
116     cleanup();
117 }
118
119 /*
120  * Start a child-process with the specified command and reroute stdin.
121  * We actually start a $SHELL to execute the command so we don't have to care
122  * about arguments and such
123  *
124  */
125 void start_child(char *command) {
126     child_pid = 0;
127     if (command != NULL) {
128         int fd[2];
129         pipe(fd);
130         child_pid = fork();
131         switch (child_pid) {
132             case -1:
133                 ELOG("Couldn't fork(): %s\n", strerror(errno));
134                 exit(EXIT_FAILURE);
135             case 0:
136                 /* Child-process. Reroute stdout and start shell */
137                 close(fd[0]);
138
139                 dup2(fd[1], STDOUT_FILENO);
140
141                 static const char *shell = NULL;
142
143                 if ((shell = getenv("SHELL")) == NULL)
144                     shell = "/bin/sh";
145
146                 execl(shell, shell, "-c", command, (char*) NULL);
147                 return;
148             default:
149                 /* Parent-process. Rerout stdin */
150                 close(fd[1]);
151
152                 dup2(fd[0], STDIN_FILENO);
153
154                 /* If hide-on-modifier is set, we start of by sending the
155                  * child a SIGSTOP, because the bars aren't mapped at start */
156                 if (config.hide_on_modifier) {
157                     stop_child();
158                 }
159
160                 break;
161         }
162     }
163
164     /* We set O_NONBLOCK because blocking is evil in event-driven software */
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_child));
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-process (if existent) 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, SIGCONT);
186         kill(child_pid, SIGTERM);
187         int status;
188         waitpid(child_pid, &status, 0);
189         child_pid = 0;
190         cleanup();
191     }
192 }
193
194 /*
195  * Sends a SIGSTOP to the child-process (if existent)
196  *
197  */
198 void stop_child() {
199     if (child_pid != 0) {
200         kill(child_pid, SIGSTOP);
201     }
202 }
203
204 /*
205  * Sends a SIGCONT to the child-process (if existent)
206  *
207  */
208 void cont_child() {
209     if (child_pid != 0) {
210         kill(child_pid, SIGCONT);
211     }
212 }