#include "pow_lua.h" #include "config.h" #include "log.h" #include "archive.h" #include "stdio.h" #include "archive_entry.h" #include "lauxlib.h" #include "stdlib.h" #include "unistd.h" #include "string.h" #include "fcntl.h" #include "sys/stat.h" #include "errno.h" int parse_octal_string(const char *str) { if (str[0] == '0' && (str[1] == 'o' || str[1] == 'O')) { str += 2; } else if (str[0] == '0') { str += 1; } int result = 0; while (*str) { int digit = *str - '0'; if (digit < 0 || digit > 7) { errno = EINVAL; return -1; } result = result * 8 + digit; str++; } return result; } int pow_lua_extract(lua_State *L) { int r; ssize_t size; struct archive *a; struct archive_entry *entry; if (!lua_isstring(L, 1)) { lua_pushnil(L); lua_pushstring(L, "arg 1 must be string (src)"); return 2; } if (!lua_isstring(L, 2)) { lua_pushnil(L); lua_pushstring(L, "arg 2 must be string (dest)"); return 2; } const char *filename = lua_tostring(L, 1); const char *dest = lua_tostring(L, 2); a = archive_read_new(); archive_read_support_filter_all(a); archive_read_support_format_all(a); if ((r = archive_read_open_filename(a, filename, 10240))) { return 0; } for (;;) { r = archive_read_next_header(a, &entry); if (r == ARCHIVE_EOF) break; if (r < ARCHIVE_OK) pow_log_info("warn while unpacking: %s", archive_error_string(a)); if (r < ARCHIVE_WARN) { pow_log_error("error while unpacking: %s", archive_error_string(a)); lua_pop(L, 1); char *error_msg = malloc(256); lua_pushnil(L); if (!error_msg) { lua_pushstring(L, "error while unpacking and failed to malloc to push " "real error message"); pow_log_error("error while unpacking and failed to push real error message"); return 2; } snprintf(error_msg, 256, "error while unpacking: %s", archive_error_string(a)); lua_pushstring(L, error_msg); free(error_msg); return 2; } const char *cur = archive_entry_pathname(entry); if (cur) { size_t need = strlen(dest) + strlen(cur) + 2; char *full = malloc(need); if (!full) { lua_pushnil(L); lua_pushstring(L, "failed to malloc entry path\n"); archive_read_close(a); archive_read_free(a); return 2; } snprintf(full, need, "%s/%s", dest, cur); archive_entry_set_pathname(entry, full); free(full); } r = archive_read_extract(a, entry, 0); if (r < ARCHIVE_WARN) { lua_pushnil(L); lua_pushfstring( L, "failed to extract '%s': %s", archive_entry_pathname(entry), archive_error_string(a) ? archive_error_string(a) : "unknown"); archive_read_close(a); archive_read_free(a); return 2; } } archive_read_close(a); archive_read_free(a); lua_pushboolean(L, 1); return 1; } int pow_lua_install(lua_State *L) { if (!lua_isstring(L, 1)) { lua_pushnil(L); lua_pushstring(L, "arg 1 must be string (src)"); return 2; } if (!lua_isstring(L, 2)) { lua_pushnil(L); lua_pushstring(L, "arg 2 must be string (dest)"); return 2; } if (!lua_isstring(L, 3)) { pow_log_info("no permission bit specified for install function, defaulting to 644"); } const char *src_str = lua_tostring(L, 1); const char *dest_str = lua_tostring(L, 2); mode_t permission_bit = S_IRUSR | S_IWUSR | S_IROTH | S_IRGRP; if (lua_isstring(L, 3)) { char *endptr; unsigned long perms = strtoul(lua_tostring(L, 3), &endptr, 8); if (*endptr != '\0') { lua_pushnil(L); lua_pushstring(L, "invalid octal permission string"); return 2; } permission_bit = (mode_t)perms; } pow_log_info("installing: %s to %s", src_str, dest_str); int src_fd = open(src_str, O_RDONLY); if (src_fd == -1) { pow_log_error("failed to open source file %s while installing", src_str); return 2; } struct stat src_stat = {0}; if (fstat(src_fd, &src_stat) == -1) { close(src_fd); pow_log_error("failed to stat source file %s while installing", src_str); return 2; } int dest_fd = open(dest_str, O_WRONLY | O_CREAT | O_TRUNC, permission_bit); if (dest_fd == -1) { close(src_fd); pow_log_error("failed to open destination file %s for writing while installing", dest_str); return 2; } char buf[8192]; off_t copied = 0; ssize_t bytes_read; ssize_t bytes_written; off_t remaining = src_stat.st_size; while (remaining > 0) { size_t to_read = remaining > sizeof(buf) ? sizeof(buf) : remaining; bytes_read = read(src_fd, buf, to_read); if (bytes_read == -1) { if (errno == EINTR) { continue; } pow_log_error("err?"); close(dest_fd); close(src_fd); return 2; } if (bytes_read == 0) { pow_log_error("unexpected EOF during copy"); close(dest_fd); close(src_fd); return 2; } ssize_t written = 0; while (written < bytes_read) { bytes_written = write(dest_fd, buf + written, bytes_read - written); if (bytes_written == -1) { if (errno == EINTR) { continue; } pow_log_error("write failed during install"); close(dest_fd); close(src_fd); return 2; } written += bytes_written; } copied += bytes_read; remaining -= bytes_read; } if (fchmod(dest_fd, permission_bit) == -1) { pow_log_error("failed to set permission bits, expect breakage!"); } close(dest_fd); close(src_fd); return 1; } int pow_lua_chdir(lua_State *L) { if (!lua_isstring(L, 1)) { lua_pushnil(L); lua_pushstring(L, "arg 1 must be string for chdir"); return 2; } char *dir = lua_tostring(L, -1); int result = chdir(dir); if (result) { pow_log_error("failed to chdir %d", result); } return 1; } luaL_Reg libpow[] = {{"chdir", pow_lua_chdir}, {"extract", pow_lua_extract}, {"install", pow_lua_install}, {NULL, NULL}}; int pow_lua_load(lua_State *L, pow_config *cfg) { luaL_newlib(L, libpow); lua_createtable(L, 0, 1); lua_pushstring(L, cfg->makeopts); lua_setfield(L, -2, "makeopts"); lua_setfield(L, -2, "config"); lua_setglobal(L, "pow"); return 0; }