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