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