#include "ccanlint.h" #include "../tools.h" #include #include #include #include #include #include #include #include #include #include #include #include #include static struct lbalance *lb; TLIST_TYPE(command, struct command); static struct tlist_command pending = TLIST_INIT(pending); static struct tlist_command running = TLIST_INIT(running); static unsigned int num_running = 0; static struct tlist_command done = TLIST_INIT(done); struct command { struct list_node list; char *command; pid_t pid; int output_fd; unsigned int time_ms; struct lbalance_task *task; int status; char *output; bool done; const void *ctx; }; static void killme(int sig) { kill(-getpid(), SIGKILL); } static void run_more(void) { struct command *c; while (num_running < lbalance_target(lb)) { int p[2]; c = tlist_top(&pending, list); if (!c) break; fflush(stdout); if (pipe(p) != 0) err(1, "Pipe failed"); c->pid = fork(); if (c->pid == -1) err(1, "Fork failed"); if (c->pid == 0) { struct itimerval itim; if (dup2(p[1], STDOUT_FILENO) != STDOUT_FILENO || dup2(p[1], STDERR_FILENO) != STDERR_FILENO || close(p[0]) != 0 || close(STDIN_FILENO) != 0 || open("/dev/null", O_RDONLY) != STDIN_FILENO) exit(128); signal(SIGALRM, killme); itim.it_interval.tv_sec = itim.it_interval.tv_usec = 0; itim.it_value = timespec_to_timeval(time_from_msec(c->time_ms)); setitimer(ITIMER_REAL, &itim, NULL); c->status = system(c->command); if (WIFEXITED(c->status)) exit(WEXITSTATUS(c->status)); /* Here's a hint... */ exit(128 + WTERMSIG(c->status)); } if (tools_verbose) printf("Running async: %s => %i\n", c->command, c->pid); close(p[1]); c->output_fd = p[0]; c->task = lbalance_task_new(lb); tlist_del_from(&pending, c, list); tlist_add_tail(&running, c, list); num_running++; } } static void destroy_command(struct command *command) { if (!command->done && command->pid) { kill(-command->pid, SIGKILL); close(command->output_fd); num_running--; } tlist_del(command, list); } void run_command_async(const void *ctx, unsigned int time_ms, const char *fmt, ...) { struct command *command; va_list ap; assert(ctx); if (!lb) lb = lbalance_new(); command = tal(ctx, struct command); command->ctx = ctx; command->time_ms = time_ms; command->pid = 0; /* We want to track length, so don't use tal_strdup */ command->output = tal_arrz(command, char, 1); va_start(ap, fmt); command->command = tal_vfmt(command, fmt, ap); va_end(ap); tlist_add_tail(&pending, command, list); command->done = false; tal_add_destructor(command, destroy_command); run_more(); } static void reap_output(void) { fd_set in; struct command *c, *next; int max_fd = 0; FD_ZERO(&in); tlist_for_each(&running, c, list) { FD_SET(c->output_fd, &in); if (c->output_fd > max_fd) max_fd = c->output_fd; } if (select(max_fd+1, &in, NULL, NULL, NULL) < 0) err(1, "select failed"); tlist_for_each_safe(&running, c, next, list) { if (FD_ISSET(c->output_fd, &in)) { int old_len, len; /* This length includes nul terminator! */ old_len = tal_count(c->output); tal_resize(&c->output, old_len + 1024); len = read(c->output_fd, c->output + old_len - 1, 1024); if (len < 0) err(1, "Reading from async command"); tal_resize(&c->output, old_len + len); c->output[old_len + len - 1] = '\0'; if (len == 0) { struct rusage ru; wait4(c->pid, &c->status, 0, &ru); if (tools_verbose) printf("Finished async %i: %s %u\n", c->pid, WIFEXITED(c->status) ? "exit status" : "killed by signal", WIFEXITED(c->status) ? WEXITSTATUS(c->status) : WTERMSIG(c->status)); lbalance_task_free(c->task, &ru); c->task = NULL; c->done = true; close(c->output_fd); tlist_del_from(&running, c, list); tlist_add_tail(&done, c, list); num_running--; } } } } void *collect_command(bool *ok, char **output) { struct command *c; const void *ctx; while ((c = tlist_top(&done, list)) == NULL) { if (tlist_empty(&pending) && tlist_empty(&running)) return NULL; reap_output(); run_more(); } *ok = (WIFEXITED(c->status) && WEXITSTATUS(c->status) == 0); ctx = c->ctx; *output = tal_steal(ctx, c->output); tal_free(c); return (void *)ctx; } /* Compile and link single C file, with object files, async. */ void compile_and_link_async(const void *ctx, unsigned int time_ms, const char *cfile, const char *ccandir, const char *objs, const char *compiler, const char *cflags, const char *libs, const char *outfile) { if (compile_verbose) printf("Compiling and linking (async) %s\n", outfile); run_command_async(ctx, time_ms, "%s %s -I%s -o %s %s %s %s", compiler, cflags, ccandir, outfile, cfile, objs, libs); }