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