From 08d1240456383bc9554840a5255f7f87114ade92 Mon Sep 17 00:00:00 2001 From: 0x221E Date: Sun, 10 May 2026 18:08:18 +0200 Subject: feature: add URL structure and refactor main logic to pkgman.c Added url structures, cookbook, archive management to automate the installation process of packages. Refactored main logic from main.c to pkgman.c for cleaner seperation of concerns and future library integration of pkgman to other projects. --- Makefile | 2 +- include/cookbook.h | 8 ++ include/lib/archive.h | 8 ++ include/net.h | 6 +- include/parser.h | 2 +- include/pkgman.h | 8 ++ src/cookbook.c | 70 ++++++++++++++++ src/lib/archive.c | 54 ++++++++++++ src/lib/url.c | 91 +++++++++++++++++++++ src/main.c | 97 ++++++++++------------ src/net.c | 115 ++++++++++++++------------ src/parser.c | 27 ++++-- src/pkgman.c | 222 ++++++++++++++++++++++++++++++++++++++++++++++++++ 13 files changed, 589 insertions(+), 121 deletions(-) create mode 100644 include/cookbook.h create mode 100644 include/lib/archive.h create mode 100644 include/pkgman.h create mode 100644 src/cookbook.c create mode 100644 src/lib/archive.c create mode 100644 src/lib/url.c create mode 100644 src/pkgman.c diff --git a/Makefile b/Makefile index 1720a5f..b2c820a 100644 --- a/Makefile +++ b/Makefile @@ -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 + +#include +#include +#include +#include +#include + +#include +#include + +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 + +#include +#include + +#include + +#include +#include + +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 + +#include +#include +#include +#include +#include + +//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'; +} diff --git a/src/main.c b/src/main.c index 4ed2ff4..c866c89 100644 --- a/src/main.c +++ b/src/main.c @@ -2,12 +2,17 @@ #include #include +#include +#include +#include +#include #include #include #include #include #include +#include /** * Install package: @@ -19,7 +24,10 @@ * Update: * pkgman update * pkgman update - * + * + * Kernel Update: + * pkgman kupdate + * * Fetch: * pkgman fetch * @@ -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])); - - int found = 0; - - LL_FOREACH(manifest, &userdata.manifest) { - if (!current->data.key.buf) - continue; - - 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); - - free(mem.buffer); + 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); + + if (ret != SUCCESS) + return ret; + + ret = pkgman_install_pkg(pkg); + + if (ret != SUCCESS) + return ret; + + 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 \n"); + printf(" pkgman build\n"); printf(" pkgman update \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; } diff --git a/src/net.c b/src/net.c index 6f14089..6d566f2 100644 --- a/src/net.c +++ b/src/net.c @@ -6,83 +6,90 @@ #include #include -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); - - 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); + 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; + } + + 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 + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#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; +} -- cgit v1.2.3