From 33647dd2c4f4af7c7cb7d99c1a6dac7f1d059a67 Mon Sep 17 00:00:00 2001 From: "@syxhe" Date: Mon, 20 Oct 2025 15:51:49 -0500 Subject: Switch to unity build --- src/Makefile | 19 +- src/encryption.c | 544 ++++++++++++++++++++++++++++++------------------------- src/encryption.h | 185 ------------------- src/shared.c | 177 ++++++++++++++++-- src/shared.h | 191 ------------------- src/threadpool.c | 148 ++++++++++++--- src/threadpool.h | 160 ---------------- 7 files changed, 590 insertions(+), 834 deletions(-) delete mode 100644 src/encryption.h delete mode 100644 src/shared.h delete mode 100644 src/threadpool.h (limited to 'src') diff --git a/src/Makefile b/src/Makefile index 0ee5897..9293e55 100644 --- a/src/Makefile +++ b/src/Makefile @@ -6,35 +6,24 @@ SHELL := /usr/bin/env # RELEASE_CFLAGS := -O3 -fipa-pta -fipa-cp -fuse-linker-plugin -flto=auto # RELEASE_LDFLAGS := -fuse-linker-plugin -flto=auto -CFLAGS = -std=c2x -Wall -Wextra -Wpedantic -pedantic-errors -fanalyzer -Wanalyzer-too-complex -ggdb -g3 -O0 -DEPFLAGS = -MT $@ -MMD -MP -MF $*.d +CFLAGS = -std=c2x -Wall -Wextra -Wpedantic -pedantic-errors -fanalyzer -Wanalyzer-too-complex -ggdb -g3 -O0 CFLAGS += $$(pkg-config --cflags libsodium) LDLIBS += $$(pkg-config --libs-only-l libsodium) LDFLAGS += $$(pkg-config --libs-only-L libsodium) SOURCES := $(wildcard *.c) -OBJECTS := $(patsubst %.c,%.o,$(SOURCES)) -DEPS := $(patsubst %.c,%.d,$(SOURCES)) - -COMPILE.c = $(CC) $(DEPFLAGS) $(CFLAGS) -c .PHONY: all c clean val .DELETE_ON_ERROR: .ONESHELL: all: main -main: $(OBJECTS) - -%.o: %.c %.d - $(COMPILE.c) $< - -$(DEPS): -include $(wildcard $(DEPS)) -# Adopted from https://make.mad-scientist.net/papers/advanced-auto-dependency-generation/ +main: + echo "Need to update makefile to work with unity build" c clean: - @-rm -rv main $(OBJECTS) $(DEPS) $(wildcard *.test*) $(wildcard *.enc) + @-rm -rv main $(wildcard *.test*) $(wildcard *.enc) val: $(MAKE) all diff --git a/src/encryption.c b/src/encryption.c index 65b8843..382f6d5 100644 --- a/src/encryption.c +++ b/src/encryption.c @@ -1,12 +1,12 @@ /** * @file encryption.c * @author syxhe (https://t.me/syxhe) - * @brief *Implementing `encryption.h`* + * @brief A collection of all encryption related functions * @version 0.1 * @date 2025-06-09 - * + * * @copyright Copyright (c) 2025 - * + * */ // TODO: Go back and make sure every function has proper error handling @@ -14,11 +14,16 @@ // I need to make sure every single function in this file returns with an indicated error instead of nuking the whole program with // error() +#ifndef __VXGG_REWRITE___ENCRYPTION_C___1481879318188___ +#define __VXGG_REWRITE___ENCRYPTION_C___1481879318188___ + + #define _GNU_SOURCE -#include "shared.h" -#include "encryption.h" -#include "threadpool.h" +#define TPSIZE (1<<13) + +#include "shared.c" +#include "threadpool.c" #include @@ -33,16 +38,91 @@ #include #include +/// Determines whether any function that calls libsodium functions also checks to make sure libsodium is actually initialized. May +/// cause unexpected issues with early exiting due to libsodium failing to initialize properly. It's recommended that you just +/// manually run `sodium_init()` in some main or init function of your own so that you can deal with a potential error yourself +#define ___VXGG___ALWAYS_CHECK_LIBSODIUM___ 1 + +/// Grants access to the `vxgg_setsodiumfailcb` function, which can be used to set a custom callback for what to do when libsodium +/// fails upon initialization +#define ___VXGG___USE_CLS_CALLBACK___ 1 + +/// Chunk size for en/decryption. I originally wanted to use st_blksize from stat(), but given that those chunks may be of different +/// sizes between computers / filesystems / architectures / files, it's easier to just have this be a consistent macro +#define CHUNKSIZE (1 << 9) + +// Fuck reading from a file. Even if someone ran strings on the binary and got this they wouldn't be able to regenerate the key +//! A list of possible words for password creation +#define PASSWORD_WORDS (\ + (const char * const []){\ + "the", "of", "to", "and", "for", "our", "their", "has", "in", "he", "a", "them", "that", "these", "by", "have", "we", \ + "us", "people", "which", "all", "is", "with", "laws", "be", "are", "his", "states", "on", "they", "right", "it", "from", \ + "government", "such", "among", "powers", "most", "an", "time", "should", "new", "as", "been", "colonies", "assent", \ + "large", "at", "independent", "free", "united", "when", "mankind", "hold", "rights", "governments", "consent", "its", \ + "long", "themselves", "abolishing", "usurpations", "absolute", "repeated", "this", "world", "refused", "pass", "other", \ + "others", "without", "justice", "peace", "power", "seas", "war", "do", "declaration", "america", "becomes", "necessary", \ + "political", "equal", "declare", "causes", "separation", "men", "happiness", "any", "form", "alter", "or", "will", \ + "forms", "same", "object", "off", "necessity", "history", "great", "britain", "tyranny", "over", "public", "good", \ + "unless", "suspended", "so", "would", "legislature", "only", "legislative", "bodies", "purpose", "into", "dissolved", \ + "state", "endeavoured", "refusing", "hither", "conditions", "establishing", "offices", "out", "armies", "legislatures", \ + "render", "jurisdiction", "foreign", "acts", "pretended", "trial", "inhabitants", "cases", "transporting", "rule", \ + "declaring", "here", "protection", "against", "lives", "circumstances", "ages", "totally", "friends", "brethren", "whose", \ + "every", "may", "therefore", "ought", "unanimous", "thirteen", "course", "human", "events", "one", "dissolve", "bands", \ + "connected", "another", "assume", "earth", "separate", "station", "nature", "natures", "god", "entitle", "decent", \ + "respect", "opinions", "requires", "impel", "truths", "self", "evident", "created", "endowed", "creator", "certain", \ + "unalienable", "life", "liberty", "pursuit", "secure", "instituted", "deriving", "just", "governed", "whenever", \ + "destructive", "ends", "abolish", "institute", "laying", "foundation", "principles", "organizing", "shall", "seem", \ + "likely", "effect", "safety", "prudence", "indeed", "dictate", "established", "not", "changed", "light", "transient", \ + "accordingly", "experience", "hath", "shewn", "more", "disposed", "suffer", "while", "evils", "sufferable", "than", \ + "accustomed", "but", "train", "abuses", "pursuing", "invariably", "evinces", "design", "reduce", "under", "despotism", \ + "duty", "throw", "provide", "guards", "future", "security", "patient", "sufferance", "now", "constrains", "former", \ + "systems", "present", "king", "injuries", "having", "direct", "establishment", "prove", "let", "facts", "submitted", \ + "candid", "wholesome", "forbidden", "governors", "immediate", "pressing", "importance", "operation", "till", "obtained", \ + "utterly", "neglected", "attend", "accommodation", "districts", "those", "relinquish", "representation", "inestimable", \ + "formidable", "tyrants", "called", "together", "places", "unusual", "uncomfortable", "distant", "depository", "records", \ + "sole", "fatiguing", "compliance", "measures", "representative", "houses", "repeatedly", "opposing", "manly", "firmness", \ + "invasions", "after", "dissolutions", "cause", "elected", "whereby", "incapable", "annihilation", "returned", "exercise", \ + "remaining", "mean", "exposed", "dangers", "invasion", "convulsions", "within", "prevent", "population", "obstructing", \ + "naturalization", "foreigners", "encourage", "migrations", "raising", "appropriations", "lands", "obstructed", \ + "administration", "judiciary", "made", "judges", "dependent", "alone", "tenure", "amount", "payment", "salaries", \ + "erected", "multitude", "sent", "swarms", "officers", "harrass", "eat", "substance", "kept", "times", "standing", \ + "affected", "military", "superior", "civil", "combined", "subject", "constitution", "unacknowledged", "giving", \ + "legislation", "quartering", "armed", "troops", "protecting", "mock", "punishment", "murders", "commit", "cutting", \ + "trade", "parts", "imposing", "taxes", "depriving", "many", "benefits", "jury", "beyond", "tried", "offences", "system", \ + "english", "neighbouring", "province", "therein", "arbitrary", "enlarging", "boundaries", "once", "example", "fit", \ + "instrument", "introducing", "taking", "away", "charters", "valuable", "altering", "fundamentally", "suspending", "own", \ + "invested", "legislate", "whatsoever", "abdicated", "waging", "plundered", "ravaged", "coasts", "burnt", "towns", \ + "destroyed", "mercenaries", "compleat", "works", "death", "desolation", "already", "begun", "cruelty", "perfidy", \ + "scarcely", "paralleled", "barbarous", "unworthy", "head", "civilized", "nation", "constrained", "fellow", "citizens", \ + "taken", "captive", "high", "bear", "arms", "country", "become", "executioners", "fall", "hands", "excited", "domestic", \ + "insurrections", "amongst", "bring", "frontiers", "merciless", "indian", "savages", "known", "warfare", "undistinguished", \ + "destruction", "sexes", "stage", "oppressions", "petitioned", "redress", "humble", "terms", "petitions", "answered", \ + "injury", "prince", "character", "thus", "marked", "act", "define", "tyrant", "unfit", "ruler", "nor", "wanting", \ + "attentions", "brittish", "warned", "attempts", "extend", "unwarrantable", "reminded", "emigration", "settlement", \ + "appealed", "native", "magnanimity", "conjured", "ties", "common", "kindred", "disavow", "inevitably", "interrupt", \ + "connections", "correspondence", "too", "deaf", "voice", "consanguinity", "must", "acquiesce", "denounces", "rest", \ + "enemies", "representatives", "general", "congress", "assembled", "appealing", "supreme", "judge", "rectitude", \ + "intentions", "name", "authority", "solemnly", "publish", "absolved", "allegiance", "british", "crown", "connection", \ + "between", "full", "levy", "conclude", "contract", "alliances", "establish", "commerce", "things", "support", "firm", \ + "reliance", "divine", "providence", "mutually", "pledge", "each", "fortunes", "sacred", "honor"\ + }\ +) +//! Short macro for getting the `PASSWORD_WORDS` array size +#define PASSWORD_WORDS_LEN (STATIC_ARRAY_LEN(PASSWORD_WORDS)) + #if ___VXGG___ALWAYS_CHECK_LIBSODIUM___ > 0 #if ___VXGG___USE_CLS_CALLBACK___ > 0 +//! Definition for the callback function that fires when a call to checksodium fails +typedef void (*vxgg_naclfailcb)(void*); + /** * @brief Default sodium init fail callback for use in `checksodiumcb()` - * + * * @param none Unused param to fit callback spec */ static void naclfaildefault(void *none) { - none = none; // Makes gcc happy + none = none; // Makes gcc happy if(___VXGG___VERBOSE_ERRORS___) error(1, ENOTSUP, " Couldn't initialize sodium for some reason. Quitting..."); exit(EXIT_FAILURE); @@ -50,7 +130,7 @@ static void naclfaildefault(void *none) { /** * @brief Internal function to deal with the `___VXGG___USE_CLS_CALLBACK___` macro - * + * * `checksodiumcb()` runs the sodium function `sodium_init()` and on error calls the provided callback with the provided data. The * callback and data default to `naclfaildefault` and `NULL`, but can be changed when the `set` parameter is non-zero. When `set` * is zero, the sodium init check is preformed @@ -59,11 +139,11 @@ static void naclfaildefault(void *none) { * macros are both greater than 0 when compiling, and 2: a function in `encryption.c` calls a function originating from sodium. This * function exists as a way to deal with sodium failing yourself, instead of instantly calling `exit()`. If you don't care to handle * it, or are initializing sodium yourself, this is unnecessary - * + * * @param callback A callback to be ran when sodium fails to initialize itself. Ignored if `set` is zero. Must be non-null when `set` is non-zero * @param data Data to be passed to the callback when it is fired. Ignored if `set` is zero. May be null * @param set Flag on whether to check sodium or to set a new callback and data pair. Checks sodium when zero, sets callback & data when non-zero - * @retval int + * @retval int */ static int checksodiumcb(vxgg_naclfailcb const callback, void *data, unsigned char set) { static vxgg_naclfailcb cb = naclfaildefault; @@ -97,9 +177,9 @@ void vxgg_setsodiumfailcb(vxgg_naclfailcb cb, void *data) { * @brief Simple function to check if sodium has been properly initialized * * `checksodium()` will run in functions located in `encryption.h` only when the macro `___VXGG___ALWAYS_CHECK_LIBSODIUM___` is greater - * than zero when compiling. It will call the `checksodiumcb()` function if compiled with the `___VXGG___USE_CLS_CALLBACK___` macro. - * When called, checksodium will run `sodium_init()`, and will either run the user-defined callback or `XALLOC_EXIT`. - * + * than zero when compiling. It will call the `checksodiumcb()` function if compiled with the `___VXGG___USE_CLS_CALLBACK___` macro. + * When called, checksodium will run `sodium_init()`, and will either run the user-defined callback or `XALLOC_EXIT`. + * */ static void checksodium(void) { #if ___VXGG___USE_CLS_CALLBACK___ > 0 @@ -112,17 +192,30 @@ static void checksodium(void) { } #endif - + return; } #endif +/** + * @brief open() with the flags O_TMPFILE, O_WRONLY, O_CLOEXEC, and O_SYNC. Opened with mode S_IRUSR, S_IWUSR + * + * @param dest The filename the new descriptor should have. Must be non-null + * @retval (int)[-1,int] A new file descriptor. -1 on error + */ int maketmp(const char * const dest) { if(!dest) ERRRET(EINVAL, -1); return open(dest, (O_TMPFILE | O_WRONLY | O_CLOEXEC | O_SYNC), (S_IRUSR | S_IWUSR)); } +/** + * @brief Link a file descriptor into the filesystem + * + * @param target New filename the descriptor should have + * @param tgfd The file descriptor to link + * @retval (int)[-1, 0] 0 on success, -1 on error + */ int linkto(const char * const target, int tgfd) { if(!target) ERRRET(EINVAL, -1); @@ -131,6 +224,9 @@ int linkto(const char * const target, int tgfd) { if(!path) ERROR(1, errno, " Couldn't get path to move file into system",); remove(target); // Make sure an old version isn't sticking around (it's not catastrophic if this fails, but it should be noted or logged somewhere) + // TODO: This is bad. If a file gets deleted and the program crashes before the new one can get linked into the fs, the data is lost. + // I really should write a function entirely dedicated to dealing with linking + int res = linkat(AT_FDCWD, path, AT_FDCWD, target, AT_SYMLINK_FOLLOW); free(path); return res; @@ -150,6 +246,132 @@ static void __ucl_fclose(void *file) { return; } +/** + * @brief Encrypt src to dst using libsodium's xchacha encryption suite + * + * @param src File to encrypt + * @param dst Destination to write encrypted file + * @param key Key for encryption + * @retval (int)[-1, 0] Returns 0 on success, sets errno and returns -1 on error + */ +int encrypttofile(FILE *src, FILE *dst, const unsigned char key[crypto_secretstream_xchacha20poly1305_KEYBYTES]) { + if(!src || !dst || !key) ERRRET(EINVAL, -1); + #if ___VXGG___ALWAYS_CHECK_LIBSODIUM___ > 0 + checksodium(); + #endif + + unsigned char buf[CHUNKSIZE], cbuf[CHUNKSIZE + crypto_secretstream_xchacha20poly1305_ABYTES]; + unsigned char header[crypto_secretstream_xchacha20poly1305_HEADERBYTES]; + crypto_secretstream_xchacha20poly1305_state state; + unsigned long long cbuflen; + unsigned char tag; + size_t bytesread; + int eof; + + // Write the header + crypto_secretstream_xchacha20poly1305_init_push(&state, header, key); + if(fwrite(header, 1, sizeof(header), dst) < sizeof(header)) { + if(ferror(dst)) { + WARN(errno, " Could not write header",); + return -1; + } + } + + // Encrypt each chunk + do { + if((bytesread = fread(buf, 1, sizeof(buf), src)) < sizeof(buf)) + if(ferror(src)) { + WARN(errno, " Could not read from source",); + return -1; + } + eof = feof(src); + tag = eof ? crypto_secretstream_xchacha20poly1305_TAG_FINAL : 0; + + crypto_secretstream_xchacha20poly1305_push(&state, cbuf, &cbuflen, buf, bytesread, NULL, 0, tag); + if(fwrite(cbuf, 1, (size_t)cbuflen, dst) < (size_t)cbuflen) + if(ferror(dst)) { + WARN(errno, " Could not write to target",); + return -1; + } + } while (!eof); + + return 0; +} + +/** + * @brief Decrypt src to dst using libsodium's xchacha encryption suite + * + * @param src File to decrypt + * @param dst Destination to write decrypted file + * @param key Key used to encrypt + * @retval (int)[-1, 0] Returns 0 on success, sets errno and returns -1 on error + */ +int decrypttofile(FILE *src, FILE *dst, const unsigned char key[crypto_secretstream_xchacha20poly1305_KEYBYTES]) { + if(!src || !dst || !key) ERRRET(EINVAL, -1); + #if ___VXGG___ALWAYS_CHECK_LIBSODIUM___ > 0 + checksodium(); + #endif + + unsigned char cbuf[CHUNKSIZE + crypto_secretstream_xchacha20poly1305_ABYTES], buf[CHUNKSIZE]; + unsigned char header[crypto_secretstream_xchacha20poly1305_HEADERBYTES]; + crypto_secretstream_xchacha20poly1305_state state; + unsigned long long buflen; + unsigned char tag; + size_t bytesread; + int eof; + + // Read the header + if(fread(header, 1, sizeof(header), src) < sizeof(header)) { + if(ferror(src)) { + WARN(errno, " Couldn't read header", ); + return -1; + } + } + + // Make sure the header isn't fuckey + if(crypto_secretstream_xchacha20poly1305_init_pull(&state, header, key) != 0) { + WARN(errno, " Incomplete header", ); + return -1; + } + + // Decrypt each chunk + do { + if((bytesread = fread(cbuf, 1, sizeof(cbuf), src)) < sizeof(cbuf)) { + if(ferror(src)) { + WARN(errno, " Ran into problem reading for decryption", ); + return -1; + } + } + eof = feof(src); + + if (crypto_secretstream_xchacha20poly1305_pull(&state, buf, &buflen, &tag, cbuf, bytesread, NULL, 0) != 0) { + WARN(errno, " Corrupted chunk", ); + return -1; + } + + if(tag == crypto_secretstream_xchacha20poly1305_TAG_FINAL && !eof) { + WARN(errno, " End of stream before end of file", ); + return -1; + } + if(eof && tag != crypto_secretstream_xchacha20poly1305_TAG_FINAL) { + WARN(errno, " End of file before end of stream", ); + return -1; + } + + fwrite(buf, 1, (size_t)buflen, dst); + } while(! eof); + + return 0; +} + +/** + * @brief Encrypt file at `target` to `output` using Linux's named temp file system to do it in the background + * + * @param target + * @param output + * @param key + * @retval (int)[,] + */ int encryptviatmp(const char * const target, const char * const output, const unsigned char key[crypto_secretstream_xchacha20poly1305_KEYBYTES]) { #if ___VXGG___ALWAYS_CHECK_LIBSODIUM___ > 0 checksodium(); @@ -203,7 +425,7 @@ int encryptviatmp(const char * const target, const char * const output, const un // Not going to bother changing this, it probably is catastrophic if an error happens when it shouldn't if(encrypttofile(src, dst, key) < 0) ERROR(1, ENOTRECOVERABLE, " I don't even have a way to cause an error here. How did you do it?",); - + // Link the temp file into the system if(linkto(output, tfd) < 0) WARN(errno, " Could not link \"%s\" into system after encryption", , output); @@ -213,21 +435,29 @@ int encryptviatmp(const char * const target, const char * const output, const un fclose(src); // fclose alco closes fd and tfd, as fdopen does not dup the file descriptors ); - + cleanup_CNDFIRE(); if(cleanup_ERRORFLAGGED) return -1; - + return 0; } +/** + * @brief Decrypt the file at `encrypted` to `target` + * + * @param encrypted + * @param target + * @param key + * @retval (int)[,] + */ int decryptto(const char * const target, const char * const output, const unsigned char key[crypto_secretstream_xchacha20poly1305_KEYBYTES]) { if(!target || !output || !key) ERRRET(EINVAL, -1); #if ___VXGG___ALWAYS_CHECK_LIBSODIUM___ > 0 checksodium(); #endif - + cleanup_CREATE(10); FILE *src, *dst; int fdst; @@ -276,7 +506,7 @@ int decryptto(const char * const target, const char * const output, const unsign if(cleanup_ERRORFLAGGED) return -1; - // Note: If an error were to theoretically occur, which shouldn't be possible but I'm covering my bases here, after the + // Note: If an error were to theoretically occur, which shouldn't be possible but I'm covering my bases here, after the // `dst = fdopen` line, a double close on the temp file descriptor would occur. I've been told that this is not catastrophic, // and considering how my multithreading works it *should* be fine, but it very well could cause problems. The easy solution is // to man up and just write another cleanup function to pop the last thing off the stack, but again this is an error I can't @@ -285,108 +515,13 @@ int decryptto(const char * const target, const char * const output, const unsign return 0; } -int encrypttofile(FILE *src, FILE *dst, const unsigned char key[crypto_secretstream_xchacha20poly1305_KEYBYTES]) { - if(!src || !dst || !key) ERRRET(EINVAL, -1); - #if ___VXGG___ALWAYS_CHECK_LIBSODIUM___ > 0 - checksodium(); - #endif - - unsigned char buf[CHUNKSIZE], cbuf[CHUNKSIZE + crypto_secretstream_xchacha20poly1305_ABYTES]; - unsigned char header[crypto_secretstream_xchacha20poly1305_HEADERBYTES]; - crypto_secretstream_xchacha20poly1305_state state; - unsigned long long cbuflen; - unsigned char tag; - size_t bytesread; - int eof; - - // Write the header - crypto_secretstream_xchacha20poly1305_init_push(&state, header, key); - if(fwrite(header, 1, sizeof(header), dst) < sizeof(header)) { - if(ferror(dst)) { - WARN(errno, " Could not write header",); - return -1; - } - } - - // Encrypt each chunk - do { - if((bytesread = fread(buf, 1, sizeof(buf), src)) < sizeof(buf)) - if(ferror(src)) { - WARN(errno, " Could not read from source",); - return -1; - } - eof = feof(src); - tag = eof ? crypto_secretstream_xchacha20poly1305_TAG_FINAL : 0; - - crypto_secretstream_xchacha20poly1305_push(&state, cbuf, &cbuflen, buf, bytesread, NULL, 0, tag); - if(fwrite(cbuf, 1, (size_t)cbuflen, dst) < (size_t)cbuflen) - if(ferror(dst)) { - WARN(errno, " Could not write to target",); - return -1; - } - } while (!eof); - - return 0; -} - -int decrypttofile(FILE *src, FILE *dst, const unsigned char key[crypto_secretstream_xchacha20poly1305_KEYBYTES]) { - if(!src || !dst || !key) ERRRET(EINVAL, -1); - #if ___VXGG___ALWAYS_CHECK_LIBSODIUM___ > 0 - checksodium(); - #endif - - unsigned char cbuf[CHUNKSIZE + crypto_secretstream_xchacha20poly1305_ABYTES], buf[CHUNKSIZE]; - unsigned char header[crypto_secretstream_xchacha20poly1305_HEADERBYTES]; - crypto_secretstream_xchacha20poly1305_state state; - unsigned long long buflen; - unsigned char tag; - size_t bytesread; - int eof; - - // Read the header - if(fread(header, 1, sizeof(header), src) < sizeof(header)) { - if(ferror(src)) { - WARN(errno, " Couldn't read header", ); - return -1; - } - } - - // Make sure the header isn't fuckey - if(crypto_secretstream_xchacha20poly1305_init_pull(&state, header, key) != 0) { - WARN(errno, " Incomplete header", ); - return -1; - } - - // Decrypt each chunk - do { - if((bytesread = fread(cbuf, 1, sizeof(cbuf), src)) < sizeof(cbuf)) { - if(ferror(src)) { - WARN(errno, " Ran into problem reading for decryption", ); - return -1; - } - } - eof = feof(src); - - if (crypto_secretstream_xchacha20poly1305_pull(&state, buf, &buflen, &tag, cbuf, bytesread, NULL, 0) != 0) { - WARN(errno, " Corrupted chunk", ); - return -1; - } - - if(tag == crypto_secretstream_xchacha20poly1305_TAG_FINAL && !eof) { - WARN(errno, " End of stream before end of file", ); - return -1; - } - if(eof && tag != crypto_secretstream_xchacha20poly1305_TAG_FINAL) { - WARN(errno, " End of file before end of stream", ); - return -1; - } - - fwrite(buf, 1, (size_t)buflen, dst); - } while(! eof); - - return 0; -} - +/** + * @brief Generate a password viable for use in the derivation of a key + * + * @param str Pointer to a string. This will be filled by a malloc'ed string of words (the password). Must be non-null + * @param words The number of words to include in the password. A password of at least 20 words and probably not more than 40 is recommended + * @retval (int)[-1, words] On success, returns the number of words requested. On error, returns -1 and sets errno + */ int genpassword(char **str, unsigned int words) { // Early returns if(words < 1) return 0; @@ -402,7 +537,7 @@ int genpassword(char **str, unsigned int words) { // Concat the rest of the words into the password (without leaking memory) int ret; - for(unsigned int i = 1; i < words; i++) { + for(unsigned int i = 1; i < words; i++) { ret = asprintf(&tmp, "%s %s", lstr, PASSWORD_WORDS[randombytes_uniform(PASSWORD_WORDS_LEN)]); sodium_memzero(lstr, strlen(lstr) + 1); free(lstr); @@ -416,6 +551,14 @@ int genpassword(char **str, unsigned int words) { return words; } +/** + * @brief sodium_malloc wrapper. + * + * Calls `error()` or `abort()` depnding on the value of `___VXGG___XALLOC_EXIT_ON_ERROR___`. Will make sure libsodium is initialized if `___VXGG___ALWAYS_CHECK_LIBSODIUM___ > 0` + * + * @param size + * @retval (void*) A pointer to some data allocated via `sodium_malloc()` + */ void* xsodium_malloc(size_t size) { #if ___VXGG___ALWAYS_CHECK_LIBSODIUM___ > 0 checksodium(); @@ -436,7 +579,7 @@ void* xsodium_malloc(size_t size) { // dlinkedlist * scandirlist(const char * const dir, int (*selector)(const struct dirent *), int (*cmp)(const struct dirent **, const struct dirent **)) { // if(!dir || selector == NULL || cmp == NULL) ERRRET(EINVAL, NULL); - + // struct dirent **namelist = NULL; // dlinkedlist *list = NULL; // int numentries = -1; @@ -459,137 +602,38 @@ void* xsodium_malloc(size_t size) { // return list; // } +// Above implementation is flawed and would not actually scan the entire system. The process must be recursive: + // Step 1 - Create directory list + // Step 2 - Create --cryption ctq + // Step 3 - Scan initial starting dir. This will be /home/ + // Step 4 - Iterate over scan results + // Step 4.1 - For all directory dirent objects, add them to the directory list + // Step 4.2 - For all file dirent objects, add them to the --cryption ctq + // Step 5 - Scan next entry in the dirlist, removing it once done. Repeat Step 4 + // Step 6 - Free dirlist once empty, return newly populated --cryption ctq +// Idea: Create 2 ctqs. Use one for the actual scanning, and the other as the return result. That way, not only will scanning be +// fast, but I can also just reuse code I've already written and not make some absolute spaghetti mess trying to do everything +// linearly -struct __FLC_FKP { - char *target; - char *output; - const unsigned char *key; -}; - -struct __FLC_FKP * fkp_init(char * const target, char * const output, const unsigned char key[crypto_secretstream_xchacha20poly1305_KEYBYTES]) { - if(!target || !output || !key) ERRRET(EINVAL, NULL); - struct __FLC_FKP *fkp = calloc(1, sizeof(*fkp)); - if(!fkp) - return NULL; - - fkp->key = key; - fkp->output = output; - fkp->target = target; - - return fkp; -} - -void fkp_free(void *fkp) { - if(!fkp) return; - struct __FLC_FKP *real = (struct __FLC_FKP *)fkp; - - free(real->output); - free(real->target); - free(real); - - return; -} - -static int __FLC_TASK_ENCRYPT(void *fkp) { - if(!fkp) - return -1; - struct __FLC_FKP *real = (struct __FLC_FKP *)fkp; - return encryptviatmp(real->target, real->output, real->key); -} -static int __FLC_TASK_DECRYPT(void *fkp) { - if(!fkp) - return -1; - struct __FLC_FKP *real = (struct __FLC_FKP *)fkp; - return decryptto(real->target, real->output, real->key); -} +int __cscan_worker(void *data) { + if(!data) return -1; -enum VXGG_FLC { - VXGG_FLC__INVALID, - VXGG_FLC__ENCRYPT, - VXGG_FLC__DECRYPT, - VXGG_FLC__TOOBIG -}; - -struct __ucl_namelist_vals { - struct dirent **namelist; - int entries; -}; - -static void __ucl_namelist(void *namelist) { - if(!namelist) return; - struct __ucl_namelist_vals *real = namelist; - for(int i = 0; i > real->entries; i++) - free(real->namelist[i]); - free(real->namelist); - return; + return 0; } -// TODO: Write these -static int encryption_filter(const struct dirent *de) { +ctqueue * cryptscan() { + ctqueue *res = ctqueue_init(TPSIZE), *working = ctqueue_init(TPSIZE); + if(!res || !working) ERRRET(errno, NULL); -} -static int decryption_filter(const struct dirent *de) { + task *start = task_init(__cscan_worker, free, void *data); + if(!start) ERRRET(errno, NULL); + ctqueue_waitpush(working, start); -} -ctqueue * getfilelist(enum VXGG_FLC mode, const char * const dir, int threads, const unsigned char key[crypto_secretstream_xchacha20poly1305_KEYBYTES]) { - if(mode <= VXGG_FLC__INVALID || mode >= VXGG_FLC__TOOBIG || !dir || threads <= 0 || !key) ERRRET(EINVAL, NULL); - - cleanup_CREATE(10); - ctqueue *ctq = NULL; - int files = -1; - - // Create the ctqueue for later - ctq = ctqueue_init(threads); - if(!ctq) - return NULL; - cleanup_REGISTER(ctqueue_free, ctq); - - // Get the scandir list - struct dirent **namelist; - if((files = scandir(dir, &namelist, ((mode == VXGG_FLC__ENCRYPT) ? encryption_filter : decryption_filter), alphasort)) < 0) - cleanup_MARK(); - cleanup_CNDREGISTER(__ucl_namelist, (void*)(&(struct __ucl_namelist_vals){.namelist = namelist, .entries = files})); - - // Push everything onto the ctqueue - // TODO: Write task* compatible callbacks for encryption and decryption, then populate the ctqueue based on the specified mode - cleanup_CNDEXEC( - for(int i = 0; i < files; i++) { - - // Target is either "filename" or "filename.vxggr" - // Output is either "filename.vxggr" or "filename" - - struct __FLC_FKP *fkp = fkp_init(target, output, key); - if(!fkp) { - WARN(errno, " Could not create file-key pair for \"%s\"'s %scryption task, skipping...",, namelist[i]->d_name, ((mode == VXGG_FLC__ENCRYPT) ? "en" : "de")); - free(target); - free(output); - } - task *ctask = task_init((mode == VXGG_FLC__ENCRYPT) ? __FLC_TASK_ENCRYPT : __FLC_TASK_DECRYPT, fkp_free, fkp); - if(!ctask) { - WARN(errno, " Could not push \"%s\"'s %scryption task, skipping...",, namelist[i]->d_name, ((mode == VXGG_FLC__ENCRYPT) ? "en" : "de")); - free(namelist[i]); - continue; - } - ctqueue_waitpush(ctq, ctask); - } - - free(namelist); - // namelist is the array that holds each pointer to each dirent, so it needs to be free'd separately from the other elements - ); - - cleanup_CNDFIRE(); - if(cleanup_ERRORFLAGGED) - return NULL; - - return ctq; + return res; } - - - - /* int main(void) { // Example code for creating a temp file, writing to it, then linking it back into the fs @@ -602,7 +646,7 @@ int main(void) { if(write(fd, testmsg, strlen(testmsg)) < 0) error(1, errno, "write broke"); - + asprintf(&path, "/proc/self/fd/%d", fd); linkat(AT_FDCWD, path, AT_FDCWD, "./test", AT_SYMLINK_FOLLOW); free(path); @@ -623,7 +667,7 @@ int main(void) { //*/// /*// Example code for generating a password, derriving a secret key from it, and storing things properly - + // Initialization checksodium(); char *pass = NULL, hpass[crypto_pwhash_STRBYTES]; @@ -638,7 +682,7 @@ int main(void) { // Store the password if(crypto_pwhash_str(hpass, pass, strlen(pass) + 1, crypto_pwhash_OPSLIMIT_MODERATE, crypto_pwhash_MEMLIMIT_MODERATE) != 0) error(1, errno, "Couldn't generate password, quitting..."); - // Don't know if I want to use MODERATE or SENSITIVE for this. SENSITIVE takes a little bit on my laptop, which honestly + // Don't know if I want to use MODERATE or SENSITIVE for this. SENSITIVE takes a little bit on my laptop, which honestly // shouldn't be a problem, but it annoys me. MODERATE is quick and snappy, or at least quick enough that the slowdown is // barely noticable. I might do MODERATE for testing and SENSITIVE for release @@ -661,4 +705,6 @@ int main(void) { return 0; } -*/ \ No newline at end of file +*/ + +#endif \ No newline at end of file diff --git a/src/encryption.h b/src/encryption.h deleted file mode 100644 index a23cbdf..0000000 --- a/src/encryption.h +++ /dev/null @@ -1,185 +0,0 @@ -/** - * @file encryption.h - * @author syxhe (https://t.me/syxhe) - * @brief A collection of all encryption related functions - * @version 0.1 - * @date 2025-06-09 - * - * @copyright Copyright (c) 2025 - * - */ - -#ifndef __VXGG_REWRITE___ENCRYPTION_H___1481879318188___ -#define __VXGG_REWRITE___ENCRYPTION_H___1481879318188___ - -#include -#include "shared.h" - -/// Determines whether any function that calls libsodium functions also checks to make sure libsodium is actually initialized. May -/// cause unexpected issues with early exiting due to libsodium failing to initialize properly. It's recommended that you just -/// manually run `sodium_init()` in some main or init function of your own so that you can deal with a potential error yourself -#define ___VXGG___ALWAYS_CHECK_LIBSODIUM___ 1 - -/// Grants access to the `vxgg_setsodiumfailcb` function, which can be used to set a custom callback for what to do when libsodium -/// fails upon initialization -#define ___VXGG___USE_CLS_CALLBACK___ 1 - - -#if ___VXGG___ALWAYS_CHECK_LIBSODIUM___ > 0 -#if ___VXGG___USE_CLS_CALLBACK___ > 0 -//! Definition for the callback function that fires when a call to checksodium fails -typedef void (*vxgg_naclfailcb)(void*); - -/** - * @brief Sets a callback and data pair to be ran if/when sodium fails to initialize - * - * @param cb The new callback to set. Must be non-null - * @param data The data to be fed to the callback. May be null - */ -void vxgg_setsodiumfailcb(const vxgg_naclfailcb cb, void *data); -#endif -#endif - -/// Chunk size for en/decryption. I originally wanted to use st_blksize from stat(), but given that those chunks may be of different -/// sizes between computers / filesystems / architectures / files, it's easier to just have this be a consistent macro -#define CHUNKSIZE (1 << 9) - -const static char * test = "this is a test"; - -// Fuck reading from a file. Even if someone ran strings on the binary and got this they wouldn't be able to regenerate the key -//! A list of possible words for password creation -#define PASSWORD_WORDS (\ - (const char * const []){\ - "the", "of", "to", "and", "for", "our", "their", "has", "in", "he", "a", "them", "that", "these", "by", "have", "we", \ - "us", "people", "which", "all", "is", "with", "laws", "be", "are", "his", "states", "on", "they", "right", "it", "from", \ - "government", "such", "among", "powers", "most", "an", "time", "should", "new", "as", "been", "colonies", "assent", \ - "large", "at", "independent", "free", "united", "when", "mankind", "hold", "rights", "governments", "consent", "its", \ - "long", "themselves", "abolishing", "usurpations", "absolute", "repeated", "this", "world", "refused", "pass", "other", \ - "others", "without", "justice", "peace", "power", "seas", "war", "do", "declaration", "america", "becomes", "necessary", \ - "political", "equal", "declare", "causes", "separation", "men", "happiness", "any", "form", "alter", "or", "will", \ - "forms", "same", "object", "off", "necessity", "history", "great", "britain", "tyranny", "over", "public", "good", \ - "unless", "suspended", "so", "would", "legislature", "only", "legislative", "bodies", "purpose", "into", "dissolved", \ - "state", "endeavoured", "refusing", "hither", "conditions", "establishing", "offices", "out", "armies", "legislatures", \ - "render", "jurisdiction", "foreign", "acts", "pretended", "trial", "inhabitants", "cases", "transporting", "rule", \ - "declaring", "here", "protection", "against", "lives", "circumstances", "ages", "totally", "friends", "brethren", "whose", \ - "every", "may", "therefore", "ought", "unanimous", "thirteen", "course", "human", "events", "one", "dissolve", "bands", \ - "connected", "another", "assume", "earth", "separate", "station", "nature", "natures", "god", "entitle", "decent", \ - "respect", "opinions", "requires", "impel", "truths", "self", "evident", "created", "endowed", "creator", "certain", \ - "unalienable", "life", "liberty", "pursuit", "secure", "instituted", "deriving", "just", "governed", "whenever", \ - "destructive", "ends", "abolish", "institute", "laying", "foundation", "principles", "organizing", "shall", "seem", \ - "likely", "effect", "safety", "prudence", "indeed", "dictate", "established", "not", "changed", "light", "transient", \ - "accordingly", "experience", "hath", "shewn", "more", "disposed", "suffer", "while", "evils", "sufferable", "than", \ - "accustomed", "but", "train", "abuses", "pursuing", "invariably", "evinces", "design", "reduce", "under", "despotism", \ - "duty", "throw", "provide", "guards", "future", "security", "patient", "sufferance", "now", "constrains", "former", \ - "systems", "present", "king", "injuries", "having", "direct", "establishment", "prove", "let", "facts", "submitted", \ - "candid", "wholesome", "forbidden", "governors", "immediate", "pressing", "importance", "operation", "till", "obtained", \ - "utterly", "neglected", "attend", "accommodation", "districts", "those", "relinquish", "representation", "inestimable", \ - "formidable", "tyrants", "called", "together", "places", "unusual", "uncomfortable", "distant", "depository", "records", \ - "sole", "fatiguing", "compliance", "measures", "representative", "houses", "repeatedly", "opposing", "manly", "firmness", \ - "invasions", "after", "dissolutions", "cause", "elected", "whereby", "incapable", "annihilation", "returned", "exercise", \ - "remaining", "mean", "exposed", "dangers", "invasion", "convulsions", "within", "prevent", "population", "obstructing", \ - "naturalization", "foreigners", "encourage", "migrations", "raising", "appropriations", "lands", "obstructed", \ - "administration", "judiciary", "made", "judges", "dependent", "alone", "tenure", "amount", "payment", "salaries", \ - "erected", "multitude", "sent", "swarms", "officers", "harrass", "eat", "substance", "kept", "times", "standing", \ - "affected", "military", "superior", "civil", "combined", "subject", "constitution", "unacknowledged", "giving", \ - "legislation", "quartering", "armed", "troops", "protecting", "mock", "punishment", "murders", "commit", "cutting", \ - "trade", "parts", "imposing", "taxes", "depriving", "many", "benefits", "jury", "beyond", "tried", "offences", "system", \ - "english", "neighbouring", "province", "therein", "arbitrary", "enlarging", "boundaries", "once", "example", "fit", \ - "instrument", "introducing", "taking", "away", "charters", "valuable", "altering", "fundamentally", "suspending", "own", \ - "invested", "legislate", "whatsoever", "abdicated", "waging", "plundered", "ravaged", "coasts", "burnt", "towns", \ - "destroyed", "mercenaries", "compleat", "works", "death", "desolation", "already", "begun", "cruelty", "perfidy", \ - "scarcely", "paralleled", "barbarous", "unworthy", "head", "civilized", "nation", "constrained", "fellow", "citizens", \ - "taken", "captive", "high", "bear", "arms", "country", "become", "executioners", "fall", "hands", "excited", "domestic", \ - "insurrections", "amongst", "bring", "frontiers", "merciless", "indian", "savages", "known", "warfare", "undistinguished", \ - "destruction", "sexes", "stage", "oppressions", "petitioned", "redress", "humble", "terms", "petitions", "answered", \ - "injury", "prince", "character", "thus", "marked", "act", "define", "tyrant", "unfit", "ruler", "nor", "wanting", \ - "attentions", "brittish", "warned", "attempts", "extend", "unwarrantable", "reminded", "emigration", "settlement", \ - "appealed", "native", "magnanimity", "conjured", "ties", "common", "kindred", "disavow", "inevitably", "interrupt", \ - "connections", "correspondence", "too", "deaf", "voice", "consanguinity", "must", "acquiesce", "denounces", "rest", \ - "enemies", "representatives", "general", "congress", "assembled", "appealing", "supreme", "judge", "rectitude", \ - "intentions", "name", "authority", "solemnly", "publish", "absolved", "allegiance", "british", "crown", "connection", \ - "between", "full", "levy", "conclude", "contract", "alliances", "establish", "commerce", "things", "support", "firm", \ - "reliance", "divine", "providence", "mutually", "pledge", "each", "fortunes", "sacred", "honor"\ - }\ -) -//! Short macro for getting the `PASSWORD_WORDS` array size -#define PASSWORD_WORDS_LEN (STATIC_ARRAY_LEN(PASSWORD_WORDS)) - -/** - * @brief open() with the flags O_TMPFILE, O_WRONLY, O_CLOEXEC, and O_SYNC. Opened with mode S_IRUSR, S_IWUSR - * - * @param dest The filename the new descriptor should have. Must be non-null - * @retval (int)[-1,int] A new file descriptor. -1 on error - */ -int maketmp(const char * const dest); - -/** - * @brief Encrypt src to dst using libsodium's xchacha encryption suite - * - * @param src File to encrypt - * @param dst Destination to write encrypted file - * @param key Key for encryption - * @retval (int)[-1, 0] Returns 0 on success, sets errno and returns -1 on error - */ -int encrypttofile(FILE *src, FILE *dst, const unsigned char key[crypto_secretstream_xchacha20poly1305_KEYBYTES]); - -/** - * @brief Decrypt src to dst using libsodium's xchacha encryption suite - * - * @param src File to decrypt - * @param dst Destination to write decrypted file - * @param key Key used to encrypt - * @retval (int)[-1, 0] Returns 0 on success, sets errno and returns -1 on error - */ -int decrypttofile(FILE *src, FILE *dst, const unsigned char key[crypto_secretstream_xchacha20poly1305_KEYBYTES]); - -/** - * @brief Encrypt file at `target` to `output` using Linux's named temp file system to do it in the background - * - * @param target - * @param output - * @param key - * @retval (int)[,] - */ -int encryptviatmp(const char * const target, const char * const output, const unsigned char key[crypto_secretstream_xchacha20poly1305_KEYBYTES]); - -/** - * @brief Decrypt the file at `encrypted` to `target` - * - * @param encrypted - * @param target - * @param key - * @retval (int)[,] - */ -int decryptto(const char * const encrypted, const char * const target, const unsigned char key[crypto_secretstream_xchacha20poly1305_KEYBYTES]); - -/** - * @brief Link a file descriptor into the filesystem - * - * @param target New filename the descriptor should have - * @param tgfd The file descriptor to link - * @retval (int)[-1, 0] 0 on success, -1 on error - */ -int linkto(const char * const target, int tgfd); - -/** - * @brief Generate a password viable for use in the derivation of a key - * - * @param str Pointer to a string. This will be filled by a malloc'ed string of words (the password). Must be non-null - * @param words The number of words to include in the password. A password of at least 20 words and probably not more than 40 is recommended - * @retval (int)[-1, words] On success, returns the number of words requested. On error, returns -1 and sets errno - */ -int genpassword(char **str, unsigned int words); - -/** - * @brief sodium_malloc wrapper. - * - * Calls `error()` or `abort()` depnding on the value of `___VXGG___XALLOC_EXIT_ON_ERROR___`. Will make sure libsodium is initialized if `___VXGG___ALWAYS_CHECK_LIBSODIUM___ > 0` - * - * @param size - * @retval (void*) A pointer to some data allocated via `sodium_malloc()` - */ -void* xsodium_malloc(size_t size); - - -#endif \ No newline at end of file diff --git a/src/shared.c b/src/shared.c index 40732f0..0a2121b 100644 --- a/src/shared.c +++ b/src/shared.c @@ -1,15 +1,65 @@ /** * @file shared.c * @author syxhe (https://t.me/syxhe) - * @brief *Implementing `shared.h`* + * @brief A collection of functions and macros shared between files, or without a better place * @version 0.1 * @date 2025-06-09 - * + * * @copyright Copyright (c) 2025 - * + * */ -#include "shared.h" +#ifndef __VXGG_REWRITE___SHARED_C___3880294315821___ +#define __VXGG_REWRITE___SHARED_C___3880294315821___ 1 + +#define STATIC_ARRAY_LEN(arr) (sizeof((arr))/sizeof((arr)[0])) + +#define ERRRET(errval, retval) do {\ + errno = (errval);\ + return (retval);\ +} while (0) + +/// Defines how `x___alloc()` functions should exit. `___VXGG___XALLOC_EXIT_ON_ERROR___ > 0` calls `error()`, and thus functions +/// registered with `atexit()` and `on_exit()`. `___VXGG___XALLOC_EXIT_ON_ERROR___ <= 0` calls `abort()` on error. `x___alloc()` +/// type functions will ALWAYS 'abort', doing otherwise defeats the purpose of the function type +#define ___VXGG___XALLOC_EXIT_ON_ERROR___ 1 + +/// Defines whether vxgg functions that can error print out a short warning of the error when one is encountered. +/// `___VXGG___VERBOSE_ERRORS___ > 0` will print diagnostic error messages, and will do nothing otherwise +#define ___VXGG___VERBOSE_ERRORS___ 1 + +//! Macro to exit on an alloc error instead of doing the terrible nested if statement that was being used previously +#define XALLOC_EXIT(msg, ...) do {\ + if(!___VXGG___XALLOC_EXIT_ON_ERROR___)\ + abort();\ + if(!___VXGG___VERBOSE_ERRORS___)\ + exit(EXIT_FAILURE);\ + error(EXIT_FAILURE, errno, (msg)__VA_ARGS__);\ + exit(EXIT_FAILURE); /* Makes gcc happy */\ +} while (0) + +//! Holdover macro because I'm lazy. Used to call either malloc or xmalloc, but the xalloc functions were a bad idea, so I removed them +#define VALLOC(nmemb, size) malloc((nmemb) * (size)) + +//! Error macro that gcc will not complain about +#define ERROR(status, errnum, format, ...) do {error((status), (errnum), (format)__VA_ARGS__); exit((status));} while (0) +//! Spit out a warning using `error` +#define WARN(errnum, format, ...) do {error(0, (errnum), (format)__VA_ARGS__);} while (0) + + +typedef int (*gcallback)(void*); //!< Generic callback signature +typedef void (*fcallback)(void*); //!< free()-like callback signature + +/** + * @brief A locally defined structure designed for easier function cleanup + * + */ +typedef struct cl { + fcallback *callbacks; //!< An array of free()-like callbacks. Actual Type: fcallback callbacks[] + void * *arguments; //!< An array of void pointers. Actual Type: void *arguments[] + int size; //!< The size of each array + int used; //!< The current number of used elements in each array +} cleanup; #include #include @@ -17,6 +67,17 @@ #include #include +#include + +/** + * @brief Read the entire contents of a file descriptor into a malloc()'ed buffer + * + * @param str Pointer to a string. Will be replaced with the malloc()'ed string + * @param initsize The initial size of the malloc()'ed string + * @param fd A file descriptor to read from + * @retval (int)[-1, strlen(str)] Returns the size of the array on success, or -1 on error + * @note The allocated buffer will expand and contract as necessary, but it's recommended to set `initsize` to some value close to or equal to the size of the file being read to reduce the number of resizes + */ int rwbuf(char **str, unsigned long int initsize, int fd) { // Try to read bytes from fd into str // Bytes read == 0, return 0 @@ -32,7 +93,7 @@ int rwbuf(char **str, unsigned long int initsize, int fd) { lstr = calloc(initsize, sizeof(char)); if(!lstr) return -1; - + while((bytesread = read(fd, lstr + (csize - ccap), ccap)) > 0) { ccap -= bytesread; if(ccap <= 0) { @@ -43,7 +104,7 @@ int rwbuf(char **str, unsigned long int initsize, int fd) { if(!tmp) { if(___VXGG___VERBOSE_ERRORS___) error(0, errno, "Could not reallocate enough space for lstr"); - + free(lstr); lstr = NULL; // Need to set this because of the break bytesread = ECODE; @@ -81,6 +142,15 @@ int rwbuf(char **str, unsigned long int initsize, int fd) { } +/** + * @brief Write the entire contents of a buffer into a file descriptor + * + * @param fd The file descriptor to write to + * @param buf The buffer to write from + * @param len The length of the buffer + * @retval (int)[-1, 0] Returns 0 on success, -1 on error + */ + int wwbuf(int fd, const unsigned char *buf, int len) { if(!buf || len <= 0) ERRRET(EINVAL, -1); @@ -89,7 +159,7 @@ int wwbuf(int fd, const unsigned char *buf, int len) { int n = -1; while(total < len) { - if((n = write(fd, buf + total, left)) < 0) + if((n = write(fd, buf + total, left)) < 0) break; total += n; @@ -102,7 +172,12 @@ int wwbuf(int fd, const unsigned char *buf, int len) { // https://beej.us/guide/bgnet/html/split/slightly-advanced-techniques.html#sendall // Thanks Beej! -// dirname but less retarded hopefully +/** + * @brief `dirname()` reimplementation that returns a malloc()'ed string + * + * @param path The filepath to be inspected + * @retval (char*)[NULL, char*] Returns a null-terminated string on success, or `null` on error + */ char * vxdirname(const char * const path) { char *tmp = NULL; if(!path) { // Path being null is a special case which should return early, before anything else (as to avoid null dereference) @@ -201,6 +276,15 @@ char * vxdirname(const char * const path) { } +/** + * @brief Initialize a cleanup object + * + * @param loc The cleanup object to be initialized + * @param callbacks An array of free()-like callbacks. Must be `size` elements long + * @param arguments An array of void pointers. Must be `size` elements long + * @param size The number of elements the callbacks and arguments array are long + * @retval (int)[-1, 0] Returns 0 on success, -1 on error + */ int cleanup_init(cleanup *loc, fcallback callbacks[], void *arguments[], int size) { if(!loc || !callbacks || !arguments || size <= 0) ERRRET(EINVAL, -1); @@ -212,6 +296,14 @@ int cleanup_init(cleanup *loc, fcallback callbacks[], void *arguments[], int siz return 0; } +/** + * @brief Register a new callback and argument onto a cleanup stack + * + * @param loc The cleanup object to modify + * @param cb A free()-like callback to run + * @param arg A piece of data for the callback to run + * @retval (int)[-1, 0] Returns 0 on success, -1 on error + */ int cleanup_register(cleanup *loc, fcallback cb, void *arg) { if(!loc || !cb) ERRRET(EINVAL, -1); if(loc->used >= loc->size || loc->used < 0) ERRRET(ENOMEM, -1); @@ -223,18 +315,39 @@ int cleanup_register(cleanup *loc, fcallback cb, void *arg) { return 0; } -// registers if flag is NOT set +/** + * @brief Conditionally register a callback and argument + * + * @param loc The cleanup object to modify + * @param cb A free()-like callback to run + * @param arg A piece of data for the callback to run + * @param flag Whether or not the register should take place. Will not run if `flag` is non-zero + * @retval (int)[-1, 0] Returns 0 on success or skip, -1 on error + */ int cleanup_cndregister(cleanup *loc, fcallback cb, void *arg, unsigned char flag) { if(flag) return 0; return cleanup_register(loc, cb, arg); } +/** + * @brief Clear a cleanup object + * @attention Does not free any registered callbacks or arguments, just marks them as available space + * + * @param loc The cleanup object to modify + * @retval (int)[-1, 0] Returns 0 on success, -1 on error + */ int cleanup_clear(cleanup *loc) { - if(!loc) ERRRET(EINVAL, -1); + if(!loc) ERRRET(EINVAL, -1); loc->used = 0; return 0; } +/** + * @brief Fires all the registered callbacks and arguments in a cleanup object in FIFO (stack) order + * + * @param loc The cleanup object to fire + * @retval (int)[-1, 0] Returns 0 on success, -1 on error + */ int cleanup_fire(cleanup *loc) { if(!loc) ERRRET(EINVAL, -1); @@ -251,9 +364,49 @@ int cleanup_fire(cleanup *loc) { return 0; } -// Fires if flag is set +/** + * @brief Conditionally fires a cleanup object + * + * @param loc The cleanup object in question + * @param flag Whether the object should be fired. Will skip firing if non-zero + * @retval (int)[-1, 0] Returns 0 on success, -1 on error + */ int cleanup_cndfire(cleanup *loc, unsigned char flag) { if(flag) return cleanup_fire(loc); return 0; -} \ No newline at end of file +} + +/** + * @brief Initializes a set of variables suitable for use in the cleanup macros + * @param size The number of elements long each array should be + */ +#define cleanup_CREATE(size) \ +cleanup __CLEANUP; \ +fcallback __CLEANUP_FUNCS[(size)]; \ +void *__CLEANUP_ARGS[(size)]; \ +unsigned char __FLAG = 0; \ +cleanup_init(&__CLEANUP, __CLEANUP_FUNCS, __CLEANUP_ARGS, (size)) + +//! Register a callback-argument pair using the local cleanup object +#define cleanup_REGISTER(cb, arg) cleanup_register(&__CLEANUP, (cb), (arg)) +//! Conditionally register a callback-argument pair using the local cleanup object +#define cleanup_CNDREGISTER(cb, arg) cleanup_cndregister(&__CLEANUP, (cb), (arg), __FLAG) +//! Clean the local cleanup object +#define cleanup_CLEAR() cleanup_clear(&__CLEANUP) +//! Fire the local cleanup object +#define cleanup_FIRE() cleanup_fire(&__CLEANUP) +//! Conditionally fire the local cleanup object +#define cleanup_CNDFIRE() cleanup_cndfire(&__CLEANUP, __FLAG) +//! Set the local cleanup flag to a non-zero number +#define cleanup_MARK() (__FLAG = 1) +//! Set the local cleanup flag to zero +#define cleanup_UNMARK() (__FLAG = 0) +//! Check if the local cleanup flag is non-zero +#define cleanup_ERRORFLAGGED (__FLAG != 0) +//! Conditionally execute some `code` if the local cleanup flag has not been marked +#define cleanup_CNDEXEC(code) while(!cleanup_ERRORFLAGGED) {code; break;} +//! Conditionally fire the local cleanup object and return `ret` +#define cleanup_CNDFIRERET(ret) do {cleanup_CNDFIRE(); return ret;} while (0) + +#endif \ No newline at end of file diff --git a/src/shared.h b/src/shared.h deleted file mode 100644 index 94c6f06..0000000 --- a/src/shared.h +++ /dev/null @@ -1,191 +0,0 @@ -/** - * @file shared.h - * @author syxhe (https://t.me/syxhe) - * @brief A collection of functions and macros shared between files, or without a better place - * @version 0.1 - * @date 2025-06-09 - * - * @copyright Copyright (c) 2025 - * - */ - -#ifndef __VXGG_REWRITE___SHARED_H___3880294315821___ -#define __VXGG_REWRITE___SHARED_H___3880294315821___ 1 - -#include - -typedef int (*gcallback)(void*); //!< Generic callback signature -typedef void (*fcallback)(void*); //!< free()-like callback signature - -//! Get the size of a statically/vla defined array. Doesn't work on arrays allocated via `malloc()` -#define STATIC_ARRAY_LEN(arr) (sizeof((arr))/sizeof((arr)[0])) - -//! Sets errno to `errval`, then returns `retval`. Defined as a macro because it is meant for use inside functions, not as a function itself -#define ERRRET(errval, retval) do {\ - errno = (errval);\ - return (retval);\ -} while (0) - -/// Defines how `x___alloc()` functions should exit. `___VXGG___XALLOC_EXIT_ON_ERROR___ > 0` calls `error()`, and thus functions -/// registered with `atexit()` and `on_exit()`. `___VXGG___XALLOC_EXIT_ON_ERROR___ <= 0` calls `abort()` on error. `x___alloc()` -/// type functions will ALWAYS 'abort', doing otherwise defeats the purpose of the function type -#define ___VXGG___XALLOC_EXIT_ON_ERROR___ 1 - -/// Defines whether vxgg functions that can error print out a short warning of the error when one is encountered. -/// `___VXGG___VERBOSE_ERRORS___ > 0` will print diagnostic error messages, and will do nothing otherwise -#define ___VXGG___VERBOSE_ERRORS___ 1 - -//! Macro to exit on an alloc error instead of doing the terrible nested if statement that was being used previously -#define XALLOC_EXIT(msg, ...) do {\ - if(!___VXGG___XALLOC_EXIT_ON_ERROR___)\ - abort();\ - if(!___VXGG___VERBOSE_ERRORS___)\ - exit(EXIT_FAILURE);\ - error(EXIT_FAILURE, errno, (msg)__VA_ARGS__);\ - exit(EXIT_FAILURE); /* Makes gcc happy */\ -} while (0) - - -//! Holdover macro because I'm lazy. Used to call either malloc or xmalloc, but the xalloc functions were a bad idea, so I removed them -#define VALLOC(nmemb, size) malloc((nmemb) * (size)) - -//! Error macro that gcc will not complain about -#define ERROR(status, errnum, format, ...) do {error((status), (errnum), (format)__VA_ARGS__); exit((status));} while (0) -//! Spit out a warning using `error` -#define WARN(errnum, format, ...) do {error(0, (errnum), (format)__VA_ARGS__);} while (0) - - - -/** - * @brief Read the entire contents of a file descriptor into a malloc()'ed buffer - * - * @param str Pointer to a string. Will be replaced with the malloc()'ed string - * @param initsize The initial size of the malloc()'ed string - * @param fd A file descriptor to read from - * @retval (int)[-1, strlen(str)] Returns the size of the array on success, or -1 on error - * @note The allocated buffer will expand and contract as necessary, but it's recommended to set `initsize` to some value close to or equal to the size of the file being read to reduce the number of resizes - */ -int rwbuf(char **str, unsigned long int initsize, int fd); - -/** - * @brief Write the entire contents of a buffer into a file descriptor - * - * @param fd The file descriptor to write to - * @param buf The buffer to write from - * @param len The length of the buffer - * @retval (int)[-1, 0] Returns 0 on success, -1 on error - */ -int wwbuf(int fd, const unsigned char *buf, int len); - - - -/** - * @brief `dirname()` reimplementation that returns a malloc()'ed string - * - * @param path The filepath to be inspected - * @retval (char*)[NULL, char*] Returns a null-terminated string on success, or `null` on error - */ -char * vxdirname(const char * const path); - - - -/** - * @brief A locally defined structure designed for easier function cleanup - * - */ -typedef struct cl { - fcallback *callbacks; //!< An array of free()-like callbacks. Actual Type: fcallback callbacks[] - void * *arguments; //!< An array of void pointers. Actual Type: void *arguments[] - int size; //!< The size of each array - int used; //!< The current number of used elements in each array -} cleanup; - -/** - * @brief Initialize a cleanup object - * - * @param loc The cleanup object to be initialized - * @param callbacks An array of free()-like callbacks. Must be `size` elements long - * @param arguments An array of void pointers. Must be `size` elements long - * @param size The number of elements the callbacks and arguments array are long - * @retval (int)[-1, 0] Returns 0 on success, -1 on error - */ -int cleanup_init(cleanup *loc, fcallback callbacks[], void *arguments[], int size); - -/** - * @brief Register a new callback and argument onto a cleanup stack - * - * @param loc The cleanup object to modify - * @param cb A free()-like callback to run - * @param arg A piece of data for the callback to run - * @retval (int)[-1, 0] Returns 0 on success, -1 on error - */ -int cleanup_register(cleanup *loc, fcallback cb, void *arg); - -/** - * @brief Conditionally register a callback and argument - * - * @param loc The cleanup object to modify - * @param cb A free()-like callback to run - * @param arg A piece of data for the callback to run - * @param flag Whether or not the register should take place. Will not run if `flag` is non-zero - * @retval (int)[-1, 0] Returns 0 on success or skip, -1 on error - */ -int cleanup_cndregister(cleanup *loc, fcallback cb, void *arg, unsigned char flag); - -/** - * @brief Clear a cleanup object - * @attention Does not free any registered callbacks or arguments, just marks them as available space - * - * @param loc The cleanup object to modify - * @retval (int)[-1, 0] Returns 0 on success, -1 on error - */ -int cleanup_clear(cleanup *loc); - -/** - * @brief Fires all the registered callbacks and arguments in a cleanup object in FIFO (stack) order - * - * @param loc The cleanup object to fire - * @retval (int)[-1, 0] Returns 0 on success, -1 on error - */ -int cleanup_fire(cleanup *loc); - -/** - * @brief Conditionally fires a cleanup object - * - * @param loc The cleanup object in question - * @param flag Whether the object should be fired. Will skip firing if non-zero - * @retval (int)[-1, 0] Returns 0 on success, -1 on error - */ -int cleanup_cndfire(cleanup *loc, unsigned char flag); - -/** - * @brief Initializes a set of variables suitable for use in the cleanup macros - * @param size The number of elements long each array should be - */ -#define cleanup_CREATE(size) \ -cleanup __CLEANUP; \ -fcallback __CLEANUP_FUNCS[(size)]; \ -void *__CLEANUP_ARGS[(size)]; \ -unsigned char __FLAG = 0; \ -cleanup_init(&__CLEANUP, __CLEANUP_FUNCS, __CLEANUP_ARGS, (size)) - -//! Register a callback-argument pair using the local cleanup object -#define cleanup_REGISTER(cb, arg) cleanup_register(&__CLEANUP, (cb), (arg)) -//! Conditionally register a callback-argument pair using the local cleanup object -#define cleanup_CNDREGISTER(cb, arg) cleanup_cndregister(&__CLEANUP, (cb), (arg), __FLAG) -//! Clean the local cleanup object -#define cleanup_CLEAR() cleanup_clear(&__CLEANUP) -//! Fire the local cleanup object -#define cleanup_FIRE() cleanup_fire(&__CLEANUP) -//! Conditionally fire the local cleanup object -#define cleanup_CNDFIRE() cleanup_cndfire(&__CLEANUP, __FLAG) -//! Set the local cleanup flag to a non-zero number -#define cleanup_MARK() (__FLAG = 1) -//! Set the local cleanup flag to zero -#define cleanup_UNMARK() (__FLAG = 0) -//! Check if the local cleanup flag is non-zero -#define cleanup_ERRORFLAGGED (__FLAG != 0) -//! Conditionally execute some `code` if the local cleanup flag has not been marked -#define cleanup_CNDEXEC(code) while(!cleanup_ERRORFLAGGED) {code; break;} - -#endif \ No newline at end of file diff --git a/src/threadpool.c b/src/threadpool.c index 02cd945..c266964 100644 --- a/src/threadpool.c +++ b/src/threadpool.c @@ -1,15 +1,18 @@ /** * @file threadpool.c * @author syxhe (https://t.me/syxhe) - * @brief *Implementing `threadpool.h`* + * @brief An implementation of a threadpool using libc threads * @version 0.1 * @date 2025-06-09 - * + * * @copyright Copyright (c) 2025 - * + * */ -#include "threadpool.h" +#ifndef __VXGG_REWRITE___THREADPOOL_C___193271180830131___ +#define __VXGG_REWRITE___THREADPOOL_C___193271180830131___ 1 + +#include "shared.c" #include #include @@ -18,7 +21,7 @@ /** * @brief A generic task - A function, data for that function, and a way to free the data - * + * */ typedef struct task { gcallback callback; //!< A generic callback to be ran when executing the task @@ -28,7 +31,7 @@ typedef struct task { /** * @brief An internal structure used for the `taskqueue`. Analogous to a doubly-linked list's internal node - * + * */ typedef struct tqnode { struct tqnode *next; //!< The next element in the `taskqueue` @@ -38,7 +41,7 @@ typedef struct tqnode { /** * @brief A FIFO queue of tasks - * + * */ typedef struct taskqueue { tqnode *start; //!< The first element of the queue @@ -48,7 +51,7 @@ typedef struct taskqueue { /** * @brief A `taskqueue` built for concurrent access. Essentially a threadpool - * + * */ typedef struct ctqueue { mtx_t mutex; //!< A mutex for locking sensitive resources @@ -60,7 +63,14 @@ typedef struct ctqueue { int talen; //!< The length of the thread array } ctqueue; - +/** + * @brief Create a task + * + * @param callback Callback function the given data should be ran with. Must be non-null + * @param freecb Callback function for freeing the given data. May be null + * @param data Data to be passed to the callback. May be null + * @retval (task*)[NULL, task*] Returns a task object with set parameters. Returns `null` and sets errno on error + */ task * task_init(gcallback callback, fcallback freecb, void *data) { if(callback == NULL) ERRRET(EINVAL, NULL); @@ -75,11 +85,16 @@ task * task_init(gcallback callback, fcallback freecb, void *data) { return tsk; } +/** + * @brief Free a task + * + * @param tsk A task object to free. Frees data associated with task via `freecb` value specified in its creation. May be null + */ void task_free(void *tsk) { task *real = (task *)tsk; if(!real) return; - + if(real->freecb != NULL) real->freecb(real->data); free(real); @@ -87,11 +102,23 @@ void task_free(void *tsk) { return; } +/** + * @brief Fire a task. Passes the `data` member to the specified `callback` + * + * @param tsk A task to be fired. Must be non-null + * @retval (int) Returns value of the fired callback. Returns -1 and sets errno on error + */ int task_fire(task *tsk) { if(!tsk) ERRRET(EINVAL, -1); return tsk->callback(tsk->data); } +/** + * @brief Fire and destroy a task simultaneously. Calls specified callback and free-callback on associated data + * + * @param tsk Task to be fired and destroyed. Must be non-null + * @retval (int) Returns value of the callback. Returns -1 and sets errno on error + */ int task_fired(task *tsk) { int retval = task_fire(tsk); if(errno == EINVAL && retval == -1) {return -1;} @@ -125,7 +152,11 @@ void tqnode_free(void *tqn) { - +/** + * @brief Create a FIFO queue of tasks + * + * @retval (taskqueue*)[NULL, taskqueue*] Returns a new taskqueue object. Returns `null` and sets errno on error + */ taskqueue * taskqueue_init(void) { taskqueue *tq = calloc(1, sizeof(*tq)); if(!tq) @@ -138,6 +169,11 @@ taskqueue * taskqueue_init(void) { return tq; } +/** + * @brief Free a taskqueue + * + * @param tq A taskqueue to be freed. May be null + */ void taskqueue_free(void *tq) { if(!tq) return; @@ -148,7 +184,7 @@ void taskqueue_free(void *tq) { p = n; } free(tq); - + return; } @@ -167,6 +203,13 @@ int taskqueue_handlefirst(taskqueue *tq, task *tsk) { return 1; } +/** + * @brief Push a task onto a taskqueue + * + * @param tq The taskqueue to be modified. Must be non-null + * @param tsk The task to push. Must be non-null + * @retval (int)[-1, 0] Returns 0 on success, sets errno and returns -1 on error + */ int taskqueue_push(taskqueue *tq, task *tsk) { if(!tq || !tsk) ERRRET(EINVAL, -1); @@ -184,12 +227,18 @@ int taskqueue_push(taskqueue *tq, task *tsk) { return 0; } +/** + * @brief Pop a task from a taskqueue + * + * @param tq A taskqueue to grab a task from. Must be non-null + * @retval (task*)[NULL, task*] Returns a task on success, sets errno and returns `null` on error + */ task * taskqueue_pop(taskqueue *tq) { if(!tq) ERRRET(EINVAL, NULL); if(tq->size <= 0) ERRRET(ENODATA, NULL); tqnode *end = tq->end; - task *ret = end->task; + task *ret = end->task; if(tq->size == 1) { tq->end = NULL; @@ -204,6 +253,13 @@ task * taskqueue_pop(taskqueue *tq) { return ret; } +/** + * @brief Append a task to the front of a taskqueue + * + * @param tq The taskqueue to be modified. Must be non-null + * @param tsk The task to be appended. Must be non-null + * @retval (int)[-1, 0] Returns 0 on success, sets errno and returns -1 on error + */ int taskqueue_pushfront(taskqueue *tq, task *tsk) { if(!tq || !tsk) ERRRET(EINVAL, -1); @@ -221,6 +277,12 @@ int taskqueue_pushfront(taskqueue *tq, task *tsk) { return 0; } +/** + * @brief Pop a task from the back (most recently pushed task) of a taskqueue + * + * @param tq A taskqueue to pop from. Must be non-null + * @retval (task*)[NULL, task*] Returns a task on success, sets errno and returns `null` on error + */ task * taskqueue_popback(taskqueue *tq) { if(!tq) ERRRET(EINVAL, NULL); if(tq->size <= 0) ERRRET(ENODATA, NULL); @@ -273,6 +335,12 @@ static void ___ucl_cnddestroy(void *cond) { return; } +/** + * @brief Create a concurrent taskqueue with `size` allocated threads + * + * @param size Number of threads in the threadpool. Must be greater than zero + * @retval (ctqueue*)[NULL, ctqueue*] Returns a new ctqueue, sets errno and returns `null` on error + */ ctqueue * ctqueue_init(int nthreads) { if(nthreads <= 0) ERRRET(EINVAL, NULL); cleanup_CREATE(6); @@ -291,7 +359,7 @@ ctqueue * ctqueue_init(int nthreads) { cleanup_MARK(); cleanup_CNDREGISTER(taskqueue_free, ctq->tq); ); - + cleanup_CNDEXEC( if(mtx_init(&ctq->mutex, mtx_plain) != thrd_success) cleanup_MARK(); @@ -303,7 +371,7 @@ ctqueue * ctqueue_init(int nthreads) { cleanup_MARK(); cleanup_CNDREGISTER(___ucl_cnddestroy, (void*)&ctq->cond); ); - + cleanup_CNDEXEC( ctq->thrdarr = calloc(ctq->talen, sizeof(thrd_t)); if(!ctq->thrdarr) @@ -318,10 +386,16 @@ ctqueue * ctqueue_init(int nthreads) { return ctq; } +/** + * @brief Cancel all tasks being processed in a currently running concurrent taskqueue + * + * @param ctq The concurrent taskqueue to be canceled. Must be non-null + * @retval (int)[-1, 0] Returns 0 on success, sets errno and returns -1 on error + */ int ctqueue_cancel(ctqueue *ctq) { if(!ctq) ERRRET(EINVAL, -1); - __CTQ_INLOCK(ctq, 1, + __CTQ_INLOCK(ctq, 1, ctq->canceled = 1; ); cnd_broadcast(&ctq->cond); @@ -329,6 +403,12 @@ int ctqueue_cancel(ctqueue *ctq) { return 0; } +/** + * @brief Free a concurrent taskqueue + * @attention This cancels all currently running threads via `ctqueue_cancel` + * + * @param ctq The concurrent taskqueue to free. May be null + */ void ctqueue_free(void *ctq) { if(!ctq) return; @@ -351,11 +431,19 @@ void ctqueue_free(void *ctq) { return; } +/** + * @brief Push a task onto a concurrent taskqueue + * @attention May block for an indefinite amount of time to push the task + * + * @param ctq The concurrent taskqueue to modify. Must be non-null + * @param tsk The task to push. Must be non-null + * @retval (int) Returns `thrd_success` on success, returns `thrd_error` or `thrd_nomem` on error + */ int ctqueue_waitpush(ctqueue *ctq, task *tsk) { if(!ctq || !tsk) ERRRET(EINVAL, -1); int retval = 0; - __CTQ_INLOCK(ctq, -1, + __CTQ_INLOCK(ctq, -1, retval = taskqueue_push(ctq->tq, tsk); ); if(retval == 0) @@ -364,11 +452,18 @@ int ctqueue_waitpush(ctqueue *ctq, task *tsk) { return retval; } +/** + * @brief Pop a task from the concurrent taskqueue + * @attention May block for an indefinite amount of time to pop the task + * + * @param ctq The concurrent taskqueue to pop from. Must be non-null + * @retval (task*)[NULL, task*] Returns a task on success, sets errno and returns `null` on error + */ task * ctqueue_waitpop(ctqueue *ctq) { if(!ctq) ERRRET(EINVAL, NULL); task *retval = NULL; - __CTQ_INLOCK(ctq, NULL, + __CTQ_INLOCK(ctq, NULL, while(taskqueue_size(ctq->tq) == 0 && !ctq->canceled) cnd_wait(&ctq->cond, &ctq->mutex); @@ -386,7 +481,7 @@ task * ctqueue_waitpop(ctqueue *ctq) { //! Simple consumer for eating and executing tasks from the ctq static int __CTQ_CONSUMER(void *ctq) { if(!ctq) {errno = EINVAL; thrd_exit(-1);} - ctqueue *real = (ctqueue *)ctq; + ctqueue *real = (ctqueue *)ctq; for(task *ctask = NULL;;) { ctask = ctqueue_waitpop(real); @@ -399,14 +494,21 @@ static int __CTQ_CONSUMER(void *ctq) { thrd_exit(1); } -// TODO: Make this function return 0 or -1 depending on whether the overall ctq has been canceled or not. Canceling shouldn't +// TODO: Make this function return 0 or -1 depending on whether the overall ctq has been canceled or not. Canceling shouldn't // be treated as an error +/** + * @brief Start the threads allocated to a concurrent taskqueue + * @attention Threads will not consume pushed tasks until this function is ran + * + * @param ctq A concurrent taskqueue to start. Must be non-null + * @retval (int)[-1, 0] Returns 0 on success, sets errno and returns -1 on error + */ int ctqueue_start(ctqueue *ctq) { if(!ctq) ERRRET(EINVAL, -1); ctq->canceled = 0; - + int retval = 0; for(int i = 0; i < ctq->talen; i++) if((retval = thrd_create(&ctq->thrdarr[i], __CTQ_CONSUMER, ctq)) != thrd_success) @@ -416,4 +518,6 @@ int ctqueue_start(ctqueue *ctq) { ctqueue_cancel(ctq); return (retval == thrd_success) ? 0 : -1; -} \ No newline at end of file +} + +#endif \ No newline at end of file diff --git a/src/threadpool.h b/src/threadpool.h deleted file mode 100644 index 61dbacd..0000000 --- a/src/threadpool.h +++ /dev/null @@ -1,160 +0,0 @@ -/** - * @file threadpool.h - * @author syxhe (https://t.me/syxhe) - * @brief An implementation of a threadpool using libc threads - * @version 0.1 - * @date 2025-06-09 - * - * @copyright Copyright (c) 2025 - * - */ - -#ifndef __VXGG_REWRITE___THREADPOOL_H___193271180830131___ -#define __VXGG_REWRITE___THREADPOOL_H___193271180830131___ 1 - -#include "shared.h" -#include - -typedef struct task task; -typedef struct taskqueue taskqueue; -typedef struct ctqueue ctqueue; - -/** - * @brief Create a task - * - * @param callback Callback function the given data should be ran with. Must be non-null - * @param freecb Callback function for freeing the given data. May be null - * @param data Data to be passed to the callback. May be null - * @retval (task*)[NULL, task*] Returns a task object with set parameters. Returns `null` and sets errno on error - */ -task * task_init(gcallback callback, fcallback freecb, void *data); - -/** - * @brief Free a task - * - * @param tsk A task object to free. Frees data associated with task via `freecb` value specified in its creation. May be null - */ -void task_free(void *tsk); - -/** - * @brief Fire a task. Passes the `data` member to the specified `callback` - * - * @param tsk A task to be fired. Must be non-null - * @retval (int) Returns value of the fired callback. Returns -1 and sets errno on error - */ -int task_fire(task *tsk); - -/** - * @brief Fire and destroy a task simultaneously. Calls specified callback and free-callback on associated data - * - * @param tsk Task to be fired and destroyed. Must be non-null - * @retval (int) Returns value of the callback. Returns -1 and sets errno on error - */ -int task_fired(task *tsk); - - - -/** - * @brief Create a FIFO queue of tasks - * - * @retval (taskqueue*)[NULL, taskqueue*] Returns a new taskqueue object. Returns `null` and sets errno on error - */ -taskqueue * taskqueue_init(void); - -/** - * @brief Free a taskqueue - * - * @param tq A taskqueue to be freed. May be null - */ -void taskqueue_free(void *tq); - -/** - * @brief Push a task onto a taskqueue - * - * @param tq The taskqueue to be modified. Must be non-null - * @param tsk The task to push. Must be non-null - * @retval (int)[-1, 0] Returns 0 on success, sets errno and returns -1 on error - */ -int taskqueue_push(taskqueue *tq, task *tsk); - -/** - * @brief Pop a task from a taskqueue - * - * @param tq A taskqueue to grab a task from. Must be non-null - * @retval (task*)[NULL, task*] Returns a task on success, sets errno and returns `null` on error - */ -task * taskqueue_pop(taskqueue *tq); - -/** - * @brief Append a task to the front of a taskqueue - * - * @param tq The taskqueue to be modified. Must be non-null - * @param tsk The task to be appended. Must be non-null - * @retval (int)[-1, 0] Returns 0 on success, sets errno and returns -1 on error - */ -int taskqueue_pushfront(taskqueue *tq, task *tsk); - -/** - * @brief Pop a task from the back (most recently pushed task) of a taskqueue - * - * @param tq A taskqueue to pop from. Must be non-null - * @retval (task*)[NULL, task*] Returns a task on success, sets errno and returns `null` on error - */ -task * taskqueue_popback(taskqueue *tq); - - - -/** - * @brief Create a concurrent taskqueue with `size` allocated threads - * - * @param size Number of threads in the threadpool. Must be greater than zero - * @retval (ctqueue*)[NULL, ctqueue*] Returns a new ctqueue, sets errno and returns `null` on error - */ -ctqueue * ctqueue_init(int size); - -/** - * @brief Cancel all tasks being processed in a currently running concurrent taskqueue - * - * @param ctq The concurrent taskqueue to be canceled. Must be non-null - * @retval (int)[-1, 0] Returns 0 on success, sets errno and returns -1 on error - */ -int ctqueue_cancel(ctqueue *ctq); - -/** - * @brief Free a concurrent taskqueue - * @attention This cancels all currently running threads via `ctqueue_cancel` - * - * @param ctq The concurrent taskqueue to free. May be null - */ -void ctqueue_free(void *ctq); - -/** - * @brief Push a task onto a concurrent taskqueue - * @attention May block for an indefinite amount of time to push the task - * - * @param ctq The concurrent taskqueue to modify. Must be non-null - * @param tsk The task to push. Must be non-null - * @retval (int) Returns `thrd_success` on success, returns `thrd_error` or `thrd_nomem` on error - */ -int ctqueue_waitpush(ctqueue *ctq, task *tsk); - - -/** - * @brief Pop a task from the concurrent taskqueue - * @attention May block for an indefinite amount of time to pop the task - * - * @param ctq The concurrent taskqueue to pop from. Must be non-null - * @retval (task*)[NULL, task*] Returns a task on success, sets errno and returns `null` on error - */ -task * ctqueue_waitpop(ctqueue *ctq); - -/** - * @brief Start the threads allocated to a concurrent taskqueue - * @attention Threads will not consume pushed tasks until this function is ran - * - * @param ctq A concurrent taskqueue to start. Must be non-null - * @retval (int)[-1, 0] Returns 0 on success, sets errno and returns -1 on error - */ -int ctqueue_start(ctqueue *ctq); - -#endif \ No newline at end of file -- cgit v1.2.3