From dfff353cf081024f9e145629dd51ef08526bb54d Mon Sep 17 00:00:00 2001 From: Jack Jamison Date: Wed, 22 Jul 2026 14:54:42 -0400 Subject: basic download and hash with libcurl and libcrypto --- packages/swenu/swenu.sh | 2 +- snag/configure | 8 ++-- snag/scripts/get_pkg_attrs.sh | 4 +- snag/src/download_db.c | 101 ++++++++++++++++++++++++++++++++++++++++++ snag/src/download_db.h | 10 +++++ snag/src/main.c | 7 +-- snag/src/package.h | 7 ++- snag/src/package_db.c | 6 +-- snag/src/package_db.h | 4 +- snag/src/util.c | 14 ++++++ snag/src/util.h | 6 +-- todo.txt | 4 +- 12 files changed, 152 insertions(+), 21 deletions(-) create mode 100644 snag/src/download_db.c create mode 100644 snag/src/download_db.h diff --git a/packages/swenu/swenu.sh b/packages/swenu/swenu.sh index 42e61c4..ca6ed76 100644 --- a/packages/swenu/swenu.sh +++ b/packages/swenu/swenu.sh @@ -5,7 +5,7 @@ pkg_webpage="https://github.com/NAHTAIV3L/swenu" pkg_license="GPL-3.0" pkg_version=1.1.0 pkg_source_url="https://github.com/NAHTAIV3L/swenu/archive/refs/tags/$pkg_version.tar.gz" -pkg_source_hash="123456789" +pkg_source_checksum="22cf0055907efa0570d4108b87f34289fea3a4c9e95289708ce99cab902b2aab" configure() { echo "configuring swenu..." diff --git a/snag/configure b/snag/configure index 757df3a..3048f71 100755 --- a/snag/configure +++ b/snag/configure @@ -1,7 +1,7 @@ #!/bin/bash bin="snag" -libs="" +libs="libcurl libcrypto" builddir="build" PREFIX="/usr" @@ -19,17 +19,17 @@ CFLAGS_DEBUG="$CFLAGS -Wall -g -DDEBUG=1" write() { echo "$@" >> build.ninja; } -echo "$CFLAGS_DEBUG $(pkg-config --cflags $libs 2>/dev/null)" | tr ' ' '\n' > compile_flags.txt +echo "$CFLAGS_DEBUG $(pkg-config --cflags $libs)" | tr ' ' '\n' > compile_flags.txt rm -f build.ninja write "builddir = $builddir" -write "libs = $(pkg-config --libs $libs 2>/dev/null)" +write "libs = $(pkg-config --libs $libs)" write 'rule link' write ' description = LD $out' write ' command = gcc $libs $in -o $out' for target in $(echo "debug release"); do - eval write "cflags_$target = \$CFLAGS_${target^^} $(pkg-config --cflags $libs 2>/dev/null)" + eval write "cflags_$target = \$CFLAGS_${target^^} $(pkg-config --cflags $libs)" write "rule cc-$target" write ' deps = gcc' diff --git a/snag/scripts/get_pkg_attrs.sh b/snag/scripts/get_pkg_attrs.sh index 932c79c..8c505b5 100755 --- a/snag/scripts/get_pkg_attrs.sh +++ b/snag/scripts/get_pkg_attrs.sh @@ -13,7 +13,7 @@ fi . $1 # output package attributes (type char, value, null delimiter) -echoatt() { [ -n "$2" ] && printf "$1$2\0"; } +echoatt() { [ -n "$2" ] && printf "$1$2\0"; return 0; } echoatt i "$pkg_id" echoatt n "$pkg_name" echoatt d "$pkg_desc" @@ -21,4 +21,4 @@ echoatt w "$pkg_webpage" echoatt l "$pkg_license" echoatt v "$pkg_version" echoatt u "$pkg_source_url" -echoatt h "$pkg_source_hash" +echoatt c "$pkg_source_checksum" diff --git a/snag/src/download_db.c b/snag/src/download_db.c new file mode 100644 index 0000000..3f51347 --- /dev/null +++ b/snag/src/download_db.c @@ -0,0 +1,101 @@ +#include "download_db.h" + +#include "util.h" + +#include +#include +#include +#include +#include + +// curl context +CURL *curl = NULL; + +// libcrpyto context +EVP_MD_CTX *mdctx = NULL; +const EVP_MD *md = NULL; + +static size_t curl_write_cb(char* ptr, size_t size, size_t nmemb, void* usrdata) +{ + // write the curl data to the file in usr data + size_t written = fwrite(ptr, size, nmemb, (FILE *)usrdata); + + // update the hash with the new data + EVP_DigestUpdate(mdctx, ptr, written); + + return written; +} + +bool download_package(package_info_t* pkg) { + + if (curl == NULL) { + // initialize curl if it isn't already + CURLcode result = curl_global_init(CURL_GLOBAL_ALL); + if (result != CURLE_OK) die("failed to initialize curl: %s", curl_easy_strerror(result)); + curl = curl_easy_init(); // note(jqj): for now we reuse one easy curl object + if (curl == NULL) die("failed to initialize easy curl"); + + /* come back to this if we have init and deinit */ + /* curl_easy_cleanup(curl); */ + /* curl_global_cleanup(); */ + } + + if (mdctx == NULL) { + // initialize libcrypto context if it isn't already + mdctx = EVP_MD_CTX_new(); + md = EVP_sha256(); + + /* come back to this if we have init and deinit */ + /* EVP_MD_CTX_free(mdctx); */ + } + + // initialize up sha256 hash + EVP_DigestInit_ex(mdctx, md, NULL); + + // open file for writing and give it to the curl callback + // todo(jqj): get actual path with name + char* download_path = "download.tar.gz"; + FILE* download_file = fopen(download_path, "wb"); + if (download_file == NULL) die("failed open download file for writing (%s): %s", download_path, strerror(errno)); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_write_cb); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, download_file); + + // make curl request + // callback writes to the file and feeds the hash + curl_easy_setopt(curl, CURLOPT_URL, pkg->attrs.source_url); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + CURLcode result = curl_easy_perform(curl); + if(result != CURLE_OK) { + log("failed to download package with curl: %s", curl_easy_strerror(result)); + return false; + } + + // get sha256 hash + unsigned char hash[SHA256_DIGEST_LENGTH]; + unsigned int hash_len; + EVP_DigestFinal_ex(mdctx, hash, &hash_len); + + // get hexadecimal string of sha256 hash + char hash_hex[SHA256_DIGEST_LENGTH * 2 + 1]; + for(int i = 0; i < SHA256_DIGEST_LENGTH; i++) { + sprintf(hash_hex + i*2,"%02x", hash[i]); + } + hash_hex[SHA256_DIGEST_LENGTH * 2] = '\0'; + + // confirm that hash matches checksum + if (pkg->attrs.source_checksum != NULL) { + if (strcmp(pkg->attrs.source_checksum, hash_hex) == 0) { + print("checksum verified for %s: %s\n", pkg->attrs.id, hash_hex); + return true; + } else { + log("FAIL: bad checksum for %s: %s\n", pkg->attrs.id, hash_hex); + return false; + } + } else { + // todo(jqj): not sure if we will allow packages without hashes yet + log("WARNING: could not verify checksum for %s download because none was provided\n", pkg->attrs.id); + return true; + } + + return true; +} diff --git a/snag/src/download_db.h b/snag/src/download_db.h new file mode 100644 index 0000000..fbf7b03 --- /dev/null +++ b/snag/src/download_db.h @@ -0,0 +1,10 @@ +#ifndef DOWNLOAD_DB_H +#define DOWNLOAD_DB_H + +#include + +#include "package.h" + +bool download_package(package_info_t* pkg); + +#endif diff --git a/snag/src/main.c b/snag/src/main.c index 7d5ea58..0813032 100644 --- a/snag/src/main.c +++ b/snag/src/main.c @@ -1,8 +1,8 @@ -#include +#include "libargs/args.h" #include "util.h" -#include "libargs/args.h" #include "package_db.h" +#include "download_db.h" int cmd_install(char* cmd_name, ArgParser* parser); @@ -43,8 +43,9 @@ int cmd_install(char* cmd_name, ArgParser* parser) { char* pkgid = ap_get_arg_at_index(parser, i); package_info_t info; if (load_package_info(pkgid, &info)) { - print("Installing %s - %s\n", info.attrs.name, info.attrs.desc); print("Downloading '%s'\n", info.attrs.source_url); + download_package(&info); + print("Installing %s - %s\n", info.attrs.name, info.attrs.desc); run_package_install(&info); } } diff --git a/snag/src/package.h b/snag/src/package.h index 3ff58b6..b848ce6 100644 --- a/snag/src/package.h +++ b/snag/src/package.h @@ -1,3 +1,6 @@ +#ifndef PACKAGE_H +#define PACKAGE_H + typedef struct { char* id; char* name; @@ -6,7 +9,7 @@ typedef struct { char* license; char* version; char* source_url; - char* source_hash; + char* source_checksum; char buffer[4069]; } package_attrs_t; @@ -14,3 +17,5 @@ typedef struct { char* script_path; package_attrs_t attrs; } package_info_t; + +#endif diff --git a/snag/src/package_db.c b/snag/src/package_db.c index 0bae257..3974120 100644 --- a/snag/src/package_db.c +++ b/snag/src/package_db.c @@ -80,7 +80,7 @@ bool load_package_info(char* pkgid, package_info_t* info) { // wait for child to finish int wstatus; if (waitpid(pid, &wstatus, 0) < 0) die("waitpid failed: %s", strerror(errno)); - if (WEXITSTATUS(wstatus) != 0) die("the child failed to execute get_pkg_attr script"); + if (WEXITSTATUS(wstatus) != 0) die("the child failed to execute get_pkg_attr script: %d", WEXITSTATUS(wstatus)); // read all bytes from pipe into attr buffer ssize_t bytes_read = read(pipefds[0], info->attrs.buffer, sizeof(info->attrs.buffer)); @@ -108,8 +108,8 @@ bool load_package_info(char* pkgid, package_info_t* info) { info->attrs.version = &info->attrs.buffer[i]; break; case 'u': info->attrs.source_url = &info->attrs.buffer[i]; break; - case 'h': - info->attrs.source_hash = &info->attrs.buffer[i]; break; + case 'c': + info->attrs.source_checksum = &info->attrs.buffer[i]; break; } for(;info->attrs.buffer[i] && i < bytes_read; i++); i++; diff --git a/snag/src/package_db.h b/snag/src/package_db.h index bae1cdd..17ff58d 100644 --- a/snag/src/package_db.h +++ b/snag/src/package_db.h @@ -1,5 +1,5 @@ -#ifndef PKG_DB_H -#define PKG_DB_H +#ifndef PACKAGE_DB_H +#define PACKAGE_DB_H #include diff --git a/snag/src/util.c b/snag/src/util.c index 8cc9b18..e79fd34 100644 --- a/snag/src/util.c +++ b/snag/src/util.c @@ -45,6 +45,20 @@ void* _emalloc(int line, const char* file, size_t size, char* alloc_reason) { } #endif +void printout(const char* format, ...) { + va_list args; + va_start(args, format); + vfprintf(stdout, format, args); + va_end(args); +} + +void print(const char* format, ...) { + va_list args; + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); +} + char* vastrcat_(int dummy, ...) { va_list args; diff --git a/snag/src/util.h b/snag/src/util.h index eca4e4d..1a05ab0 100644 --- a/snag/src/util.h +++ b/snag/src/util.h @@ -20,9 +20,9 @@ void* _emalloc(int line, const char* file, size_t size, char* alloc_reason); #endif -// functions for outputting and displaying text -#define printout(fmt, ...) fprintf(stdout, fmt, ##__VA_ARGS__) -#define print(fmt, ...) fprintf(stderr, fmt, ##__VA_ARGS__) +// functions for outputting and displaying text, log is used for nonroutine bad things +void printout(const char* format, ...) __attribute__((format(printf, 1, 2))); +void print(const char* format, ...) __attribute__((format(printf, 1, 2))); // string functions #define vastrcat(...) vastrcat_(0, __VA_ARGS__, NULL) diff --git a/todo.txt b/todo.txt index 1d35394..845cbe3 100644 --- a/todo.txt +++ b/todo.txt @@ -5,8 +5,8 @@ SNAG - [ ] download packages to download db and verify hash - [ ] actually create install environment for packages in install script - [ ] snag api -- [ ] actually try to handle a proper package installation -- [ ] figure out how we handle make configuration, and tools for build scripts +- [ ] proper download with snag api (supports git download) +- [ ] proper installation - [ ] dependencies Things to come back to -- cgit v1.2.3