diff options
author | Rafael G. Martins <rafael@rafaelmartins.eng.br> | 2016-12-27 02:29:54 +0100 |
---|---|---|
committer | Rafael G. Martins <rafael@rafaelmartins.eng.br> | 2016-12-27 02:43:59 +0100 |
commit | 8cac2c3e3a61b64b9a9855dec413239bcec7f9d2 (patch) | |
tree | 6240cdabaa0352f08d62bbfa6de7c53f5a3f1063 /src/blogc-make/exec.c | |
parent | 7bf68b0b617fb3ffa86f38fe06a49786883037f4 (diff) | |
download | blogc-8cac2c3e3a61b64b9a9855dec413239bcec7f9d2.tar.gz blogc-8cac2c3e3a61b64b9a9855dec413239bcec7f9d2.tar.bz2 blogc-8cac2c3e3a61b64b9a9855dec413239bcec7f9d2.zip |
make: implemented a build tool for blogc
so, this is basically what happens when you don't have anything better
to do in the christmas weekend. most of this code was written in the
last 2 or 3 days.
i'd like to thank the chivas brothers, the weather and my psychological
problems for this achievement.
on a serious note, this tool still needs a man page, more tests, and the
aws lambda function should be adapted to use it instead of (or together
with) make/busybox.
also, while talking about aws lambda, this tool can be nicely embedded
into the blogc binary, to produce a single "small" static binary for
usage in lambda ;)
Diffstat (limited to 'src/blogc-make/exec.c')
-rw-r--r-- | src/blogc-make/exec.c | 297 |
1 files changed, 297 insertions, 0 deletions
diff --git a/src/blogc-make/exec.c b/src/blogc-make/exec.c new file mode 100644 index 0000000..75b7c00 --- /dev/null +++ b/src/blogc-make/exec.c @@ -0,0 +1,297 @@ +/* + * blogc: A blog compiler. + * Copyright (C) 2015-2016 Rafael G. Martins <rafael@rafaelmartins.eng.br> + * + * This program can be distributed under the terms of the BSD License. + * See the file LICENSE. + */ + +#include <stdbool.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <errno.h> +#include <libgen.h> +#include "../common/error.h" +#include "../common/file.h" +#include "../common/utils.h" +#include "ctx.h" +#include "exec.h" +#include "settings.h" + + +int +bm_exec_command(const char *cmd, const char *input, char **output, + char **error, bc_error_t **err) +{ + if (err == NULL || *err != NULL) + return 3; + + int fd_in[2]; + if (-1 == pipe(fd_in)) { + *err = bc_error_new_printf(BLOGC_MAKE_ERROR_EXEC, + "Failed to create stdin pipe: %s", strerror(errno)); + return 3; + } + + int fd_out[2]; + if (-1 == pipe(fd_out)) { + *err = bc_error_new_printf(BLOGC_MAKE_ERROR_EXEC, + "Failed to create stdout pipe: %s", strerror(errno)); + close(fd_in[0]); + close(fd_in[1]); + return 3; + } + + int fd_err[2]; + if (-1 == pipe(fd_err)) { + *err = bc_error_new_printf(BLOGC_MAKE_ERROR_EXEC, + "Failed to create stderr pipe: %s", strerror(errno)); + close(fd_in[0]); + close(fd_in[1]); + close(fd_out[0]); + close(fd_out[1]); + return 3; + } + + pid_t pid = fork(); + if (pid == -1) { + *err = bc_error_new_printf(BLOGC_MAKE_ERROR_EXEC, + "Failed to fork: %s", strerror(errno)); + close(fd_in[0]); + close(fd_in[1]); + close(fd_out[0]); + close(fd_out[1]); + close(fd_err[0]); + close(fd_err[1]); + return 3; + } + + // child + if (pid == 0) { + close(fd_in[1]); + close(fd_out[0]); + close(fd_err[0]); + + dup2(fd_in[0], STDIN_FILENO); + dup2(fd_out[1], STDOUT_FILENO); + dup2(fd_err[1], STDERR_FILENO); + + char *const argv[] = { + "/bin/sh", + "-c", + (char*) cmd, + NULL, + }; + + execv(argv[0], argv); + + exit(1); + } + + // parent + close(fd_in[0]); + close(fd_out[1]); + close(fd_err[1]); + + if (input != NULL) { + if (-1 == write(fd_in[1], input, strlen(input))) { + *err = bc_error_new_printf(BLOGC_MAKE_ERROR_EXEC, + "Failed to write to stdin pipe: %s", strerror(errno)); + close(fd_in[1]); + close(fd_out[0]); + close(fd_err[0]); + return 3; + } + } + + close(fd_in[1]); + + char buffer[BC_FILE_CHUNK_SIZE]; + ssize_t s; + + bc_string_t *out = NULL; + while(0 != (s = read(fd_out[0], buffer, BC_FILE_CHUNK_SIZE))) { + if (s == -1) { + *err = bc_error_new_printf(BLOGC_MAKE_ERROR_EXEC, + "Failed to read from stdout pipe: %s", strerror(errno)); + close(fd_out[0]); + close(fd_err[0]); + bc_string_free(out, true); + return 3; + } + if (out == NULL) { + out = bc_string_new(); + } + bc_string_append_len(out, buffer, s); + } + if (out != NULL) { + *output = bc_string_free(out, false); + } + close(fd_out[0]); + + out = NULL; + while(0 != (s = read(fd_err[0], buffer, BC_FILE_CHUNK_SIZE))) { + if (s == -1) { + *err = bc_error_new_printf(BLOGC_MAKE_ERROR_EXEC, + "Failed to read from stderr pipe: %s", strerror(errno)); + close(fd_err[0]); + bc_string_free(out, true); + return 3; + } + if (out == NULL) + out = bc_string_new(); + bc_string_append_len(out, buffer, s); + } + if (out != NULL) { + *error = bc_string_free(out, false); + } + close(fd_err[0]); + + int status; + waitpid(pid, &status, 0); + + return WEXITSTATUS(status); +} + + +static void +list_variables(const char *key, const char *value, bc_string_t *str) +{ + char *tmp = bc_shell_quote(value); + bc_string_append_printf(str, " -D %s=%s", key, tmp); + free(tmp); +} + + +char* +bm_exec_build_blogc_cmd(bm_settings_t *settings, bc_trie_t *variables, + bool listing, const char *template, const char *output, bool sources_stdin) +{ + bc_string_t *rv = bc_string_new(); + + const char *locale = NULL; + if (settings != NULL) { + locale = bc_trie_lookup(settings->settings, "locale"); + } + if (locale != NULL) { + char *tmp = bc_shell_quote(locale); + bc_string_append_printf(rv, "LC_ALL=%s ", tmp); + free(tmp); + } + + bc_string_append(rv, "blogc"); + + if (settings != NULL) { + bc_trie_foreach(settings->env, + (bc_trie_foreach_func_t) list_variables, rv); + } + + bc_trie_foreach(variables, (bc_trie_foreach_func_t) list_variables, rv); + + if (listing) { + bc_string_append(rv, " -l"); + } + + if (template != NULL) { + char *tmp = bc_shell_quote(template); + bc_string_append_printf(rv, " -t %s", tmp); + free(tmp); + } + + if (output != NULL) { + char *tmp = bc_shell_quote(output); + bc_string_append_printf(rv, " -o %s", tmp); + free(tmp); + } + + if (sources_stdin) { + bc_string_append(rv, " -i"); + } + + return bc_string_free(rv, false); +} + + +int +bm_exec_blogc(bm_settings_t *settings, bc_trie_t *variables, bool listing, + bm_filectx_t *template, bm_filectx_t *output, bc_slist_t *sources, + bool verbose, bool only_first_source) +{ + bc_string_t *input = bc_string_new(); + for (bc_slist_t *l = sources; l != NULL; l = l->next) { + bc_string_append_printf(input, "%s\n", ((bm_filectx_t*) l->data)->path); + if (only_first_source) + break; + } + + char *cmd = bm_exec_build_blogc_cmd(settings, variables, listing, + template->path, output->path, input->len > 0); + + if (verbose) + printf("%s\n", cmd); + else + printf(" BLOGC %s\n", output->short_path); + fflush(stdout); + + char *out = NULL; + char *err = NULL; + bc_error_t *error = NULL; + + int rv = bm_exec_command(cmd, input->str, &out, &err, &error); + + if (error != NULL) { + bc_error_print(error, "blogc-make"); + free(cmd); + free(out); + free(err); + bc_string_free(input, true); + bc_error_free(error); + return 3; + } + + if (rv != 0) { + if (verbose) { + fprintf(stderr, + "error: Failed to execute command.\n" + "\n" + "STATUS CODE: %d\n", rv); + if (input->len > 0) { + fprintf(stderr, "\nSTDIN:\n" + "----------------------------->8-----------------------------\n" + "%s\n" + "----------------------------->8-----------------------------\n", + bc_str_strip(input->str)); + } + if (out != NULL) { + fprintf(stderr, "\nSTDOUT:\n" + "----------------------------->8-----------------------------\n" + "%s\n" + "----------------------------->8-----------------------------\n", + bc_str_strip(out)); + } + if (err != NULL) { + fprintf(stderr, "\nSTDERR:\n" + "----------------------------->8-----------------------------\n" + "%s\n" + "----------------------------->8-----------------------------\n", + bc_str_strip(err)); + } + } + else { + fprintf(stderr, + "error: Failed to execute command, returned status code: %d\n", + rv); + } + } + + bc_string_free(input, true); + free(cmd); + free(out); + free(err); + + return rv; +} |