]> git.ozlabs.org Git - ccan/blob - tools/ccanlint/async.c
tools: use rbuf instead of grab_file.
[ccan] / tools / ccanlint / async.c
1 #include "ccanlint.h"
2 #include "../tools.h"
3 #include <sys/types.h>
4 #include <sys/time.h>
5 #include <sys/resource.h>
6 #include <sys/wait.h>
7 #include <sys/stat.h>
8 #include <fcntl.h>
9 #include <stdlib.h>
10 #include <unistd.h>
11 #include <err.h>
12 #include <assert.h>
13 #include <ccan/lbalance/lbalance.h>
14 #include <ccan/tlist/tlist.h>
15 #include <ccan/time/time.h>
16 #include <ccan/talloc/talloc.h>
17
18 static struct lbalance *lb;
19 TLIST_TYPE(command, struct command);
20 static struct tlist_command pending = TLIST_INIT(pending);
21 static struct tlist_command running = TLIST_INIT(running);
22 static unsigned int num_running = 0;
23 static struct tlist_command done = TLIST_INIT(done);
24
25 struct command {
26         struct list_node list;
27         char *command;
28         pid_t pid;
29         int output_fd;
30         unsigned int time_ms;
31         struct lbalance_task *task;
32         int status;
33         char *output;
34         bool done;
35         const void *ctx;
36 };
37
38 static void killme(int sig)
39 {
40         kill(-getpid(), SIGKILL);
41 }
42
43 static void run_more(void)
44 {
45         struct command *c;
46
47         while (num_running < lbalance_target(lb)) {
48                 int p[2];
49
50                 c = tlist_top(&pending, list);
51                 if (!c)
52                         break;
53
54                 fflush(stdout);
55                 if (pipe(p) != 0)
56                         err(1, "Pipe failed");
57                 c->pid = fork();
58                 if (c->pid == -1)
59                         err(1, "Fork failed");
60                 if (c->pid == 0) {
61                         struct itimerval itim;
62
63                         if (dup2(p[1], STDOUT_FILENO) != STDOUT_FILENO
64                             || dup2(p[1], STDERR_FILENO) != STDERR_FILENO
65                             || close(p[0]) != 0
66                             || close(STDIN_FILENO) != 0
67                             || open("/dev/null", O_RDONLY) != STDIN_FILENO)
68                                 exit(128);
69
70                         signal(SIGALRM, killme);
71                         itim.it_interval.tv_sec = itim.it_interval.tv_usec = 0;
72                         itim.it_value = timespec_to_timeval(time_from_msec(c->time_ms));
73                         setitimer(ITIMER_REAL, &itim, NULL);
74
75                         c->status = system(c->command);
76                         if (WIFEXITED(c->status))
77                                 exit(WEXITSTATUS(c->status));
78                         /* Here's a hint... */
79                         exit(128 + WTERMSIG(c->status));
80                 }
81
82                 if (tools_verbose)
83                         printf("Running async: %s => %i\n", c->command, c->pid);
84
85                 close(p[1]);
86                 c->output_fd = p[0];
87                 c->task = lbalance_task_new(lb);
88                 tlist_del_from(&pending, c, list);
89                 tlist_add_tail(&running, c, list);
90                 num_running++;
91         }
92 }
93
94 static int destroy_command(struct command *command)
95 {
96         if (!command->done && command->pid) {
97                 kill(-command->pid, SIGKILL);
98                 close(command->output_fd);
99                 num_running--;
100         }
101
102         tlist_del(command, list);
103         return 0;
104 }
105
106 void run_command_async(const void *ctx, unsigned int time_ms,
107                        const char *fmt, ...)
108 {
109         struct command *command;
110         va_list ap;
111
112         assert(ctx);
113
114         if (!lb)
115                 lb = lbalance_new();
116
117         command = talloc(ctx, struct command);
118         command->ctx = ctx;
119         command->time_ms = time_ms;
120         command->pid = 0;
121         command->output = talloc_strdup(command, "");
122         va_start(ap, fmt);
123         command->command = talloc_vasprintf(command, fmt, ap);
124         va_end(ap);
125         tlist_add_tail(&pending, command, list);
126         command->done = false;
127         talloc_set_destructor(command, destroy_command);
128
129         run_more();
130 }
131
132 static void reap_output(void)
133 {
134         fd_set in;
135         struct command *c, *next;
136         int max_fd = 0;
137
138         FD_ZERO(&in);
139
140         tlist_for_each(&running, c, list) {
141                 FD_SET(c->output_fd, &in);
142                 if (c->output_fd > max_fd)
143                         max_fd = c->output_fd;
144         }
145
146         if (select(max_fd+1, &in, NULL, NULL, NULL) < 0)
147                 err(1, "select failed");
148
149         tlist_for_each_safe(&running, c, next, list) {
150                 if (FD_ISSET(c->output_fd, &in)) {
151                         int old_len, len;
152                         /* This length includes nul terminator! */
153                         old_len = talloc_array_length(c->output);
154                         c->output = talloc_realloc(c, c->output, char,
155                                                    old_len + 1024);
156                         len = read(c->output_fd, c->output + old_len - 1, 1024);
157                         if (len < 0)
158                                 err(1, "Reading from async command");
159                         c->output = talloc_realloc(c, c->output, char,
160                                                    old_len + len);
161                         c->output[old_len + len - 1] = '\0';
162                         if (len == 0) {
163                                 struct rusage ru;
164                                 wait4(c->pid, &c->status, 0, &ru);
165                                 if (tools_verbose)
166                                         printf("Finished async %i: %s %u\n",
167                                                c->pid,
168                                                WIFEXITED(c->status)
169                                                ? "exit status"
170                                                : "killed by signal",
171                                                WIFEXITED(c->status)
172                                                ? WEXITSTATUS(c->status)
173                                                : WTERMSIG(c->status));
174                                 lbalance_task_free(c->task, &ru);
175                                 c->task = NULL;
176                                 c->done = true;
177                                 close(c->output_fd);
178                                 tlist_del_from(&running, c, list);
179                                 tlist_add_tail(&done, c, list);
180                                 num_running--;
181                         }
182                 }
183         }
184 }
185
186 void *collect_command(bool *ok, char **output)
187 {
188         struct command *c;
189         const void *ctx;
190
191         while ((c = tlist_top(&done, list)) == NULL) {
192                 if (tlist_empty(&pending) && tlist_empty(&running))
193                         return NULL;
194                 reap_output();
195                 run_more();
196         }
197
198         *ok = (WIFEXITED(c->status) && WEXITSTATUS(c->status) == 0);
199         ctx = c->ctx;
200         *output = talloc_steal(ctx, c->output);
201         talloc_free(c);
202         return (void *)ctx;
203 }
204
205 /* Compile and link single C file, with object files, async. */
206 void compile_and_link_async(const void *ctx, unsigned int time_ms,
207                             const char *cfile, const char *ccandir,
208                             const char *objs, const char *compiler,
209                             const char *cflags,
210                             const char *libs, const char *outfile)
211 {
212         if (compile_verbose)
213                 printf("Compiling and linking (async) %s\n", outfile);
214         run_command_async(ctx, time_ms,
215                           "%s %s -I%s -o %s %s %s %s",
216                           compiler, cflags,
217                           ccandir, outfile, cfile, objs, libs);
218 }