/* $NetBSD: common.c,v 1.11 2022/04/04 11:22:51 riastradh Exp $ */ /*- * Copyright (c) 2009, 2017 Joerg Sonnenberger . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include "config.h" #include #include #include #include #include #include #if defined(HAVE_POSIX_SPAWNP) && defined(HAVE_SPAWN_H) #include #endif #include "common.h" static char *worklog_path; static char *real_path; char *exec_path; char *exec_name; char *wrksrc; char *sysroot; int debug; enum operation_mode current_operation_mode = mode_unknown; static struct arglist prepend_args = TAILQ_HEAD_INITIALIZER(prepend_args); static struct arglist append_args = TAILQ_HEAD_INITIALIZER(append_args); static struct arglist prepend_executable_args = TAILQ_HEAD_INITIALIZER(prepend_executable_args); static struct arglist append_executable_args = TAILQ_HEAD_INITIALIZER(append_executable_args); static struct arglist prepend_shared_args = TAILQ_HEAD_INITIALIZER(prepend_shared_args); static struct arglist append_shared_args = TAILQ_HEAD_INITIALIZER(append_shared_args); struct argument *prepend_after; const char library_name_chars[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" "01234567890-_"; char * concat(const char *s1, const char *s2) { size_t len1, len2; char *output; len1 = strlen(s1); len2 = strlen(s2); output = xmalloc(len1 + len2 + 1); memcpy(output, s1, len1); memcpy(output + len1, s2, len2); output[len1 + len2] = '\0'; return output; } char * concat2(const char *s1, const char *s2, size_t len2) { size_t len1; char *output; len1 = strlen(s1); output = xmalloc(len1 + len2 + 1); memcpy(output, s1, len1); memcpy(output + len1, s2, len2); output[len1 + len2] = '\0'; return output; } struct argument * argument_new(char *str) { struct argument *arg; arg = xmalloc(sizeof(*arg)); arg->val = str; return arg; } struct argument * argument_copy(const char *str) { struct argument *arg; arg = xmalloc(sizeof(*arg)); arg->val = xstrdup(str); return arg; } void argument_update(struct argument *arg, char *str) { free(arg->val); arg->val = str; } void arglist_from_argc(struct arglist *args, int argc, char **argv) { struct argument *arg; TAILQ_INIT(args); for (--argc, ++argv; argc; --argc, ++argv) { if (**argv == '\0') continue; arg = argument_copy(*argv); TAILQ_INSERT_TAIL(args, arg, link); } } static void arglist_prepend_list(struct arglist *args, struct arglist *prepends) { struct argument *arg, *arg2; if (prepend_after) { TAILQ_FOREACH(arg, prepends, link) { arg2 = argument_copy(arg->val); TAILQ_INSERT_AFTER(args, prepend_after, arg2, link); } } else { TAILQ_FOREACH_REVERSE(arg, prepends, arglist, link) { arg2 = argument_copy(arg->val); TAILQ_INSERT_HEAD(args, arg2, link); } } } static void arglist_append_list(struct arglist *args, struct arglist *appends) { struct argument *arg, *arg2; TAILQ_FOREACH(arg, appends, link) { arg2 = argument_copy(arg->val); TAILQ_INSERT_TAIL(args, arg2, link); } } void arglist_apply_config(struct arglist *args) { arglist_prepend_list(args, &prepend_args); arglist_append_list(args, &append_args); switch (current_operation_mode) { default: break; case mode_link_executable: arglist_prepend_list(args, &prepend_executable_args); arglist_append_list(args, &append_executable_args); break; case mode_link_shared: arglist_prepend_list(args, &prepend_shared_args); arglist_append_list(args, &append_shared_args); break; } } void argument_unlink(struct arglist *args, struct argument **argp) { struct argument *arg; arg = *argp; *argp = TAILQ_NEXT(arg, link); TAILQ_REMOVE(args, arg, link); free(arg->val); free(arg); } void parse_config(const char *wrapper) { const char *config_dir; char *config_file, *line; size_t len; ssize_t llen; FILE *fp; config_dir = getenv("CWRAPPERS_CONFIG_DIR"); if (config_dir == NULL) errx(255, "CWRAPPERS_CONFIG_DIR is missing from environment"); config_file = xasprintf("%s/%s", config_dir, wrapper); fp = fopen(config_file, "r"); if (fp == NULL) errx(255, "Failed to open wrapper config file `%s'", config_file); for (len = 0, line = NULL; (llen = getline(&line, &len, fp)) > 0; free(line), line = NULL, len = 0) { if (line[llen - 1] == '\n') { line[llen - 1] = '\0'; --llen; } if (strncmp(line, "worklog=", 8) == 0) { free(worklog_path); worklog_path = xstrdup(line + 8); continue; } if (strncmp(line, "path=", 5) == 0) { free(real_path); real_path = xstrdup(line + 5); continue; } if (strncmp(line, "exec_path=", 10) == 0) { free(exec_path); exec_path = xstrdup(line + 10); continue; } if (strncmp(line, "exec=", 5) == 0) { free(exec_name); exec_name = xstrdup(line + 5); continue; } if (strncmp(line, "reorder=", 8) == 0) { register_reorder(line + 8); continue; } if (strncmp(line, "transform=", 10) == 0) { register_generic_transform(line + 10); continue; } if (strncmp(line, "prepend=", 8) == 0) { struct argument *arg; arg = argument_copy(line + 8); TAILQ_INSERT_TAIL(&prepend_args, arg, link); } if (strncmp(line, "append=", 7) == 0) { struct argument *arg; arg = argument_copy(line + 7); TAILQ_INSERT_TAIL(&append_args, arg, link); } if (strncmp(line, "prepend_executable=", 19) == 0) { struct argument *arg; arg = argument_copy(line + 19); TAILQ_INSERT_TAIL(&prepend_executable_args, arg, link); } if (strncmp(line, "append_executable=", 18) == 0) { struct argument *arg; arg = argument_copy(line + 18); TAILQ_INSERT_TAIL(&append_executable_args, arg, link); } if (strncmp(line, "prepend_shared=", 15) == 0) { struct argument *arg; arg = argument_copy(line + 15); TAILQ_INSERT_TAIL(&prepend_shared_args, arg, link); } if (strncmp(line, "append_shared=", 14) == 0) { struct argument *arg; arg = argument_copy(line + 14); TAILQ_INSERT_TAIL(&append_shared_args, arg, link); } if (strncmp(line, "wrksrc=", 7) == 0) { free(wrksrc); wrksrc = xstrdup(line + 7); continue; } if (strncmp(line, "sysroot=", 8) == 0) { free(sysroot); sysroot = xstrdup(line + 8); continue; } if (strncmp(line, "unwrap=", 7) == 0) { register_unwrap(line + 7); continue; } if (strncmp(line, "debug=", 6) == 0) { debug = atoi(line + 6); continue; } } fclose(fp); if (worklog_path == NULL) errx(255, "worklog path has not been set"); if (exec_name == NULL) errx(255, "executable name has not been set"); if (exec_path == NULL) errx(255, "executable path has not been set"); } FILE * worklog_open(void) { FILE *fp; fp = fopen(worklog_path, "a"); if (fp == NULL) err(255, "Could not open %s", worklog_path); return fp; } void worklog_cmd(FILE *worklog, const char *prefix, const char *cmd, struct arglist *args) { static const char safe_chars[] = "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "01234567890-_/:.,+="; struct argument *arg; size_t len, buflen; char *buf; fprintf(worklog, "%s %s", prefix, cmd); buf = NULL; buflen = 0; TAILQ_FOREACH(arg, args, link) { len = strlen(arg->val); if (len == 0) { fputs(" ''", worklog); continue; } if (strspn(arg->val, safe_chars) == len) { fputc(' ', worklog); fputs(arg->val, worklog); continue; } len = shquote(arg->val, buf, buflen); if (len >= buflen) { buflen = len + 1; free(buf); buf = xmalloc(buflen); shquote(arg->val, buf, buflen); } fputc(' ', worklog); fputs(buf, worklog); } fputc('\n', worklog); if (buf) free(buf); } #if __GNUC__ + 0 >= 3 __attribute__((__noreturn__)) #endif static void command_child_exec(char **argv) { static const char failed_exec_msg[] = "exec failed\n"; int status; execvp(exec_name, argv); status = write(STDERR_FILENO, failed_exec_msg, sizeof(failed_exec_msg) - 1); _exit(255 | status); } int command_exec(struct arglist *args, int do_fork, char **environment) { struct argument *arg; char **argv, **argv2; int argc, status; pid_t child; argc = 2; TAILQ_FOREACH(arg, args, link) ++argc; argv2 = xmalloc(sizeof(void *) * argc); argv2[0] = exec_name; argv = argv2 + 1; TAILQ_FOREACH(arg, args, link) *argv++ = arg->val; *argv = NULL; if (real_path) setenv("PATH", real_path, 1); if (!do_fork) command_child_exec(argv2); #if defined(HAVE_POSIX_SPAWNP) && defined(HAVE_SPAWN_H) status = posix_spawnp(&child, exec_name, NULL, NULL, argv2, environment); if (status) { errno = status; err(255, "posix_spawn failed"); } #else child = vfork(); if (child == -1) err(255, "fork failed"); if (child == 0) command_child_exec(argv2); #endif waitpid(child, &status, 0); return WEXITSTATUS(status); } size_t wrapper_hash(const char *str) { return wrapper_hash2(str, strlen(str)); } size_t wrapper_hash2(const char *str, size_t len) { uint32_t hashes[3]; mi_vector_hash(str, len, 0xdeadbeef, hashes); return hashes[0]; }