From 9282991429a92d6df17213acdc49fe687f42da6c Mon Sep 17 00:00:00 2001 From: Jack Jamison Date: Thu, 23 Jul 2026 15:34:43 -0400 Subject: download db is now source db, ready to start build --- snag/configure | 2 +- snag/readme.txt | 8 +-- snag/scripts/get_pkg_attrs.sh | 1 + snag/src/download_db.c | 119 ---------------------------------------- snag/src/download_db.h | 12 ----- snag/src/main.c | 9 ++-- snag/src/package.h | 11 ++-- snag/src/package_db.c | 6 ++- snag/src/source_db.c | 123 ++++++++++++++++++++++++++++++++++++++++++ snag/src/source_db.h | 10 ++++ 10 files changed, 155 insertions(+), 146 deletions(-) delete mode 100644 snag/src/download_db.c delete mode 100644 snag/src/download_db.h create mode 100644 snag/src/source_db.c create mode 100644 snag/src/source_db.h (limited to 'snag') diff --git a/snag/configure b/snag/configure index a140a28..ca3c638 100755 --- a/snag/configure +++ b/snag/configure @@ -1,7 +1,7 @@ #!/bin/sh # temp for testing -mkdir -p ./var/snag/{downloads,installs,builds} +mkdir -p ./var/snag/{sources,installs,builds} [ ! -e ./var/snag/packages ] && ln -s ../../../packages/ ./var/snag/packages bin="snag" diff --git a/snag/readme.txt b/snag/readme.txt index c64ced7..093af4a 100644 --- a/snag/readme.txt +++ b/snag/readme.txt @@ -14,7 +14,7 @@ why - list why this package is installed (manually installed, as a depe ## Important Directories Package Database - /var/snag/pkgs/ Install Database - /var/snag/installs/ -Download Database - /var/snag/downloads/ +Source Database - /var/snag/sources/ Logs/In Progress Builds - /var/snag/builds/ SNAG Configuration - /etc/snag/ @@ -25,7 +25,7 @@ Package Info/Metadata - the metadata encoded in the package description Package Attribute - a single key value property of the package info Install Database - the directory containing information on installed package Install Description - the data stored about a package in the install database -Download Database - the directory where package downloads are stored and cached +Source Database - the directory where package sources are stored and cached ## Interesting Features - switch between cli and tui mode mid run @@ -34,7 +34,7 @@ Download Database - the directory where package downloads are stored and cac ## Package Database and Description Format Each package has a directory in the package database, named by its `id`. Inside each package directory is the description script named `{id}.sh` -The description script should have functions for `configure`, `build`, and `install` +The description script can contain functions for `unpack`, `configure`, `build`, and `install` The description script should define variables for attributes with the name `pkg_{attr}` Packages have the following attributes: @@ -48,7 +48,7 @@ version - software version, in whatever format is needed. commit hashes are allo ### Snag Shell API TBD -## Download Database +## Source Database TBD ## Build Database diff --git a/snag/scripts/get_pkg_attrs.sh b/snag/scripts/get_pkg_attrs.sh index 8c505b5..d5e46fa 100755 --- a/snag/scripts/get_pkg_attrs.sh +++ b/snag/scripts/get_pkg_attrs.sh @@ -20,5 +20,6 @@ echoatt d "$pkg_desc" echoatt w "$pkg_webpage" echoatt l "$pkg_license" echoatt v "$pkg_version" +echoatt s "$pkg_source_name" echoatt u "$pkg_source_url" echoatt c "$pkg_source_checksum" diff --git a/snag/src/download_db.c b/snag/src/download_db.c deleted file mode 100644 index e0aa85e..0000000 --- a/snag/src/download_db.c +++ /dev/null @@ -1,119 +0,0 @@ -#include "download_db.h" - -#include "util.h" - -#include -#include -#include -#include -#include -#include - -// note: fixed location of download database for now -const char download_db_path[] = "./var/snag/downloads"; - -// libcrpyto context -EVP_MD_CTX *mdctx = NULL; -const EVP_MD *md = NULL; - -// curl context -CURL *curl = NULL; - -static size_t download_write_callback(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 package_get_download(package_info_t* pkg, package_download_t* download) { - memset(download, 0, sizeof(*download)); - - // construct path of download - // note(jqj): extension is fixed to tar gz until the switch out download - // attrs for the swenu api download command which will specify - // more information - char* download_path = vastrcat(download_db_path, "/", pkg->attrs.id, "-", pkg->attrs.version, ".tar.gz"); - - // return download if it already exists in database - if (access(download_path, F_OK) == 0) { - // todo(jqj): verify checksum anyway - download->download_path = download_path; - return true; - } else { - print("Downloading %s\n", pkg->attrs.source_url); - } - - // initialize curl if it isn't already - if (curl == NULL) { - 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(); */ - } - - // initialize libcrypto context if it isn't already - if (mdctx == NULL) { - 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 - FILE* download_file = fopen(download_path, "wb"); - if (download_file == NULL) die("failed open download file for writing (%s): %s", download_path, strerror(errno)); - - // make curl request with url and output file - // 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); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, download_write_callback); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, download_file); - CURLcode result = curl_easy_perform(curl); - fclose(download_file); - if(result != CURLE_OK) { - // todo(jqj): handle better, maybe try again - print("failed to download package with curl: %s", curl_easy_strerror(result)); - goto fail; - } - - // get sha256 hash - unsigned char hash[SHA256_DIGEST_LENGTH]; - unsigned int hash_len; - EVP_DigestFinal_ex(mdctx, hash, &hash_len); - - // make 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 checksum matches - assert(pkg->attrs.source_checksum != NULL); - if (strcmp(pkg->attrs.source_checksum, hash_hex) == 0) { - print("checksum verified for download: %s\n", hash_hex); - download->download_path = download_path; - return true; - } else { - print("FAIL: bad checksum for download: %s\n", hash_hex); - goto fail; - } - - fail: - if (access(download_path, F_OK) == 0) remove(download_path); - free(download_path); - return false; -} diff --git a/snag/src/download_db.h b/snag/src/download_db.h deleted file mode 100644 index d45817f..0000000 --- a/snag/src/download_db.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef DOWNLOAD_DB_H -#define DOWNLOAD_DB_H - -#include "package.h" - -typedef struct { - char* download_path; -} package_download_t; - -bool package_get_download(package_info_t* pkg, package_download_t* download); - -#endif diff --git a/snag/src/main.c b/snag/src/main.c index c5a0736..5835859 100644 --- a/snag/src/main.c +++ b/snag/src/main.c @@ -3,7 +3,7 @@ #include "util.h" #include "package_db.h" #include "build_db.h" -#include "download_db.h" +#include "source_db.h" int cmd_install(char* cmd_name, ArgParser* parser); @@ -46,9 +46,10 @@ int cmd_install(char* cmd_name, ArgParser* parser) { if (load_package_info(pkgid, &info)) { print("Installing %s - %s\n", info.attrs.name, info.attrs.desc); - // download or retrieve package download - package_download_t download; - if (!package_get_download(&info, &download)) continue; + // confirm that package source exists or is downloaded + if (!verify_source(&info.attrs.source) && !download_source(&info.attrs.source)) { + continue; + } // run the package install package_run_build(&info); diff --git a/snag/src/package.h b/snag/src/package.h index a96c5c1..54009c1 100644 --- a/snag/src/package.h +++ b/snag/src/package.h @@ -3,8 +3,12 @@ #include -// this struct is the "handle" of a package to be passed into other package -// functions +typedef struct { + char* name; + char* url; + char* checksum; +} source_info_t; + typedef struct { char* script_path; struct { @@ -15,8 +19,7 @@ typedef struct { char* webpage; char* license; char* version; - char* source_url; - char* source_checksum; + source_info_t source; } attrs; } package_info_t; diff --git a/snag/src/package_db.c b/snag/src/package_db.c index 533b66a..036e284 100644 --- a/snag/src/package_db.c +++ b/snag/src/package_db.c @@ -105,10 +105,12 @@ bool load_package_info(char* pkgid, package_info_t* info) { info->attrs.license = &info->attrs.buffer[i]; break; case 'v': info->attrs.version = &info->attrs.buffer[i]; break; + case 's': + info->attrs.source.name = &info->attrs.buffer[i]; break; case 'u': - info->attrs.source_url = &info->attrs.buffer[i]; break; + info->attrs.source.url = &info->attrs.buffer[i]; break; case 'c': - info->attrs.source_checksum = &info->attrs.buffer[i]; break; + info->attrs.source.checksum = &info->attrs.buffer[i]; break; } while(info->attrs.buffer[i] != '\0' && i < bytes_read) i++; i++; diff --git a/snag/src/source_db.c b/snag/src/source_db.c new file mode 100644 index 0000000..ab7ec99 --- /dev/null +++ b/snag/src/source_db.c @@ -0,0 +1,123 @@ +#include "source_db.h" + +#include "util.h" + +#include +#include +#include +#include +#include +#include + +// libcrpyto context +EVP_MD_CTX *mdctx = NULL; +const EVP_MD *md = NULL; +void ensure_libcrypto(); + +// curl context +CURL *curl = NULL; +void ensure_libcurl(); + +// note: fixed location of source database for now +const char source_db_path[] = "./var/snag/sources"; +char* get_source_path(source_info_t* source) { return join_path(source_db_path, source->name); } + +bool verify_source(source_info_t* source) { + // verify source exists + char* source_path = get_source_path(source); + bool verified = access(source_path, F_OK) == 0; + + // todo(jqj): actually verify checksum + free(source_path); + return verified; +} + +static size_t download_write_callback(char* ptr, size_t size, size_t nmemb, void* usrdata); +bool download_source(source_info_t* source) { + + char* source_path = get_source_path(source); + print("Downloading %s\n", source->url); + + // initialize libcurl and libcrypto if they aren't already + ensure_libcurl(); + ensure_libcrypto(); + + // initialize sha256 hash + EVP_DigestInit_ex(mdctx, md, NULL); + + // open file for writing + FILE* download_file = fopen(source_path, "wb"); + if (download_file == NULL) die("failed open download file for writing (%s): %s", source_path, strerror(errno)); + + // make curl request with url and output file + // callback writes to the file and feeds the hash + curl_easy_setopt(curl, CURLOPT_WRITEDATA, download_file); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, download_write_callback); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + curl_easy_setopt(curl, CURLOPT_URL, source->url); + CURLcode result = curl_easy_perform(curl); + fclose(download_file); + if(result != CURLE_OK) { + // todo(jqj): handle better, maybe try again + print("failed to download package with curl: %s", curl_easy_strerror(result)); + goto fail; + } + + // get sha256 hash + unsigned char hash[SHA256_DIGEST_LENGTH]; + unsigned int hash_len; + EVP_DigestFinal_ex(mdctx, hash, &hash_len); + + // make 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 checksum matches + assert(source->checksum != NULL); + if (strcmp(source->checksum, hash_hex) == 0) { + print("checksum verified for download: %s\n", hash_hex); + return true; + } else { + print("FAIL: bad checksum for download: %s\n", hash_hex); + goto fail; + } + +fail: + if (access(source_path, F_OK) == 0) remove(source_path); + free(source_path); + return false; +} + +static size_t download_write_callback(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; +} + +void ensure_libcrypto() { + if (mdctx == NULL) { + mdctx = EVP_MD_CTX_new(); + md = EVP_sha256(); + /* come back to this if we have init and deinit */ + /* EVP_MD_CTX_free(mdctx); */ + } +} + +void ensure_libcurl() { + if (curl == NULL) { + 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(); */ + } +} diff --git a/snag/src/source_db.h b/snag/src/source_db.h new file mode 100644 index 0000000..e22f221 --- /dev/null +++ b/snag/src/source_db.h @@ -0,0 +1,10 @@ +#ifndef SOURCE_DB_H +#define SOURCE_DB_H + +#include "package.h" + +bool verify_source(source_info_t* source); +bool download_source(source_info_t* source); +bool unpack_source(source_info_t* source, char* dest_path); + +#endif -- cgit v1.2.3