diff options
| -rw-r--r-- | Makefile | 2 | ||||
| -rw-r--r-- | include/cookbook.h | 8 | ||||
| -rw-r--r-- | include/lib/archive.h | 8 | ||||
| -rw-r--r-- | include/net.h | 6 | ||||
| -rw-r--r-- | include/parser.h | 2 | ||||
| -rw-r--r-- | include/pkgman.h | 8 | ||||
| -rw-r--r-- | src/cookbook.c | 70 | ||||
| -rw-r--r-- | src/lib/archive.c | 54 | ||||
| -rw-r--r-- | src/lib/url.c | 91 | ||||
| -rw-r--r-- | src/main.c | 89 | ||||
| -rw-r--r-- | src/net.c | 113 | ||||
| -rw-r--r-- | src/parser.c | 27 | ||||
| -rw-r--r-- | src/pkgman.c | 222 |
13 files changed, 584 insertions, 116 deletions
@@ -8,7 +8,7 @@ INCLUDE_DIR = ./include SRC_FILES = $(shell find $(SRC_DIR) -name '*.c') OBJ_FILES = $(SRC_FILES:%=$(BUILD_DIR)/%.o) -CFLAGS = -Wall -Wpedantic -Werror -lcurl -larchive -lzstd -I./include +CFLAGS = -lcurl -larchive -lzstd -lgpgme -I./include $(BUILD_DIR)/%.c.o: %.c @mkdir -p $(dir $@) diff --git a/include/cookbook.h b/include/cookbook.h new file mode 100644 index 0000000..86f66aa --- /dev/null +++ b/include/cookbook.h @@ -0,0 +1,8 @@ +#ifndef COOKBOOK_H +#define COOKBOOK_H + +typedef struct url url; + +int cookbook_run(struct url *script_loc, char *script); + +#endif diff --git a/include/lib/archive.h b/include/lib/archive.h new file mode 100644 index 0000000..f0dde8f --- /dev/null +++ b/include/lib/archive.h @@ -0,0 +1,8 @@ +#ifndef ARCHIVE_H +#define ARCHIVE_H + +typedef struct url url; + +int pkg_extract(char *archive_path, struct url *out_dir); + +#endif diff --git a/include/net.h b/include/net.h index e6cb32a..b2b415f 100644 --- a/include/net.h +++ b/include/net.h @@ -9,18 +9,18 @@ typedef struct string_view string_view; -struct memory { +struct net_write_data { char *buffer; size_t size; }; -struct net_fwrite_userdata { +struct net_file_write_data { FILE *file; }; int net_init(); void net_shutdown(); -int net_send_request(char *url, struct memory *mem, int write_opts); +int net_send_request(char *url, int write_opts, void *userdata); #endif diff --git a/include/parser.h b/include/parser.h index a8a2a11..9358115 100644 --- a/include/parser.h +++ b/include/parser.h @@ -34,7 +34,7 @@ struct parser { }; int parser_init(struct parser *parser, - struct memory *mem, + struct string_view *mem, struct parser_backend *backend, void *userdata); int parser_parse(struct parser *parser); diff --git a/include/pkgman.h b/include/pkgman.h new file mode 100644 index 0000000..0d17622 --- /dev/null +++ b/include/pkgman.h @@ -0,0 +1,8 @@ +#ifndef PKGMAN_H +#define PKGMAN_H + +int pkgman_upstream_check(const char *pkg); +int pkgman_install_pkg(const char *pkg); +int pkgman_upstream_integrity_download(const char *pkg); + +#endif diff --git a/src/cookbook.c b/src/cookbook.c new file mode 100644 index 0000000..db2c81a --- /dev/null +++ b/src/cookbook.c @@ -0,0 +1,70 @@ +#include <cookbook.h> + +#include <string.h> +#include <unistd.h> +#include <stdio.h> +#include <sys/wait.h> +#include <stdlib.h> + +#include <lib/url.h> +#include <err.h> + +int cookbook_run(struct url *script_loc, char *script) +{ + printf("Launching build script...\n"); + + int pipefd[2]; + + if (pipe(pipefd) == -1) + return -FORKERR; + + pid_t ps = fork(); + + switch (ps) { + case -1: + perror("fork"); + return -FORKERR; + case 0: + close(pipefd[0]); + dup2(pipefd[1], STDOUT_FILENO); + close(pipefd[1]); + + chdir(script_loc->buffer); + url_append(script_loc, "build.sh"); + if(execve(script_loc->buffer, + (char*[]){ script_loc->buffer, script,NULL}, + (char*[]){ + "PWD=/tmp/pkgman/nvim-extract", + "PATH=/bin:/usr/bin", + NULL }) == -1) { + fprintf(stderr, "An error was encountered!\n"); + } + break; + default: { + size_t size = 0; + size_t cap = 40; + char *mem = malloc(40); + char buf[1024]; + memset(buf, 0, 1024); + + close(pipefd[1]); + while (read(pipefd[0], buf, 1) > 0) { + if (size >= cap) { + cap *= 2; + mem = realloc(mem, cap); + printf("Realloced!"); + } + + memcpy(mem + size, buf, 1); + size++; + } + + printf("Build script stdouts: %.*s\n", size, mem); + + free(mem); + int ret; + waitpid(ps, &ret, 0); + printf("Build script exited with code %d\n", ret); + } + } +} diff --git a/src/lib/archive.c b/src/lib/archive.c new file mode 100644 index 0000000..2dcd2ec --- /dev/null +++ b/src/lib/archive.c @@ -0,0 +1,54 @@ +#include <lib/archive.h> + +#include <err.h> +#include <lib/url.h> + +#include <assert.h> + +#include <archive.h> +#include <archive_entry.h> + +int pkg_extract(char *archive_path, struct url *out_dir) +{ + assert(out_dir != NULL); + + struct archive *read = archive_read_new(); + struct archive *disk = archive_write_disk_new(); + // Read opts + archive_read_support_filter_zstd(read); + archive_read_support_format_tar(read); + + archive_read_open_filename(read, archive_path, 65535); + // Write opts + archive_write_disk_set_options(disk, ARCHIVE_EXTRACT_TIME); + + // tmp archive entry (for iterating read) + struct archive_entry *entry; + + while (archive_read_next_header(read, &entry) == ARCHIVE_OK) { + const char *entry_path = archive_entry_pathname(entry); + + struct url fullpath = {0}; + url_copy(out_dir, &fullpath); + url_append(&fullpath, entry_path); + + archive_entry_set_pathname(entry, fullpath.buffer); + + archive_write_header(disk, entry); + + const void *buf; + size_t len; + off_t offset; + + while(archive_read_data_block(read, &buf, &len, &offset) == ARCHIVE_OK) + archive_write_data_block(disk, buf, len, offset); + + archive_write_finish_entry(disk); + url_free(&fullpath); + } + + archive_free(disk); + archive_free(read); + + return SUCCESS; +} diff --git a/src/lib/url.c b/src/lib/url.c new file mode 100644 index 0000000..7a15558 --- /dev/null +++ b/src/lib/url.c @@ -0,0 +1,91 @@ +#include <lib/url.h> + +#include <assert.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <err.h> + +//TODO: Audit this implementation. Potentially an arena alloc? + +int url_init(struct url *url, char *base) +{ + assert(url != NULL); + + size_t len = strlen(base) + 1; + void *ptr = malloc(len); + + if (ptr == NULL) + return -URLINITERR; + + url->buffer = ptr; + memcpy(url->buffer, base, len); + url->len = len; + url->buffer[url->len - 1] = '\0'; + + return SUCCESS; +} + +void url_free(struct url *url) +{ + assert(url != NULL); + free(url->buffer); +} + + +int url_copy(struct url *from, struct url *to) +{ + assert(from != NULL); + assert(to != NULL); + + to->buffer = malloc(from->len); + if(to->buffer == NULL) + return -ERR; + + memcpy(to->buffer, from->buffer, from->len); + + to->len = from->len; + return SUCCESS; +} + +int url_append(struct url *url, const char *add) +{ + assert(url != NULL); + assert(add != NULL); + + if (url->buffer == NULL || url->len < 8) { + return -URLPATHERR; + } + + url->len--; + size_t addlen = strlen(add); + size_t nlen = url->len + addlen + 1; + + url->buffer = realloc(url->buffer, nlen); + + memcpy(url->buffer + url->len, add, addlen); + + url->len = nlen; + url->buffer[url->len - 1] = '\0'; +} + +int url_append_path(struct url *url, const char *add) +{ + assert(url != NULL); + assert(add != NULL); + + if (url->buffer == NULL || url->len < 8) { + return -URLPATHERR; + } + + url->buffer[url->len - 1] = '/'; //TODO: Check existence of "/" + size_t addlen = strlen(add); + size_t nlen = url->len + addlen + 1; + + url->buffer = realloc(url->buffer, nlen); + + memcpy(url->buffer + url->len, add, addlen); + + url->len = nlen; + url->buffer[url->len - 1] = '\0'; +} @@ -2,12 +2,17 @@ #include <string.h> #include <stdlib.h> +#include <zstd.h> +#include <stdio.h> +#include <sodium.h> +#include <pkgman.h> #include <um.h> #include <err.h> #include <parser.h> #include <lib/sv.h> #include <net.h> +#include <lib/url.h> /** * Install package: @@ -19,7 +24,10 @@ * Update: * pkgman update * pkgman update <package_name> - * + * + * Kernel Update: + * pkgman kupdate + * * Fetch: * pkgman fetch <query> * @@ -40,59 +48,36 @@ void install_usage() int cmd_install(int argc, char **argv) { - if(argc < 1) { - install_usage(); - return USAGE; - } - - int ret = 1; - - struct memory mem = { 0 }; - net_send_request("https://packages.0xinfinity.dev/list", &mem, WRITE_OPT_MEMORY); - - struct um_user_data userdata = { 0 }; - struct parser parser; - struct parser_backend backend = um_backend(); - - parser_init(&parser, &mem, &backend, (void*)&userdata); - parser_parse(&parser); - - struct string_view pkg = sv_create(argv[0], strlen(argv[0])); + if (argc < 1) { + install_usage(); + return USAGE; + } + + char *pkg = argv[0]; + + int ret = pkgman_upstream_check(pkg); + + if (ret != SUCCESS) + return -PKGNOTFND; + + ret = pkgman_upstream_integrity_download(pkg); - int found = 0; + if (ret != SUCCESS) + return ret; - LL_FOREACH(manifest, &userdata.manifest) { - if (!current->data.key.buf) - continue; + ret = pkgman_install_pkg(pkg); - if (sv_equal(&pkg, ¤t->data.key)) - found = 1; - } - - if (!found) { - printf("Package '%s' not found!\n",argv[0]); - ret = -PKGNOTFND; - goto cleanup; - } - - printf("Package '%s' found on upstream!\n", argv[0]); - ret = SUCCESS; - -cleanup: - // clean LL items - LL_FOREACH(manifest, &userdata.manifest) { - if (current->data.key.buf == NULL) - continue; - - free(current->data.key.buf); - free(current->data.value.buf); - } - - ll_manifest_free(&userdata.manifest); + if (ret != SUCCESS) + return ret; - free(mem.buffer); + return SUCCESS; +} - return ret; +int cmd_build(int argc, char** argv) +{ + // ZSTD_compress("test", 60, "aaa.pkg", 30, 3); + printf("Produced a tar file!"); + return SUCCESS; } typedef int (*cmd_fn)(int, char**); @@ -105,6 +90,7 @@ struct cmd_entry // First-level command table struct cmd_entry table[] = { + { SV("build"), cmd_build }, { SV("install"), cmd_install }, { SV(NULL), NULL }, }; @@ -118,6 +104,7 @@ void usage() printf("-------------------------------\n"); printf("Available commands:\n"); printf(" pkgman install <pkg>\n"); + printf(" pkgman build\n"); printf(" pkgman update <pkg?>\n"); } @@ -130,11 +117,13 @@ int main(int argc, char **argv) cmd_fn cmd_func = NULL; - struct string_view argv1 = SV(argv[1]); + struct string_view argv1 = (struct string_view) + {.buf = argv[1], .len = strlen(argv[1])}; for (int i = 0; i < ARRAY_SIZE(table); i++) { if (table[i].key.buf == NULL) break; + if (sv_equal(&argv1, &table[i].key)) cmd_func = table[i].func; } @@ -6,83 +6,90 @@ #include <curl/curl.h> #include <stdlib.h> -size_t write_callback(char *ptr, size_t size, size_t nmemb, void *userdata) +size_t write_cb(char *ptr, size_t size, size_t nmemb, void *userdata) { - assert(ptr != NULL); - assert(userdata != NULL); + assert(ptr != NULL); + assert(userdata != NULL); - struct memory *chunk = (struct memory*)userdata; - size_t nsize = chunk->size + nmemb; + struct net_write_data *chunk = (struct net_write_data*)userdata; + size_t nsize = chunk->size + nmemb; - chunk->buffer = realloc(chunk->buffer, nsize); + chunk->buffer = realloc(chunk->buffer, nsize); - if (nsize > chunk->size) - memcpy(&chunk->buffer[chunk->size], ptr, nmemb); + if (nsize > chunk->size) + memcpy(&chunk->buffer[chunk->size], ptr, nmemb); - chunk->size = nsize; + chunk->size = nsize; - return nmemb; + return nmemb; } size_t write_file_cb(char *ptr, size_t size, size_t nmemb, void *userdata) { - assert(ptr != NULL); - assert(ptr != NULL); + assert(ptr != NULL); + assert(ptr != NULL); + + struct net_file_write_data * user = + (struct net_file_write_data*)userdata; + + if (user->file == NULL) { + fprintf(stderr, "net: Write failed, " + "file stream could not be found!\n"); + return 0; + } - struct net_fwrite_userdata *user = (struct net_fwrite_userdata*)userdata; - - if (user->file == NULL) { - fprintf(stderr, "net: Write failed, file stream could not be found!\n"); - return 0; - } - - return fwrite(ptr, nmemb, 1, user->file); + return fwrite(ptr, size, nmemb, user->file); } int net_init() { - CURLcode result = curl_global_init(CURL_GLOBAL_ALL); - if (result != CURLE_OK) - return -NETINITERR; - return 0; + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if (result != CURLE_OK) + return -NETINITERR; + return SUCCESS; } void net_shutdown() { - curl_global_cleanup(); + curl_global_cleanup(); } -int net_send_request(char *url, struct memory *mem, int write_opts) +int net_send_request(char *url, int write_opts, void* userdata) { - assert(mem != NULL); - CURL *curl; - curl = curl_easy_init(); - if (!curl) - return -CONINITERR; - - curl_easy_setopt(curl, CURLOPT_URL, url); + assert(url != NULL); + assert(userdata != NULL); + CURL *curl; + curl = curl_easy_init(); + + if (!curl) + return -CONINITERR; - if (write_opts == WRITE_OPT_MEMORY) - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback); - else if (write_opts == WRITE_OPT_FILE) - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_file_cb); - else { - fprintf(stderr, "net: a valid write_opts must be specified! current: %d", write_opts); - } - - curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)mem); - curl_easy_setopt(curl, CURLOPT_CA_CACHE_TIMEOUT, 604800L); - - CURLcode result; - result = curl_easy_perform(curl); + curl_easy_setopt(curl, CURLOPT_URL, url); + + if (write_opts == WRITE_OPT_MEMORY) { + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb); + } else if (write_opts == WRITE_OPT_FILE) { + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_file_cb); + } else { + fprintf(stderr, "net: a valid write_opts must be specified! " + "current: %d", + write_opts); + return -CONINITERR; + } + + curl_easy_setopt(curl, CURLOPT_WRITEDATA, userdata); + curl_easy_setopt(curl, CURLOPT_CA_CACHE_TIMEOUT, 604800L); + + CURLcode result; + result = curl_easy_perform(curl); - if (result != CURLE_OK) { - fprintf(stderr, "curl_easy_perform() failed: %s\n", - curl_easy_strerror(result)); - return -(CONREQERR + result); - } + if (result != CURLE_OK) { + fprintf(stderr, "curl_easy_perform() failed: %s\n", + curl_easy_strerror(result)); + return -(CONREQERR + result); + } - curl_easy_cleanup(curl); + curl_easy_cleanup(curl); - return 0; + return SUCCESS; } diff --git a/src/parser.c b/src/parser.c index c195764..602de51 100644 --- a/src/parser.c +++ b/src/parser.c @@ -87,7 +87,7 @@ void skip_whitespace(struct parser *parser) } int parser_init(struct parser *parser, - struct memory *mem, + struct string_view *mem, struct parser_backend *backend, void *userdata) { @@ -98,12 +98,12 @@ int parser_init(struct parser *parser, if ((backend->on_block_start == NULL) || (backend->on_block_end == NULL) || (backend->on_kv == NULL) || - (mem->buffer == NULL)) + (mem->buf == NULL)) return -PARINITERR; parser->pos = 0; - parser->src = mem->buffer; - parser->len = mem->size; + parser->src = mem->buf; + parser->len = mem->len; parser->state = PARSER_STATE_GRACEFUL; parser->backend = backend; parser->userdata = userdata; @@ -122,13 +122,15 @@ int parser_parse(struct parser *parser) char nc = parser_peek(parser, 1); if (c == '[' && nc == '/') - parser_parse_block_end(parser); + parser_parse_block_end(parser); else if (c == '[') - parser_parse_block_start(parser); + parser_parse_block_start(parser); else if (c == '-') - parser_parse_kv(parser); + parser_parse_kv(parser); + /* else if (c == '+') */ + /* parser_parse_blocklist(parser); */ else - parser_advance(parser, 1); + parser_advance(parser, 1); } if (parser->state == PARSER_STATE_ERROR) @@ -224,3 +226,12 @@ void parser_parse_kv(struct parser *parser) struct string_view value = sv_create(&parser->src[valpos], vallen); parser->backend->on_kv(&key, &value, parser->userdata); } + +/* void parser_parse_blocklist(struct parser *parser) */ +/* { */ +/* assert(parser != NULL); */ + + + +/* /\* parser->backend->on_blocklist(&value, parser->userdata); *\/ */ +/* } */ diff --git a/src/pkgman.c b/src/pkgman.c new file mode 100644 index 0000000..c21d0c5 --- /dev/null +++ b/src/pkgman.c @@ -0,0 +1,222 @@ +#include <pkgman.h> + +#include <net.h> +#include <parser.h> +#include <um.h> +#include <lib/sv.h> +#include <err.h> +#include <lib/url.h> +#include <lib/archive.h> +#include <cookbook.h> + +#include <assert.h> +#include <string.h> +#include <stdlib.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <gpgme.h> +#include <archive.h> +#include <archive_entry.h> + +#define UPSTREAM_URL "https://packages.0xinfinity.dev" + +int pkgman_upstream_check(const char *pkg) +{ + assert(pkg != NULL); + + int ret = -ERR; + + struct net_write_data mem = { 0 }; + if ((ret = net_send_request(UPSTREAM_URL "/list", + WRITE_OPT_MEMORY, + (void*)&mem)) != SUCCESS) + goto cleanup; + + struct um_user_data userdata = { 0 }; + struct parser parser; + struct parser_backend backend = um_backend(); + struct string_view parser_src = {0}; + parser_src.buf = mem.buffer; + parser_src.len = mem.size; + + parser_init(&parser, &parser_src, &backend, (void*)&userdata); + parser_parse(&parser); + + struct string_view sv_pkg = sv_create(pkg, strlen(pkg)); + + int found = 0; + + LL_FOREACH(manifest, &userdata.manifest) { + if (!current->data.key.buf) + continue; + + if (sv_equal(&sv_pkg, ¤t->data.key)) { + found = 1; + printf("Package '%s' found on upstream!\n", pkg); + ret = SUCCESS; + } + } + + if (!found) { + printf("Package '%s' not found!\n", pkg); + ret = -PKGNOTFND; + } + + + LL_FOREACH(manifest, &userdata.manifest) { + if (current->data.key.buf == NULL) + continue; + + free(current->data.key.buf); + free(current->data.value.buf); + } + + ll_manifest_free(&userdata.manifest); + + cleanup: + free(mem.buffer); + + return ret; +} + +int pkgman_download(const char *url, const char *dst) +{ + struct net_file_write_data fwdata; + + printf("url: %s, dst: %s\n", url, dst); + + fwdata.file = fopen(dst, "w"); // change to tmp dir + + if (!fwdata.file) { + return -PKGNOTFND; + } + + net_send_request(url, WRITE_OPT_FILE, (void*)&fwdata); + + fclose(fwdata.file); + return SUCCESS; +} + +int pkgman_install_pkg(const char *pkg) +{ + struct url path = {0}; + url_init(&path, "/tmp/pkgman"); + url_append_path(&path, pkg); + + struct url dst_path = {0}; + url_init(&dst_path, "/tmp/pkgman"); + url_append_path(&dst_path, pkg); + url_append(&dst_path, "-extract/"); + + mkdir(dst_path.buffer, 0777); + + pkg_extract(path.buffer, &dst_path); + + cookbook_run(&dst_path, "artifacts"); + cookbook_run(&dst_path, "install"); + + url_free(&dst_path); + url_free(&path); + return SUCCESS; +} + +int pkgman_upstream_integrity_download(const char *pkg) +{ + int ret = -ERR; + struct url url_pkg = {0}; + struct url url_sig = {0}; + + url_init(&url_pkg, UPSTREAM_URL); + url_append_path(&url_pkg, pkg); + url_append(&url_pkg, ".tar.zstd"); + + url_copy(&url_pkg, &url_sig); + url_append(&url_sig, ".sig"); + + struct url path_pkg = {0}; + struct url path_sig = {0}; + + url_init(&path_pkg, "/tmp/pkgman"); + url_append_path(&path_pkg, pkg); + + url_copy(&path_pkg, &path_sig); + url_append(&path_sig, ".sig"); + + ///// I need to abstract away the cache filemgmt feature in the future. + + mkdir("/tmp/pkgman/", 0777); + + //// + + if(pkgman_download(url_pkg.buffer, path_pkg.buffer) != SUCCESS) { + ret = -PKGNOTFND; + goto cleanup; + } + + printf("'%s' package downloaded.\n", pkg); + + // Download signature file + if(pkgman_download(url_sig.buffer, path_sig.buffer) != SUCCESS) { + ret = -PKGNOTFND; + goto cleanup; + } + + printf("Signature for '%s' downloaded.\n", url_sig.buffer); + + // Verify integrity + + gpgme_check_version(NULL); + + gpgme_ctx_t ctx; + + gpgme_new(&ctx); + gpgme_set_protocol(ctx, GPGME_PROTOCOL_OPENPGP); + + int fd = open(path_pkg.buffer, O_RDONLY); + int sigfd = open(path_sig.buffer, O_RDONLY); + + gpgme_data_t file; + gpgme_data_t sig; + + gpgme_data_new_from_fd(&file, fd); + gpgme_data_new_from_fd(&sig, sigfd); + + if (gpgme_op_verify(ctx, sig, file, NULL) == GPG_ERR_NO_ERROR) { + printf("Signature operation ran successfully.\n"); + } else { + printf("Integrity verification failed!\n"); + ret = -INTEGRITYERR; + gpgme_release(ctx); + goto cleanup; + } + + + gpgme_verify_result_t integ_res = gpgme_op_verify_result(ctx); + + // Checking the first signature for now should be fine. + //TODO: Revisit this. + if ((integ_res->signatures->summary & GPGME_SIGSUM_VALID) && + (integ_res->signatures->status == GPG_ERR_NO_ERROR)) { + printf("Integrity check successful, " + "package can be installed.\n"); + } else { + printf("Signature invalid!\n"); + ret = -INTEGRITYERR; + gpgme_release(ctx); + goto cleanup; + } + + ret = SUCCESS; + + gpgme_release(ctx); + + cleanup: + + url_free(&url_pkg); + url_free(&url_sig); + + url_free(&path_pkg); + url_free(&path_sig); + + return ret; +} |
