diff options
| -rw-r--r-- | src/Makefile | 19 | ||||
| -rw-r--r-- | src/encryption.c | 544 | ||||
| -rw-r--r-- | src/encryption.h | 185 | ||||
| -rw-r--r-- | src/shared.c | 177 | ||||
| -rw-r--r-- | src/shared.h | 191 | ||||
| -rw-r--r-- | src/threadpool.c | 148 | ||||
| -rw-r--r-- | src/threadpool.h | 160 |
7 files changed, 590 insertions, 834 deletions
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 | |||
| 6 | # RELEASE_CFLAGS := -O3 -fipa-pta -fipa-cp -fuse-linker-plugin -flto=auto | 6 | # RELEASE_CFLAGS := -O3 -fipa-pta -fipa-cp -fuse-linker-plugin -flto=auto |
| 7 | # RELEASE_LDFLAGS := -fuse-linker-plugin -flto=auto | 7 | # RELEASE_LDFLAGS := -fuse-linker-plugin -flto=auto |
| 8 | 8 | ||
| 9 | CFLAGS = -std=c2x -Wall -Wextra -Wpedantic -pedantic-errors -fanalyzer -Wanalyzer-too-complex -ggdb -g3 -O0 | 9 | CFLAGS = -std=c2x -Wall -Wextra -Wpedantic -pedantic-errors -fanalyzer -Wanalyzer-too-complex -ggdb -g3 -O0 |
| 10 | DEPFLAGS = -MT $@ -MMD -MP -MF $*.d | ||
| 11 | 10 | ||
| 12 | CFLAGS += $$(pkg-config --cflags libsodium) | 11 | CFLAGS += $$(pkg-config --cflags libsodium) |
| 13 | LDLIBS += $$(pkg-config --libs-only-l libsodium) | 12 | LDLIBS += $$(pkg-config --libs-only-l libsodium) |
| 14 | LDFLAGS += $$(pkg-config --libs-only-L libsodium) | 13 | LDFLAGS += $$(pkg-config --libs-only-L libsodium) |
| 15 | 14 | ||
| 16 | SOURCES := $(wildcard *.c) | 15 | SOURCES := $(wildcard *.c) |
| 17 | OBJECTS := $(patsubst %.c,%.o,$(SOURCES)) | ||
| 18 | DEPS := $(patsubst %.c,%.d,$(SOURCES)) | ||
| 19 | |||
| 20 | COMPILE.c = $(CC) $(DEPFLAGS) $(CFLAGS) -c | ||
| 21 | 16 | ||
| 22 | .PHONY: all c clean val | 17 | .PHONY: all c clean val |
| 23 | .DELETE_ON_ERROR: | 18 | .DELETE_ON_ERROR: |
| 24 | .ONESHELL: | 19 | .ONESHELL: |
| 25 | 20 | ||
| 26 | all: main | 21 | all: main |
| 27 | main: $(OBJECTS) | 22 | main: |
| 28 | 23 | echo "Need to update makefile to work with unity build" | |
| 29 | %.o: %.c %.d | ||
| 30 | $(COMPILE.c) $< | ||
| 31 | |||
| 32 | $(DEPS): | ||
| 33 | include $(wildcard $(DEPS)) | ||
| 34 | # Adopted from https://make.mad-scientist.net/papers/advanced-auto-dependency-generation/ | ||
| 35 | 24 | ||
| 36 | c clean: | 25 | c clean: |
| 37 | @-rm -rv main $(OBJECTS) $(DEPS) $(wildcard *.test*) $(wildcard *.enc) | 26 | @-rm -rv main $(wildcard *.test*) $(wildcard *.enc) |
| 38 | 27 | ||
| 39 | val: | 28 | val: |
| 40 | $(MAKE) all | 29 | $(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 @@ | |||
| 1 | /** | 1 | /** |
| 2 | * @file encryption.c | 2 | * @file encryption.c |
| 3 | * @author syxhe (https://t.me/syxhe) | 3 | * @author syxhe (https://t.me/syxhe) |
| 4 | * @brief *Implementing `encryption.h`* | 4 | * @brief A collection of all encryption related functions |
| 5 | * @version 0.1 | 5 | * @version 0.1 |
| 6 | * @date 2025-06-09 | 6 | * @date 2025-06-09 |
| 7 | * | 7 | * |
| 8 | * @copyright Copyright (c) 2025 | 8 | * @copyright Copyright (c) 2025 |
| 9 | * | 9 | * |
| 10 | */ | 10 | */ |
| 11 | 11 | ||
| 12 | // TODO: Go back and make sure every function has proper error handling | 12 | // TODO: Go back and make sure every function has proper error handling |
| @@ -14,11 +14,16 @@ | |||
| 14 | // I need to make sure every single function in this file returns with an indicated error instead of nuking the whole program with | 14 | // I need to make sure every single function in this file returns with an indicated error instead of nuking the whole program with |
| 15 | // error() | 15 | // error() |
| 16 | 16 | ||
| 17 | #ifndef __VXGG_REWRITE___ENCRYPTION_C___1481879318188___ | ||
| 18 | #define __VXGG_REWRITE___ENCRYPTION_C___1481879318188___ | ||
| 19 | |||
| 20 | |||
| 17 | #define _GNU_SOURCE | 21 | #define _GNU_SOURCE |
| 18 | 22 | ||
| 19 | #include "shared.h" | 23 | #define TPSIZE (1<<13) |
| 20 | #include "encryption.h" | 24 | |
| 21 | #include "threadpool.h" | 25 | #include "shared.c" |
| 26 | #include "threadpool.c" | ||
| 22 | 27 | ||
| 23 | #include <sodium.h> | 28 | #include <sodium.h> |
| 24 | 29 | ||
| @@ -33,16 +38,91 @@ | |||
| 33 | #include <fcntl.h> | 38 | #include <fcntl.h> |
| 34 | #include <stdio.h> | 39 | #include <stdio.h> |
| 35 | 40 | ||
| 41 | /// Determines whether any function that calls libsodium functions also checks to make sure libsodium is actually initialized. May | ||
| 42 | /// cause unexpected issues with early exiting due to libsodium failing to initialize properly. It's recommended that you just | ||
| 43 | /// manually run `sodium_init()` in some main or init function of your own so that you can deal with a potential error yourself | ||
| 44 | #define ___VXGG___ALWAYS_CHECK_LIBSODIUM___ 1 | ||
| 45 | |||
| 46 | /// Grants access to the `vxgg_setsodiumfailcb` function, which can be used to set a custom callback for what to do when libsodium | ||
| 47 | /// fails upon initialization | ||
| 48 | #define ___VXGG___USE_CLS_CALLBACK___ 1 | ||
| 49 | |||
| 50 | /// Chunk size for en/decryption. I originally wanted to use st_blksize from stat(), but given that those chunks may be of different | ||
| 51 | /// sizes between computers / filesystems / architectures / files, it's easier to just have this be a consistent macro | ||
| 52 | #define CHUNKSIZE (1 << 9) | ||
| 53 | |||
| 54 | // 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 | ||
| 55 | //! A list of possible words for password creation | ||
| 56 | #define PASSWORD_WORDS (\ | ||
| 57 | (const char * const []){\ | ||
| 58 | "the", "of", "to", "and", "for", "our", "their", "has", "in", "he", "a", "them", "that", "these", "by", "have", "we", \ | ||
| 59 | "us", "people", "which", "all", "is", "with", "laws", "be", "are", "his", "states", "on", "they", "right", "it", "from", \ | ||
| 60 | "government", "such", "among", "powers", "most", "an", "time", "should", "new", "as", "been", "colonies", "assent", \ | ||
| 61 | "large", "at", "independent", "free", "united", "when", "mankind", "hold", "rights", "governments", "consent", "its", \ | ||
| 62 | "long", "themselves", "abolishing", "usurpations", "absolute", "repeated", "this", "world", "refused", "pass", "other", \ | ||
| 63 | "others", "without", "justice", "peace", "power", "seas", "war", "do", "declaration", "america", "becomes", "necessary", \ | ||
| 64 | "political", "equal", "declare", "causes", "separation", "men", "happiness", "any", "form", "alter", "or", "will", \ | ||
| 65 | "forms", "same", "object", "off", "necessity", "history", "great", "britain", "tyranny", "over", "public", "good", \ | ||
| 66 | "unless", "suspended", "so", "would", "legislature", "only", "legislative", "bodies", "purpose", "into", "dissolved", \ | ||
| 67 | "state", "endeavoured", "refusing", "hither", "conditions", "establishing", "offices", "out", "armies", "legislatures", \ | ||
| 68 | "render", "jurisdiction", "foreign", "acts", "pretended", "trial", "inhabitants", "cases", "transporting", "rule", \ | ||
| 69 | "declaring", "here", "protection", "against", "lives", "circumstances", "ages", "totally", "friends", "brethren", "whose", \ | ||
| 70 | "every", "may", "therefore", "ought", "unanimous", "thirteen", "course", "human", "events", "one", "dissolve", "bands", \ | ||
| 71 | "connected", "another", "assume", "earth", "separate", "station", "nature", "natures", "god", "entitle", "decent", \ | ||
| 72 | "respect", "opinions", "requires", "impel", "truths", "self", "evident", "created", "endowed", "creator", "certain", \ | ||
| 73 | "unalienable", "life", "liberty", "pursuit", "secure", "instituted", "deriving", "just", "governed", "whenever", \ | ||
| 74 | "destructive", "ends", "abolish", "institute", "laying", "foundation", "principles", "organizing", "shall", "seem", \ | ||
| 75 | "likely", "effect", "safety", "prudence", "indeed", "dictate", "established", "not", "changed", "light", "transient", \ | ||
| 76 | "accordingly", "experience", "hath", "shewn", "more", "disposed", "suffer", "while", "evils", "sufferable", "than", \ | ||
| 77 | "accustomed", "but", "train", "abuses", "pursuing", "invariably", "evinces", "design", "reduce", "under", "despotism", \ | ||
| 78 | "duty", "throw", "provide", "guards", "future", "security", "patient", "sufferance", "now", "constrains", "former", \ | ||
| 79 | "systems", "present", "king", "injuries", "having", "direct", "establishment", "prove", "let", "facts", "submitted", \ | ||
| 80 | "candid", "wholesome", "forbidden", "governors", "immediate", "pressing", "importance", "operation", "till", "obtained", \ | ||
| 81 | "utterly", "neglected", "attend", "accommodation", "districts", "those", "relinquish", "representation", "inestimable", \ | ||
| 82 | "formidable", "tyrants", "called", "together", "places", "unusual", "uncomfortable", "distant", "depository", "records", \ | ||
| 83 | "sole", "fatiguing", "compliance", "measures", "representative", "houses", "repeatedly", "opposing", "manly", "firmness", \ | ||
| 84 | "invasions", "after", "dissolutions", "cause", "elected", "whereby", "incapable", "annihilation", "returned", "exercise", \ | ||
| 85 | "remaining", "mean", "exposed", "dangers", "invasion", "convulsions", "within", "prevent", "population", "obstructing", \ | ||
| 86 | "naturalization", "foreigners", "encourage", "migrations", "raising", "appropriations", "lands", "obstructed", \ | ||
| 87 | "administration", "judiciary", "made", "judges", "dependent", "alone", "tenure", "amount", "payment", "salaries", \ | ||
| 88 | "erected", "multitude", "sent", "swarms", "officers", "harrass", "eat", "substance", "kept", "times", "standing", \ | ||
| 89 | "affected", "military", "superior", "civil", "combined", "subject", "constitution", "unacknowledged", "giving", \ | ||
| 90 | "legislation", "quartering", "armed", "troops", "protecting", "mock", "punishment", "murders", "commit", "cutting", \ | ||
| 91 | "trade", "parts", "imposing", "taxes", "depriving", "many", "benefits", "jury", "beyond", "tried", "offences", "system", \ | ||
| 92 | "english", "neighbouring", "province", "therein", "arbitrary", "enlarging", "boundaries", "once", "example", "fit", \ | ||
| 93 | "instrument", "introducing", "taking", "away", "charters", "valuable", "altering", "fundamentally", "suspending", "own", \ | ||
| 94 | "invested", "legislate", "whatsoever", "abdicated", "waging", "plundered", "ravaged", "coasts", "burnt", "towns", \ | ||
| 95 | "destroyed", "mercenaries", "compleat", "works", "death", "desolation", "already", "begun", "cruelty", "perfidy", \ | ||
| 96 | "scarcely", "paralleled", "barbarous", "unworthy", "head", "civilized", "nation", "constrained", "fellow", "citizens", \ | ||
| 97 | "taken", "captive", "high", "bear", "arms", "country", "become", "executioners", "fall", "hands", "excited", "domestic", \ | ||
| 98 | "insurrections", "amongst", "bring", "frontiers", "merciless", "indian", "savages", "known", "warfare", "undistinguished", \ | ||
| 99 | "destruction", "sexes", "stage", "oppressions", "petitioned", "redress", "humble", "terms", "petitions", "answered", \ | ||
| 100 | "injury", "prince", "character", "thus", "marked", "act", "define", "tyrant", "unfit", "ruler", "nor", "wanting", \ | ||
| 101 | "attentions", "brittish", "warned", "attempts", "extend", "unwarrantable", "reminded", "emigration", "settlement", \ | ||
| 102 | "appealed", "native", "magnanimity", "conjured", "ties", "common", "kindred", "disavow", "inevitably", "interrupt", \ | ||
| 103 | "connections", "correspondence", "too", "deaf", "voice", "consanguinity", "must", "acquiesce", "denounces", "rest", \ | ||
| 104 | "enemies", "representatives", "general", "congress", "assembled", "appealing", "supreme", "judge", "rectitude", \ | ||
| 105 | "intentions", "name", "authority", "solemnly", "publish", "absolved", "allegiance", "british", "crown", "connection", \ | ||
| 106 | "between", "full", "levy", "conclude", "contract", "alliances", "establish", "commerce", "things", "support", "firm", \ | ||
| 107 | "reliance", "divine", "providence", "mutually", "pledge", "each", "fortunes", "sacred", "honor"\ | ||
| 108 | }\ | ||
| 109 | ) | ||
| 110 | //! Short macro for getting the `PASSWORD_WORDS` array size | ||
| 111 | #define PASSWORD_WORDS_LEN (STATIC_ARRAY_LEN(PASSWORD_WORDS)) | ||
| 112 | |||
| 36 | #if ___VXGG___ALWAYS_CHECK_LIBSODIUM___ > 0 | 113 | #if ___VXGG___ALWAYS_CHECK_LIBSODIUM___ > 0 |
| 37 | #if ___VXGG___USE_CLS_CALLBACK___ > 0 | 114 | #if ___VXGG___USE_CLS_CALLBACK___ > 0 |
| 38 | 115 | ||
| 116 | //! Definition for the callback function that fires when a call to checksodium fails | ||
| 117 | typedef void (*vxgg_naclfailcb)(void*); | ||
| 118 | |||
| 39 | /** | 119 | /** |
| 40 | * @brief Default sodium init fail callback for use in `checksodiumcb()` | 120 | * @brief Default sodium init fail callback for use in `checksodiumcb()` |
| 41 | * | 121 | * |
| 42 | * @param none Unused param to fit callback spec | 122 | * @param none Unused param to fit callback spec |
| 43 | */ | 123 | */ |
| 44 | static void naclfaildefault(void *none) { | 124 | static void naclfaildefault(void *none) { |
| 45 | none = none; // Makes gcc happy | 125 | none = none; // Makes gcc happy |
| 46 | if(___VXGG___VERBOSE_ERRORS___) | 126 | if(___VXGG___VERBOSE_ERRORS___) |
| 47 | error(1, ENOTSUP, "<naclfaildefault> Couldn't initialize sodium for some reason. Quitting..."); | 127 | error(1, ENOTSUP, "<naclfaildefault> Couldn't initialize sodium for some reason. Quitting..."); |
| 48 | exit(EXIT_FAILURE); | 128 | exit(EXIT_FAILURE); |
| @@ -50,7 +130,7 @@ static void naclfaildefault(void *none) { | |||
| 50 | 130 | ||
| 51 | /** | 131 | /** |
| 52 | * @brief Internal function to deal with the `___VXGG___USE_CLS_CALLBACK___` macro | 132 | * @brief Internal function to deal with the `___VXGG___USE_CLS_CALLBACK___` macro |
| 53 | * | 133 | * |
| 54 | * `checksodiumcb()` runs the sodium function `sodium_init()` and on error calls the provided callback with the provided data. The | 134 | * `checksodiumcb()` runs the sodium function `sodium_init()` and on error calls the provided callback with the provided data. The |
| 55 | * callback and data default to `naclfaildefault` and `NULL`, but can be changed when the `set` parameter is non-zero. When `set` | 135 | * callback and data default to `naclfaildefault` and `NULL`, but can be changed when the `set` parameter is non-zero. When `set` |
| 56 | * is zero, the sodium init check is preformed | 136 | * is zero, the sodium init check is preformed |
| @@ -59,11 +139,11 @@ static void naclfaildefault(void *none) { | |||
| 59 | * macros are both greater than 0 when compiling, and 2: a function in `encryption.c` calls a function originating from sodium. This | 139 | * macros are both greater than 0 when compiling, and 2: a function in `encryption.c` calls a function originating from sodium. This |
| 60 | * function exists as a way to deal with sodium failing yourself, instead of instantly calling `exit()`. If you don't care to handle | 140 | * function exists as a way to deal with sodium failing yourself, instead of instantly calling `exit()`. If you don't care to handle |
| 61 | * it, or are initializing sodium yourself, this is unnecessary | 141 | * it, or are initializing sodium yourself, this is unnecessary |
| 62 | * | 142 | * |
| 63 | * @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 | 143 | * @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 |
| 64 | * @param data Data to be passed to the callback when it is fired. Ignored if `set` is zero. May be null | 144 | * @param data Data to be passed to the callback when it is fired. Ignored if `set` is zero. May be null |
| 65 | * @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 | 145 | * @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 |
| 66 | * @retval int | 146 | * @retval int |
| 67 | */ | 147 | */ |
| 68 | static int checksodiumcb(vxgg_naclfailcb const callback, void *data, unsigned char set) { | 148 | static int checksodiumcb(vxgg_naclfailcb const callback, void *data, unsigned char set) { |
| 69 | static vxgg_naclfailcb cb = naclfaildefault; | 149 | static vxgg_naclfailcb cb = naclfaildefault; |
| @@ -97,9 +177,9 @@ void vxgg_setsodiumfailcb(vxgg_naclfailcb cb, void *data) { | |||
| 97 | * @brief Simple function to check if sodium has been properly initialized | 177 | * @brief Simple function to check if sodium has been properly initialized |
| 98 | * | 178 | * |
| 99 | * `checksodium()` will run in functions located in `encryption.h` only when the macro `___VXGG___ALWAYS_CHECK_LIBSODIUM___` is greater | 179 | * `checksodium()` will run in functions located in `encryption.h` only when the macro `___VXGG___ALWAYS_CHECK_LIBSODIUM___` is greater |
| 100 | * than zero when compiling. It will call the `checksodiumcb()` function if compiled with the `___VXGG___USE_CLS_CALLBACK___` macro. | 180 | * than zero when compiling. It will call the `checksodiumcb()` function if compiled with the `___VXGG___USE_CLS_CALLBACK___` macro. |
| 101 | * When called, checksodium will run `sodium_init()`, and will either run the user-defined callback or `XALLOC_EXIT`. | 181 | * When called, checksodium will run `sodium_init()`, and will either run the user-defined callback or `XALLOC_EXIT`. |
| 102 | * | 182 | * |
| 103 | */ | 183 | */ |
| 104 | static void checksodium(void) { | 184 | static void checksodium(void) { |
| 105 | #if ___VXGG___USE_CLS_CALLBACK___ > 0 | 185 | #if ___VXGG___USE_CLS_CALLBACK___ > 0 |
| @@ -112,17 +192,30 @@ static void checksodium(void) { | |||
| 112 | } | 192 | } |
| 113 | 193 | ||
| 114 | #endif | 194 | #endif |
| 115 | 195 | ||
| 116 | return; | 196 | return; |
| 117 | } | 197 | } |
| 118 | 198 | ||
| 119 | #endif | 199 | #endif |
| 120 | 200 | ||
| 201 | /** | ||
| 202 | * @brief open() with the flags O_TMPFILE, O_WRONLY, O_CLOEXEC, and O_SYNC. Opened with mode S_IRUSR, S_IWUSR | ||
| 203 | * | ||
| 204 | * @param dest The filename the new descriptor should have. Must be non-null | ||
| 205 | * @retval (int)[-1,int] A new file descriptor. -1 on error | ||
| 206 | */ | ||
| 121 | int maketmp(const char * const dest) { | 207 | int maketmp(const char * const dest) { |
| 122 | if(!dest) ERRRET(EINVAL, -1); | 208 | if(!dest) ERRRET(EINVAL, -1); |
| 123 | return open(dest, (O_TMPFILE | O_WRONLY | O_CLOEXEC | O_SYNC), (S_IRUSR | S_IWUSR)); | 209 | return open(dest, (O_TMPFILE | O_WRONLY | O_CLOEXEC | O_SYNC), (S_IRUSR | S_IWUSR)); |
| 124 | } | 210 | } |
| 125 | 211 | ||
| 212 | /** | ||
| 213 | * @brief Link a file descriptor into the filesystem | ||
| 214 | * | ||
| 215 | * @param target New filename the descriptor should have | ||
| 216 | * @param tgfd The file descriptor to link | ||
| 217 | * @retval (int)[-1, 0] 0 on success, -1 on error | ||
| 218 | */ | ||
| 126 | int linkto(const char * const target, int tgfd) { | 219 | int linkto(const char * const target, int tgfd) { |
| 127 | if(!target) ERRRET(EINVAL, -1); | 220 | if(!target) ERRRET(EINVAL, -1); |
| 128 | 221 | ||
| @@ -131,6 +224,9 @@ int linkto(const char * const target, int tgfd) { | |||
| 131 | if(!path) | 224 | if(!path) |
| 132 | ERROR(1, errno, "<linkto> Couldn't get path to move file into system",); | 225 | ERROR(1, errno, "<linkto> Couldn't get path to move file into system",); |
| 133 | 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) | 226 | 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) |
| 227 | // 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. | ||
| 228 | // I really should write a function entirely dedicated to dealing with linking | ||
| 229 | |||
| 134 | int res = linkat(AT_FDCWD, path, AT_FDCWD, target, AT_SYMLINK_FOLLOW); | 230 | int res = linkat(AT_FDCWD, path, AT_FDCWD, target, AT_SYMLINK_FOLLOW); |
| 135 | free(path); | 231 | free(path); |
| 136 | return res; | 232 | return res; |
| @@ -150,6 +246,132 @@ static void __ucl_fclose(void *file) { | |||
| 150 | return; | 246 | return; |
| 151 | } | 247 | } |
| 152 | 248 | ||
| 249 | /** | ||
| 250 | * @brief Encrypt src to dst using libsodium's xchacha encryption suite | ||
| 251 | * | ||
| 252 | * @param src File to encrypt | ||
| 253 | * @param dst Destination to write encrypted file | ||
| 254 | * @param key Key for encryption | ||
| 255 | * @retval (int)[-1, 0] Returns 0 on success, sets errno and returns -1 on error | ||
| 256 | */ | ||
| 257 | int encrypttofile(FILE *src, FILE *dst, const unsigned char key[crypto_secretstream_xchacha20poly1305_KEYBYTES]) { | ||
| 258 | if(!src || !dst || !key) ERRRET(EINVAL, -1); | ||
| 259 | #if ___VXGG___ALWAYS_CHECK_LIBSODIUM___ > 0 | ||
| 260 | checksodium(); | ||
| 261 | #endif | ||
| 262 | |||
| 263 | unsigned char buf[CHUNKSIZE], cbuf[CHUNKSIZE + crypto_secretstream_xchacha20poly1305_ABYTES]; | ||
| 264 | unsigned char header[crypto_secretstream_xchacha20poly1305_HEADERBYTES]; | ||
| 265 | crypto_secretstream_xchacha20poly1305_state state; | ||
| 266 | unsigned long long cbuflen; | ||
| 267 | unsigned char tag; | ||
| 268 | size_t bytesread; | ||
| 269 | int eof; | ||
| 270 | |||
| 271 | // Write the header | ||
| 272 | crypto_secretstream_xchacha20poly1305_init_push(&state, header, key); | ||
| 273 | if(fwrite(header, 1, sizeof(header), dst) < sizeof(header)) { | ||
| 274 | if(ferror(dst)) { | ||
| 275 | WARN(errno, "<encrypttofile> Could not write header",); | ||
| 276 | return -1; | ||
| 277 | } | ||
| 278 | } | ||
| 279 | |||
| 280 | // Encrypt each chunk | ||
| 281 | do { | ||
| 282 | if((bytesread = fread(buf, 1, sizeof(buf), src)) < sizeof(buf)) | ||
| 283 | if(ferror(src)) { | ||
| 284 | WARN(errno, "<encrypttofile> Could not read from source",); | ||
| 285 | return -1; | ||
| 286 | } | ||
| 287 | eof = feof(src); | ||
| 288 | tag = eof ? crypto_secretstream_xchacha20poly1305_TAG_FINAL : 0; | ||
| 289 | |||
| 290 | crypto_secretstream_xchacha20poly1305_push(&state, cbuf, &cbuflen, buf, bytesread, NULL, 0, tag); | ||
| 291 | if(fwrite(cbuf, 1, (size_t)cbuflen, dst) < (size_t)cbuflen) | ||
| 292 | if(ferror(dst)) { | ||
| 293 | WARN(errno, "<encrypttofile> Could not write to target",); | ||
| 294 | return -1; | ||
| 295 | } | ||
| 296 | } while (!eof); | ||
| 297 | |||
| 298 | return 0; | ||
| 299 | } | ||
| 300 | |||
| 301 | /** | ||
| 302 | * @brief Decrypt src to dst using libsodium's xchacha encryption suite | ||
| 303 | * | ||
| 304 | * @param src File to decrypt | ||
| 305 | * @param dst Destination to write decrypted file | ||
| 306 | * @param key Key used to encrypt | ||
| 307 | * @retval (int)[-1, 0] Returns 0 on success, sets errno and returns -1 on error | ||
| 308 | */ | ||
| 309 | int decrypttofile(FILE *src, FILE *dst, const unsigned char key[crypto_secretstream_xchacha20poly1305_KEYBYTES]) { | ||
| 310 | if(!src || !dst || !key) ERRRET(EINVAL, -1); | ||
| 311 | #if ___VXGG___ALWAYS_CHECK_LIBSODIUM___ > 0 | ||
| 312 | checksodium(); | ||
| 313 | #endif | ||
| 314 | |||
| 315 | unsigned char cbuf[CHUNKSIZE + crypto_secretstream_xchacha20poly1305_ABYTES], buf[CHUNKSIZE]; | ||
| 316 | unsigned char header[crypto_secretstream_xchacha20poly1305_HEADERBYTES]; | ||
| 317 | crypto_secretstream_xchacha20poly1305_state state; | ||
| 318 | unsigned long long buflen; | ||
| 319 | unsigned char tag; | ||
| 320 | size_t bytesread; | ||
| 321 | int eof; | ||
| 322 | |||
| 323 | // Read the header | ||
| 324 | if(fread(header, 1, sizeof(header), src) < sizeof(header)) { | ||
| 325 | if(ferror(src)) { | ||
| 326 | WARN(errno, "<decrypttofile> Couldn't read header", ); | ||
| 327 | return -1; | ||
| 328 | } | ||
| 329 | } | ||
| 330 | |||
| 331 | // Make sure the header isn't fuckey | ||
| 332 | if(crypto_secretstream_xchacha20poly1305_init_pull(&state, header, key) != 0) { | ||
| 333 | WARN(errno, "<decrypttofile> Incomplete header", ); | ||
| 334 | return -1; | ||
| 335 | } | ||
| 336 | |||
| 337 | // Decrypt each chunk | ||
| 338 | do { | ||
| 339 | if((bytesread = fread(cbuf, 1, sizeof(cbuf), src)) < sizeof(cbuf)) { | ||
| 340 | if(ferror(src)) { | ||
| 341 | WARN(errno, "<decrypttofile> Ran into problem reading for decryption", ); | ||
| 342 | return -1; | ||
| 343 | } | ||
| 344 | } | ||
| 345 | eof = feof(src); | ||
| 346 | |||
| 347 | if (crypto_secretstream_xchacha20poly1305_pull(&state, buf, &buflen, &tag, cbuf, bytesread, NULL, 0) != 0) { | ||
| 348 | WARN(errno, "<decrypttofile> Corrupted chunk", ); | ||
| 349 | return -1; | ||
| 350 | } | ||
| 351 | |||
| 352 | if(tag == crypto_secretstream_xchacha20poly1305_TAG_FINAL && !eof) { | ||
| 353 | WARN(errno, "<decrypttofile> End of stream before end of file", ); | ||
| 354 | return -1; | ||
| 355 | } | ||
| 356 | if(eof && tag != crypto_secretstream_xchacha20poly1305_TAG_FINAL) { | ||
| 357 | WARN(errno, "<decrypttofile> End of file before end of stream", ); | ||
| 358 | return -1; | ||
| 359 | } | ||
| 360 | |||
| 361 | fwrite(buf, 1, (size_t)buflen, dst); | ||
| 362 | } while(! eof); | ||
| 363 | |||
| 364 | return 0; | ||
| 365 | } | ||
| 366 | |||
| 367 | /** | ||
| 368 | * @brief Encrypt file at `target` to `output` using Linux's named temp file system to do it in the background | ||
| 369 | * | ||
| 370 | * @param target | ||
| 371 | * @param output | ||
| 372 | * @param key | ||
| 373 | * @retval (int)[,] | ||
| 374 | */ | ||
| 153 | int encryptviatmp(const char * const target, const char * const output, const unsigned char key[crypto_secretstream_xchacha20poly1305_KEYBYTES]) { | 375 | int encryptviatmp(const char * const target, const char * const output, const unsigned char key[crypto_secretstream_xchacha20poly1305_KEYBYTES]) { |
| 154 | #if ___VXGG___ALWAYS_CHECK_LIBSODIUM___ > 0 | 376 | #if ___VXGG___ALWAYS_CHECK_LIBSODIUM___ > 0 |
| 155 | checksodium(); | 377 | checksodium(); |
| @@ -203,7 +425,7 @@ int encryptviatmp(const char * const target, const char * const output, const un | |||
| 203 | // Not going to bother changing this, it probably is catastrophic if an error happens when it shouldn't | 425 | // Not going to bother changing this, it probably is catastrophic if an error happens when it shouldn't |
| 204 | if(encrypttofile(src, dst, key) < 0) | 426 | if(encrypttofile(src, dst, key) < 0) |
| 205 | ERROR(1, ENOTRECOVERABLE, "<encryptviatmp> I don't even have a way to cause an error here. How did you do it?",); | 427 | ERROR(1, ENOTRECOVERABLE, "<encryptviatmp> I don't even have a way to cause an error here. How did you do it?",); |
| 206 | 428 | ||
| 207 | // Link the temp file into the system | 429 | // Link the temp file into the system |
| 208 | if(linkto(output, tfd) < 0) | 430 | if(linkto(output, tfd) < 0) |
| 209 | WARN(errno, "<encryptviatmp> Could not link \"%s\" into system after encryption", , output); | 431 | WARN(errno, "<encryptviatmp> 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 | |||
| 213 | fclose(src); | 435 | fclose(src); |
| 214 | // fclose alco closes fd and tfd, as fdopen does not dup the file descriptors | 436 | // fclose alco closes fd and tfd, as fdopen does not dup the file descriptors |
| 215 | ); | 437 | ); |
| 216 | 438 | ||
| 217 | 439 | ||
| 218 | cleanup_CNDFIRE(); | 440 | cleanup_CNDFIRE(); |
| 219 | if(cleanup_ERRORFLAGGED) | 441 | if(cleanup_ERRORFLAGGED) |
| 220 | return -1; | 442 | return -1; |
| 221 | 443 | ||
| 222 | return 0; | 444 | return 0; |
| 223 | } | 445 | } |
| 224 | 446 | ||
| 447 | /** | ||
| 448 | * @brief Decrypt the file at `encrypted` to `target` | ||
| 449 | * | ||
| 450 | * @param encrypted | ||
| 451 | * @param target | ||
| 452 | * @param key | ||
| 453 | * @retval (int)[,] | ||
| 454 | */ | ||
| 225 | int decryptto(const char * const target, const char * const output, const unsigned char key[crypto_secretstream_xchacha20poly1305_KEYBYTES]) { | 455 | int decryptto(const char * const target, const char * const output, const unsigned char key[crypto_secretstream_xchacha20poly1305_KEYBYTES]) { |
| 226 | if(!target || !output || !key) ERRRET(EINVAL, -1); | 456 | if(!target || !output || !key) ERRRET(EINVAL, -1); |
| 227 | #if ___VXGG___ALWAYS_CHECK_LIBSODIUM___ > 0 | 457 | #if ___VXGG___ALWAYS_CHECK_LIBSODIUM___ > 0 |
| 228 | checksodium(); | 458 | checksodium(); |
| 229 | #endif | 459 | #endif |
| 230 | 460 | ||
| 231 | cleanup_CREATE(10); | 461 | cleanup_CREATE(10); |
| 232 | FILE *src, *dst; | 462 | FILE *src, *dst; |
| 233 | int fdst; | 463 | int fdst; |
| @@ -276,7 +506,7 @@ int decryptto(const char * const target, const char * const output, const unsign | |||
| 276 | if(cleanup_ERRORFLAGGED) | 506 | if(cleanup_ERRORFLAGGED) |
| 277 | return -1; | 507 | return -1; |
| 278 | 508 | ||
| 279 | // Note: If an error were to theoretically occur, which shouldn't be possible but I'm covering my bases here, after the | 509 | // Note: If an error were to theoretically occur, which shouldn't be possible but I'm covering my bases here, after the |
| 280 | // `dst = fdopen` line, a double close on the temp file descriptor would occur. I've been told that this is not catastrophic, | 510 | // `dst = fdopen` line, a double close on the temp file descriptor would occur. I've been told that this is not catastrophic, |
| 281 | // and considering how my multithreading works it *should* be fine, but it very well could cause problems. The easy solution is | 511 | // and considering how my multithreading works it *should* be fine, but it very well could cause problems. The easy solution is |
| 282 | // 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 | 512 | // 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 | |||
| 285 | return 0; | 515 | return 0; |
| 286 | } | 516 | } |
| 287 | 517 | ||
| 288 | int encrypttofile(FILE *src, FILE *dst, const unsigned char key[crypto_secretstream_xchacha20poly1305_KEYBYTES]) { | 518 | /** |
| 289 | if(!src || !dst || !key) ERRRET(EINVAL, -1); | 519 | * @brief Generate a password viable for use in the derivation of a key |
| 290 | #if ___VXGG___ALWAYS_CHECK_LIBSODIUM___ > 0 | 520 | * |
| 291 | checksodium(); | 521 | * @param str Pointer to a string. This will be filled by a malloc'ed string of words (the password). Must be non-null |
| 292 | #endif | 522 | * @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 |
| 293 | 523 | * @retval (int)[-1, words] On success, returns the number of words requested. On error, returns -1 and sets errno | |
| 294 | unsigned char buf[CHUNKSIZE], cbuf[CHUNKSIZE + crypto_secretstream_xchacha20poly1305_ABYTES]; | 524 | */ |
| 295 | unsigned char header[crypto_secretstream_xchacha20poly1305_HEADERBYTES]; | ||
| 296 | crypto_secretstream_xchacha20poly1305_state state; | ||
| 297 | unsigned long long cbuflen; | ||
| 298 | unsigned char tag; | ||
| 299 | size_t bytesread; | ||
| 300 | int eof; | ||
| 301 | |||
| 302 | // Write the header | ||
| 303 | crypto_secretstream_xchacha20poly1305_init_push(&state, header, key); | ||
| 304 | if(fwrite(header, 1, sizeof(header), dst) < sizeof(header)) { | ||
| 305 | if(ferror(dst)) { | ||
| 306 | WARN(errno, "<encrypttofile> Could not write header",); | ||
| 307 | return -1; | ||
| 308 | } | ||
| 309 | } | ||
| 310 | |||
| 311 | // Encrypt each chunk | ||
| 312 | do { | ||
| 313 | if((bytesread = fread(buf, 1, sizeof(buf), src)) < sizeof(buf)) | ||
| 314 | if(ferror(src)) { | ||
| 315 | WARN(errno, "<encrypttofile> Could not read from source",); | ||
| 316 | return -1; | ||
| 317 | } | ||
| 318 | eof = feof(src); | ||
| 319 | tag = eof ? crypto_secretstream_xchacha20poly1305_TAG_FINAL : 0; | ||
| 320 | |||
| 321 | crypto_secretstream_xchacha20poly1305_push(&state, cbuf, &cbuflen, buf, bytesread, NULL, 0, tag); | ||
| 322 | if(fwrite(cbuf, 1, (size_t)cbuflen, dst) < (size_t)cbuflen) | ||
| 323 | if(ferror(dst)) { | ||
| 324 | WARN(errno, "<encrypttofile> Could not write to target",); | ||
| 325 | return -1; | ||
| 326 | } | ||
| 327 | } while (!eof); | ||
| 328 | |||
| 329 | return 0; | ||
| 330 | } | ||
| 331 | |||
| 332 | int decrypttofile(FILE *src, FILE *dst, const unsigned char key[crypto_secretstream_xchacha20poly1305_KEYBYTES]) { | ||
| 333 | if(!src || !dst || !key) ERRRET(EINVAL, -1); | ||
| 334 | #if ___VXGG___ALWAYS_CHECK_LIBSODIUM___ > 0 | ||
| 335 | checksodium(); | ||
| 336 | #endif | ||
| 337 | |||
| 338 | unsigned char cbuf[CHUNKSIZE + crypto_secretstream_xchacha20poly1305_ABYTES], buf[CHUNKSIZE]; | ||
| 339 | unsigned char header[crypto_secretstream_xchacha20poly1305_HEADERBYTES]; | ||
| 340 | crypto_secretstream_xchacha20poly1305_state state; | ||
| 341 | unsigned long long buflen; | ||
| 342 | unsigned char tag; | ||
| 343 | size_t bytesread; | ||
| 344 | int eof; | ||
| 345 | |||
| 346 | // Read the header | ||
| 347 | if(fread(header, 1, sizeof(header), src) < sizeof(header)) { | ||
| 348 | if(ferror(src)) { | ||
| 349 | WARN(errno, "<decrypttofile> Couldn't read header", ); | ||
| 350 | return -1; | ||
| 351 | } | ||
| 352 | } | ||
| 353 | |||
| 354 | // Make sure the header isn't fuckey | ||
| 355 | if(crypto_secretstream_xchacha20poly1305_init_pull(&state, header, key) != 0) { | ||
| 356 | WARN(errno, "<decrypttofile> Incomplete header", ); | ||
| 357 | return -1; | ||
| 358 | } | ||
| 359 | |||
| 360 | // Decrypt each chunk | ||
| 361 | do { | ||
| 362 | if((bytesread = fread(cbuf, 1, sizeof(cbuf), src)) < sizeof(cbuf)) { | ||
| 363 | if(ferror(src)) { | ||
| 364 | WARN(errno, "<decrypttofile> Ran into problem reading for decryption", ); | ||
| 365 | return -1; | ||
| 366 | } | ||
| 367 | } | ||
| 368 | eof = feof(src); | ||
| 369 | |||
| 370 | if (crypto_secretstream_xchacha20poly1305_pull(&state, buf, &buflen, &tag, cbuf, bytesread, NULL, 0) != 0) { | ||
| 371 | WARN(errno, "<decrypttofile> Corrupted chunk", ); | ||
| 372 | return -1; | ||
| 373 | } | ||
| 374 | |||
| 375 | if(tag == crypto_secretstream_xchacha20poly1305_TAG_FINAL && !eof) { | ||
| 376 | WARN(errno, "<decrypttofile> End of stream before end of file", ); | ||
| 377 | return -1; | ||
| 378 | } | ||
| 379 | if(eof && tag != crypto_secretstream_xchacha20poly1305_TAG_FINAL) { | ||
| 380 | WARN(errno, "<decrypttofile> End of file before end of stream", ); | ||
| 381 | return -1; | ||
| 382 | } | ||
| 383 | |||
| 384 | fwrite(buf, 1, (size_t)buflen, dst); | ||
| 385 | } while(! eof); | ||
| 386 | |||
| 387 | return 0; | ||
| 388 | } | ||
| 389 | |||
| 390 | int genpassword(char **str, unsigned int words) { | 525 | int genpassword(char **str, unsigned int words) { |
| 391 | // Early returns | 526 | // Early returns |
| 392 | if(words < 1) return 0; | 527 | if(words < 1) return 0; |
| @@ -402,7 +537,7 @@ int genpassword(char **str, unsigned int words) { | |||
| 402 | 537 | ||
| 403 | // Concat the rest of the words into the password (without leaking memory) | 538 | // Concat the rest of the words into the password (without leaking memory) |
| 404 | int ret; | 539 | int ret; |
| 405 | for(unsigned int i = 1; i < words; i++) { | 540 | for(unsigned int i = 1; i < words; i++) { |
| 406 | ret = asprintf(&tmp, "%s %s", lstr, PASSWORD_WORDS[randombytes_uniform(PASSWORD_WORDS_LEN)]); | 541 | ret = asprintf(&tmp, "%s %s", lstr, PASSWORD_WORDS[randombytes_uniform(PASSWORD_WORDS_LEN)]); |
| 407 | sodium_memzero(lstr, strlen(lstr) + 1); | 542 | sodium_memzero(lstr, strlen(lstr) + 1); |
| 408 | free(lstr); | 543 | free(lstr); |
| @@ -416,6 +551,14 @@ int genpassword(char **str, unsigned int words) { | |||
| 416 | return words; | 551 | return words; |
| 417 | } | 552 | } |
| 418 | 553 | ||
| 554 | /** | ||
| 555 | * @brief sodium_malloc wrapper. | ||
| 556 | * | ||
| 557 | * 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` | ||
| 558 | * | ||
| 559 | * @param size | ||
| 560 | * @retval (void*) A pointer to some data allocated via `sodium_malloc()` | ||
| 561 | */ | ||
| 419 | void* xsodium_malloc(size_t size) { | 562 | void* xsodium_malloc(size_t size) { |
| 420 | #if ___VXGG___ALWAYS_CHECK_LIBSODIUM___ > 0 | 563 | #if ___VXGG___ALWAYS_CHECK_LIBSODIUM___ > 0 |
| 421 | checksodium(); | 564 | checksodium(); |
| @@ -436,7 +579,7 @@ void* xsodium_malloc(size_t size) { | |||
| 436 | 579 | ||
| 437 | // dlinkedlist * scandirlist(const char * const dir, int (*selector)(const struct dirent *), int (*cmp)(const struct dirent **, const struct dirent **)) { | 580 | // dlinkedlist * scandirlist(const char * const dir, int (*selector)(const struct dirent *), int (*cmp)(const struct dirent **, const struct dirent **)) { |
| 438 | // if(!dir || selector == NULL || cmp == NULL) ERRRET(EINVAL, NULL); | 581 | // if(!dir || selector == NULL || cmp == NULL) ERRRET(EINVAL, NULL); |
| 439 | 582 | ||
| 440 | // struct dirent **namelist = NULL; | 583 | // struct dirent **namelist = NULL; |
| 441 | // dlinkedlist *list = NULL; | 584 | // dlinkedlist *list = NULL; |
| 442 | // int numentries = -1; | 585 | // int numentries = -1; |
| @@ -459,137 +602,38 @@ void* xsodium_malloc(size_t size) { | |||
| 459 | // return list; | 602 | // return list; |
| 460 | // } | 603 | // } |
| 461 | 604 | ||
| 605 | // Above implementation is flawed and would not actually scan the entire system. The process must be recursive: | ||
| 606 | // Step 1 - Create directory list | ||
| 607 | // Step 2 - Create --cryption ctq | ||
| 608 | // Step 3 - Scan initial starting dir. This will be /home/ | ||
| 609 | // Step 4 - Iterate over scan results | ||
| 610 | // Step 4.1 - For all directory dirent objects, add them to the directory list | ||
| 611 | // Step 4.2 - For all file dirent objects, add them to the --cryption ctq | ||
| 612 | // Step 5 - Scan next entry in the dirlist, removing it once done. Repeat Step 4 | ||
| 613 | // Step 6 - Free dirlist once empty, return newly populated --cryption ctq | ||
| 462 | 614 | ||
| 615 | // Idea: Create 2 ctqs. Use one for the actual scanning, and the other as the return result. That way, not only will scanning be | ||
| 616 | // fast, but I can also just reuse code I've already written and not make some absolute spaghetti mess trying to do everything | ||
| 617 | // linearly | ||
| 463 | 618 | ||
| 464 | struct __FLC_FKP { | 619 | int __cscan_worker(void *data) { |
| 465 | char *target; | 620 | if(!data) return -1; |
| 466 | char *output; | ||
| 467 | const unsigned char *key; | ||
| 468 | }; | ||
| 469 | |||
| 470 | struct __FLC_FKP * fkp_init(char * const target, char * const output, const unsigned char key[crypto_secretstream_xchacha20poly1305_KEYBYTES]) { | ||
| 471 | if(!target || !output || !key) ERRRET(EINVAL, NULL); | ||
| 472 | struct __FLC_FKP *fkp = calloc(1, sizeof(*fkp)); | ||
| 473 | if(!fkp) | ||
| 474 | return NULL; | ||
| 475 | |||
| 476 | fkp->key = key; | ||
| 477 | fkp->output = output; | ||
| 478 | fkp->target = target; | ||
| 479 | |||
| 480 | return fkp; | ||
| 481 | } | ||
| 482 | |||
| 483 | void fkp_free(void *fkp) { | ||
| 484 | if(!fkp) return; | ||
| 485 | struct __FLC_FKP *real = (struct __FLC_FKP *)fkp; | ||
| 486 | |||
| 487 | free(real->output); | ||
| 488 | free(real->target); | ||
| 489 | free(real); | ||
| 490 | |||
| 491 | return; | ||
| 492 | } | ||
| 493 | |||
| 494 | static int __FLC_TASK_ENCRYPT(void *fkp) { | ||
| 495 | if(!fkp) | ||
| 496 | return -1; | ||
| 497 | struct __FLC_FKP *real = (struct __FLC_FKP *)fkp; | ||
| 498 | return encryptviatmp(real->target, real->output, real->key); | ||
| 499 | } | ||
| 500 | static int __FLC_TASK_DECRYPT(void *fkp) { | ||
| 501 | if(!fkp) | ||
| 502 | return -1; | ||
| 503 | struct __FLC_FKP *real = (struct __FLC_FKP *)fkp; | ||
| 504 | return decryptto(real->target, real->output, real->key); | ||
| 505 | } | ||
| 506 | 621 | ||
| 507 | enum VXGG_FLC { | 622 | return 0; |
| 508 | VXGG_FLC__INVALID, | ||
| 509 | VXGG_FLC__ENCRYPT, | ||
| 510 | VXGG_FLC__DECRYPT, | ||
| 511 | VXGG_FLC__TOOBIG | ||
| 512 | }; | ||
| 513 | |||
| 514 | struct __ucl_namelist_vals { | ||
| 515 | struct dirent **namelist; | ||
| 516 | int entries; | ||
| 517 | }; | ||
| 518 | |||
| 519 | static void __ucl_namelist(void *namelist) { | ||
| 520 | if(!namelist) return; | ||
| 521 | struct __ucl_namelist_vals *real = namelist; | ||
| 522 | for(int i = 0; i > real->entries; i++) | ||
| 523 | free(real->namelist[i]); | ||
| 524 | free(real->namelist); | ||
| 525 | return; | ||
| 526 | } | 623 | } |
| 527 | 624 | ||
| 528 | // TODO: Write these | 625 | ctqueue * cryptscan() { |
| 529 | static int encryption_filter(const struct dirent *de) { | 626 | ctqueue *res = ctqueue_init(TPSIZE), *working = ctqueue_init(TPSIZE); |
| 627 | if(!res || !working) ERRRET(errno, NULL); | ||
| 530 | 628 | ||
| 531 | } | 629 | task *start = task_init(__cscan_worker, free, void *data); |
| 532 | static int decryption_filter(const struct dirent *de) { | 630 | if(!start) ERRRET(errno, NULL); |
| 631 | ctqueue_waitpush(working, start); | ||
| 533 | 632 | ||
| 534 | } | ||
| 535 | 633 | ||
| 536 | ctqueue * getfilelist(enum VXGG_FLC mode, const char * const dir, int threads, const unsigned char key[crypto_secretstream_xchacha20poly1305_KEYBYTES]) { | 634 | return res; |
| 537 | if(mode <= VXGG_FLC__INVALID || mode >= VXGG_FLC__TOOBIG || !dir || threads <= 0 || !key) ERRRET(EINVAL, NULL); | ||
| 538 | |||
| 539 | cleanup_CREATE(10); | ||
| 540 | ctqueue *ctq = NULL; | ||
| 541 | int files = -1; | ||
| 542 | |||
| 543 | // Create the ctqueue for later | ||
| 544 | ctq = ctqueue_init(threads); | ||
| 545 | if(!ctq) | ||
| 546 | return NULL; | ||
| 547 | cleanup_REGISTER(ctqueue_free, ctq); | ||
| 548 | |||
| 549 | // Get the scandir list | ||
| 550 | struct dirent **namelist; | ||
| 551 | if((files = scandir(dir, &namelist, ((mode == VXGG_FLC__ENCRYPT) ? encryption_filter : decryption_filter), alphasort)) < 0) | ||
| 552 | cleanup_MARK(); | ||
| 553 | cleanup_CNDREGISTER(__ucl_namelist, (void*)(&(struct __ucl_namelist_vals){.namelist = namelist, .entries = files})); | ||
| 554 | |||
| 555 | // Push everything onto the ctqueue | ||
| 556 | // TODO: Write task* compatible callbacks for encryption and decryption, then populate the ctqueue based on the specified mode | ||
| 557 | cleanup_CNDEXEC( | ||
| 558 | for(int i = 0; i < files; i++) { | ||
| 559 | |||
| 560 | // Target is either "filename" or "filename.vxggr" | ||
| 561 | // Output is either "filename.vxggr" or "filename" | ||
| 562 | |||
| 563 | struct __FLC_FKP *fkp = fkp_init(target, output, key); | ||
| 564 | if(!fkp) { | ||
| 565 | WARN(errno, "<getfilelist> Could not create file-key pair for \"%s\"'s %scryption task, skipping...",, namelist[i]->d_name, ((mode == VXGG_FLC__ENCRYPT) ? "en" : "de")); | ||
| 566 | free(target); | ||
| 567 | free(output); | ||
| 568 | } | ||
| 569 | task *ctask = task_init((mode == VXGG_FLC__ENCRYPT) ? __FLC_TASK_ENCRYPT : __FLC_TASK_DECRYPT, fkp_free, fkp); | ||
| 570 | if(!ctask) { | ||
| 571 | WARN(errno, "<getfilelist> Could not push \"%s\"'s %scryption task, skipping...",, namelist[i]->d_name, ((mode == VXGG_FLC__ENCRYPT) ? "en" : "de")); | ||
| 572 | free(namelist[i]); | ||
| 573 | continue; | ||
| 574 | } | ||
| 575 | ctqueue_waitpush(ctq, ctask); | ||
| 576 | } | ||
| 577 | |||
| 578 | free(namelist); | ||
| 579 | // namelist is the array that holds each pointer to each dirent, so it needs to be free'd separately from the other elements | ||
| 580 | ); | ||
| 581 | |||
| 582 | cleanup_CNDFIRE(); | ||
| 583 | if(cleanup_ERRORFLAGGED) | ||
| 584 | return NULL; | ||
| 585 | |||
| 586 | return ctq; | ||
| 587 | } | 635 | } |
| 588 | 636 | ||
| 589 | |||
| 590 | |||
| 591 | |||
| 592 | |||
| 593 | /* | 637 | /* |
| 594 | int main(void) { | 638 | int main(void) { |
| 595 | // Example code for creating a temp file, writing to it, then linking it back into the fs | 639 | // Example code for creating a temp file, writing to it, then linking it back into the fs |
| @@ -602,7 +646,7 @@ int main(void) { | |||
| 602 | 646 | ||
| 603 | if(write(fd, testmsg, strlen(testmsg)) < 0) | 647 | if(write(fd, testmsg, strlen(testmsg)) < 0) |
| 604 | error(1, errno, "write broke"); | 648 | error(1, errno, "write broke"); |
| 605 | 649 | ||
| 606 | asprintf(&path, "/proc/self/fd/%d", fd); | 650 | asprintf(&path, "/proc/self/fd/%d", fd); |
| 607 | linkat(AT_FDCWD, path, AT_FDCWD, "./test", AT_SYMLINK_FOLLOW); | 651 | linkat(AT_FDCWD, path, AT_FDCWD, "./test", AT_SYMLINK_FOLLOW); |
| 608 | free(path); | 652 | free(path); |
| @@ -623,7 +667,7 @@ int main(void) { | |||
| 623 | //*/// | 667 | //*/// |
| 624 | 668 | ||
| 625 | /*// Example code for generating a password, derriving a secret key from it, and storing things properly | 669 | /*// Example code for generating a password, derriving a secret key from it, and storing things properly |
| 626 | 670 | ||
| 627 | // Initialization | 671 | // Initialization |
| 628 | checksodium(); | 672 | checksodium(); |
| 629 | char *pass = NULL, hpass[crypto_pwhash_STRBYTES]; | 673 | char *pass = NULL, hpass[crypto_pwhash_STRBYTES]; |
| @@ -638,7 +682,7 @@ int main(void) { | |||
| 638 | // Store the password | 682 | // Store the password |
| 639 | if(crypto_pwhash_str(hpass, pass, strlen(pass) + 1, crypto_pwhash_OPSLIMIT_MODERATE, crypto_pwhash_MEMLIMIT_MODERATE) != 0) | 683 | if(crypto_pwhash_str(hpass, pass, strlen(pass) + 1, crypto_pwhash_OPSLIMIT_MODERATE, crypto_pwhash_MEMLIMIT_MODERATE) != 0) |
| 640 | error(1, errno, "Couldn't generate password, quitting..."); | 684 | error(1, errno, "Couldn't generate password, quitting..."); |
| 641 | // Don't know if I want to use MODERATE or SENSITIVE for this. SENSITIVE takes a little bit on my laptop, which honestly | 685 | // Don't know if I want to use MODERATE or SENSITIVE for this. SENSITIVE takes a little bit on my laptop, which honestly |
| 642 | // shouldn't be a problem, but it annoys me. MODERATE is quick and snappy, or at least quick enough that the slowdown is | 686 | // shouldn't be a problem, but it annoys me. MODERATE is quick and snappy, or at least quick enough that the slowdown is |
| 643 | // barely noticable. I might do MODERATE for testing and SENSITIVE for release | 687 | // barely noticable. I might do MODERATE for testing and SENSITIVE for release |
| 644 | 688 | ||
| @@ -661,4 +705,6 @@ int main(void) { | |||
| 661 | 705 | ||
| 662 | return 0; | 706 | return 0; |
| 663 | } | 707 | } |
| 664 | */ \ No newline at end of file | 708 | */ |
| 709 | |||
| 710 | #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 @@ | |||
| 1 | /** | ||
| 2 | * @file encryption.h | ||
| 3 | * @author syxhe (https://t.me/syxhe) | ||
| 4 | * @brief A collection of all encryption related functions | ||
| 5 | * @version 0.1 | ||
| 6 | * @date 2025-06-09 | ||
| 7 | * | ||
| 8 | * @copyright Copyright (c) 2025 | ||
| 9 | * | ||
| 10 | */ | ||
| 11 | |||
| 12 | #ifndef __VXGG_REWRITE___ENCRYPTION_H___1481879318188___ | ||
| 13 | #define __VXGG_REWRITE___ENCRYPTION_H___1481879318188___ | ||
| 14 | |||
| 15 | #include <sodium.h> | ||
| 16 | #include "shared.h" | ||
| 17 | |||
| 18 | /// Determines whether any function that calls libsodium functions also checks to make sure libsodium is actually initialized. May | ||
| 19 | /// cause unexpected issues with early exiting due to libsodium failing to initialize properly. It's recommended that you just | ||
| 20 | /// manually run `sodium_init()` in some main or init function of your own so that you can deal with a potential error yourself | ||
| 21 | #define ___VXGG___ALWAYS_CHECK_LIBSODIUM___ 1 | ||
| 22 | |||
| 23 | /// Grants access to the `vxgg_setsodiumfailcb` function, which can be used to set a custom callback for what to do when libsodium | ||
| 24 | /// fails upon initialization | ||
| 25 | #define ___VXGG___USE_CLS_CALLBACK___ 1 | ||
| 26 | |||
| 27 | |||
| 28 | #if ___VXGG___ALWAYS_CHECK_LIBSODIUM___ > 0 | ||
| 29 | #if ___VXGG___USE_CLS_CALLBACK___ > 0 | ||
| 30 | //! Definition for the callback function that fires when a call to checksodium fails | ||
| 31 | typedef void (*vxgg_naclfailcb)(void*); | ||
| 32 | |||
| 33 | /** | ||
| 34 | * @brief Sets a callback and data pair to be ran if/when sodium fails to initialize | ||
| 35 | * | ||
| 36 | * @param cb The new callback to set. Must be non-null | ||
| 37 | * @param data The data to be fed to the callback. May be null | ||
| 38 | */ | ||
| 39 | void vxgg_setsodiumfailcb(const vxgg_naclfailcb cb, void *data); | ||
| 40 | #endif | ||
| 41 | #endif | ||
| 42 | |||
| 43 | /// Chunk size for en/decryption. I originally wanted to use st_blksize from stat(), but given that those chunks may be of different | ||
| 44 | /// sizes between computers / filesystems / architectures / files, it's easier to just have this be a consistent macro | ||
| 45 | #define CHUNKSIZE (1 << 9) | ||
| 46 | |||
| 47 | const static char * test = "this is a test"; | ||
| 48 | |||
| 49 | // 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 | ||
| 50 | //! A list of possible words for password creation | ||
| 51 | #define PASSWORD_WORDS (\ | ||
| 52 | (const char * const []){\ | ||
| 53 | "the", "of", "to", "and", "for", "our", "their", "has", "in", "he", "a", "them", "that", "these", "by", "have", "we", \ | ||
| 54 | "us", "people", "which", "all", "is", "with", "laws", "be", "are", "his", "states", "on", "they", "right", "it", "from", \ | ||
| 55 | "government", "such", "among", "powers", "most", "an", "time", "should", "new", "as", "been", "colonies", "assent", \ | ||
| 56 | "large", "at", "independent", "free", "united", "when", "mankind", "hold", "rights", "governments", "consent", "its", \ | ||
| 57 | "long", "themselves", "abolishing", "usurpations", "absolute", "repeated", "this", "world", "refused", "pass", "other", \ | ||
| 58 | "others", "without", "justice", "peace", "power", "seas", "war", "do", "declaration", "america", "becomes", "necessary", \ | ||
| 59 | "political", "equal", "declare", "causes", "separation", "men", "happiness", "any", "form", "alter", "or", "will", \ | ||
| 60 | "forms", "same", "object", "off", "necessity", "history", "great", "britain", "tyranny", "over", "public", "good", \ | ||
| 61 | "unless", "suspended", "so", "would", "legislature", "only", "legislative", "bodies", "purpose", "into", "dissolved", \ | ||
| 62 | "state", "endeavoured", "refusing", "hither", "conditions", "establishing", "offices", "out", "armies", "legislatures", \ | ||
| 63 | "render", "jurisdiction", "foreign", "acts", "pretended", "trial", "inhabitants", "cases", "transporting", "rule", \ | ||
| 64 | "declaring", "here", "protection", "against", "lives", "circumstances", "ages", "totally", "friends", "brethren", "whose", \ | ||
| 65 | "every", "may", "therefore", "ought", "unanimous", "thirteen", "course", "human", "events", "one", "dissolve", "bands", \ | ||
| 66 | "connected", "another", "assume", "earth", "separate", "station", "nature", "natures", "god", "entitle", "decent", \ | ||
| 67 | "respect", "opinions", "requires", "impel", "truths", "self", "evident", "created", "endowed", "creator", "certain", \ | ||
| 68 | "unalienable", "life", "liberty", "pursuit", "secure", "instituted", "deriving", "just", "governed", "whenever", \ | ||
| 69 | "destructive", "ends", "abolish", "institute", "laying", "foundation", "principles", "organizing", "shall", "seem", \ | ||
| 70 | "likely", "effect", "safety", "prudence", "indeed", "dictate", "established", "not", "changed", "light", "transient", \ | ||
| 71 | "accordingly", "experience", "hath", "shewn", "more", "disposed", "suffer", "while", "evils", "sufferable", "than", \ | ||
| 72 | "accustomed", "but", "train", "abuses", "pursuing", "invariably", "evinces", "design", "reduce", "under", "despotism", \ | ||
| 73 | "duty", "throw", "provide", "guards", "future", "security", "patient", "sufferance", "now", "constrains", "former", \ | ||
| 74 | "systems", "present", "king", "injuries", "having", "direct", "establishment", "prove", "let", "facts", "submitted", \ | ||
| 75 | "candid", "wholesome", "forbidden", "governors", "immediate", "pressing", "importance", "operation", "till", "obtained", \ | ||
| 76 | "utterly", "neglected", "attend", "accommodation", "districts", "those", "relinquish", "representation", "inestimable", \ | ||
| 77 | "formidable", "tyrants", "called", "together", "places", "unusual", "uncomfortable", "distant", "depository", "records", \ | ||
| 78 | "sole", "fatiguing", "compliance", "measures", "representative", "houses", "repeatedly", "opposing", "manly", "firmness", \ | ||
| 79 | "invasions", "after", "dissolutions", "cause", "elected", "whereby", "incapable", "annihilation", "returned", "exercise", \ | ||
| 80 | "remaining", "mean", "exposed", "dangers", "invasion", "convulsions", "within", "prevent", "population", "obstructing", \ | ||
| 81 | "naturalization", "foreigners", "encourage", "migrations", "raising", "appropriations", "lands", "obstructed", \ | ||
| 82 | "administration", "judiciary", "made", "judges", "dependent", "alone", "tenure", "amount", "payment", "salaries", \ | ||
| 83 | "erected", "multitude", "sent", "swarms", "officers", "harrass", "eat", "substance", "kept", "times", "standing", \ | ||
| 84 | "affected", "military", "superior", "civil", "combined", "subject", "constitution", "unacknowledged", "giving", \ | ||
| 85 | "legislation", "quartering", "armed", "troops", "protecting", "mock", "punishment", "murders", "commit", "cutting", \ | ||
| 86 | "trade", "parts", "imposing", "taxes", "depriving", "many", "benefits", "jury", "beyond", "tried", "offences", "system", \ | ||
| 87 | "english", "neighbouring", "province", "therein", "arbitrary", "enlarging", "boundaries", "once", "example", "fit", \ | ||
| 88 | "instrument", "introducing", "taking", "away", "charters", "valuable", "altering", "fundamentally", "suspending", "own", \ | ||
| 89 | "invested", "legislate", "whatsoever", "abdicated", "waging", "plundered", "ravaged", "coasts", "burnt", "towns", \ | ||
| 90 | "destroyed", "mercenaries", "compleat", "works", "death", "desolation", "already", "begun", "cruelty", "perfidy", \ | ||
| 91 | "scarcely", "paralleled", "barbarous", "unworthy", "head", "civilized", "nation", "constrained", "fellow", "citizens", \ | ||
| 92 | "taken", "captive", "high", "bear", "arms", "country", "become", "executioners", "fall", "hands", "excited", "domestic", \ | ||
| 93 | "insurrections", "amongst", "bring", "frontiers", "merciless", "indian", "savages", "known", "warfare", "undistinguished", \ | ||
| 94 | "destruction", "sexes", "stage", "oppressions", "petitioned", "redress", "humble", "terms", "petitions", "answered", \ | ||
| 95 | "injury", "prince", "character", "thus", "marked", "act", "define", "tyrant", "unfit", "ruler", "nor", "wanting", \ | ||
| 96 | "attentions", "brittish", "warned", "attempts", "extend", "unwarrantable", "reminded", "emigration", "settlement", \ | ||
| 97 | "appealed", "native", "magnanimity", "conjured", "ties", "common", "kindred", "disavow", "inevitably", "interrupt", \ | ||
| 98 | "connections", "correspondence", "too", "deaf", "voice", "consanguinity", "must", "acquiesce", "denounces", "rest", \ | ||
| 99 | "enemies", "representatives", "general", "congress", "assembled", "appealing", "supreme", "judge", "rectitude", \ | ||
| 100 | "intentions", "name", "authority", "solemnly", "publish", "absolved", "allegiance", "british", "crown", "connection", \ | ||
| 101 | "between", "full", "levy", "conclude", "contract", "alliances", "establish", "commerce", "things", "support", "firm", \ | ||
| 102 | "reliance", "divine", "providence", "mutually", "pledge", "each", "fortunes", "sacred", "honor"\ | ||
| 103 | }\ | ||
| 104 | ) | ||
| 105 | //! Short macro for getting the `PASSWORD_WORDS` array size | ||
| 106 | #define PASSWORD_WORDS_LEN (STATIC_ARRAY_LEN(PASSWORD_WORDS)) | ||
| 107 | |||
| 108 | /** | ||
| 109 | * @brief open() with the flags O_TMPFILE, O_WRONLY, O_CLOEXEC, and O_SYNC. Opened with mode S_IRUSR, S_IWUSR | ||
| 110 | * | ||
| 111 | * @param dest The filename the new descriptor should have. Must be non-null | ||
| 112 | * @retval (int)[-1,int] A new file descriptor. -1 on error | ||
| 113 | */ | ||
| 114 | int maketmp(const char * const dest); | ||
| 115 | |||
| 116 | /** | ||
| 117 | * @brief Encrypt src to dst using libsodium's xchacha encryption suite | ||
| 118 | * | ||
| 119 | * @param src File to encrypt | ||
| 120 | * @param dst Destination to write encrypted file | ||
| 121 | * @param key Key for encryption | ||
| 122 | * @retval (int)[-1, 0] Returns 0 on success, sets errno and returns -1 on error | ||
| 123 | */ | ||
| 124 | int encrypttofile(FILE *src, FILE *dst, const unsigned char key[crypto_secretstream_xchacha20poly1305_KEYBYTES]); | ||
| 125 | |||
| 126 | /** | ||
| 127 | * @brief Decrypt src to dst using libsodium's xchacha encryption suite | ||
| 128 | * | ||
| 129 | * @param src File to decrypt | ||
| 130 | * @param dst Destination to write decrypted file | ||
| 131 | * @param key Key used to encrypt | ||
| 132 | * @retval (int)[-1, 0] Returns 0 on success, sets errno and returns -1 on error | ||
| 133 | */ | ||
| 134 | int decrypttofile(FILE *src, FILE *dst, const unsigned char key[crypto_secretstream_xchacha20poly1305_KEYBYTES]); | ||
| 135 | |||
| 136 | /** | ||
| 137 | * @brief Encrypt file at `target` to `output` using Linux's named temp file system to do it in the background | ||
| 138 | * | ||
| 139 | * @param target | ||
| 140 | * @param output | ||
| 141 | * @param key | ||
| 142 | * @retval (int)[,] | ||
| 143 | */ | ||
| 144 | int encryptviatmp(const char * const target, const char * const output, const unsigned char key[crypto_secretstream_xchacha20poly1305_KEYBYTES]); | ||
| 145 | |||
| 146 | /** | ||
| 147 | * @brief Decrypt the file at `encrypted` to `target` | ||
| 148 | * | ||
| 149 | * @param encrypted | ||
| 150 | * @param target | ||
| 151 | * @param key | ||
| 152 | * @retval (int)[,] | ||
| 153 | */ | ||
| 154 | int decryptto(const char * const encrypted, const char * const target, const unsigned char key[crypto_secretstream_xchacha20poly1305_KEYBYTES]); | ||
| 155 | |||
| 156 | /** | ||
| 157 | * @brief Link a file descriptor into the filesystem | ||
| 158 | * | ||
| 159 | * @param target New filename the descriptor should have | ||
| 160 | * @param tgfd The file descriptor to link | ||
| 161 | * @retval (int)[-1, 0] 0 on success, -1 on error | ||
| 162 | */ | ||
| 163 | int linkto(const char * const target, int tgfd); | ||
| 164 | |||
| 165 | /** | ||
| 166 | * @brief Generate a password viable for use in the derivation of a key | ||
| 167 | * | ||
| 168 | * @param str Pointer to a string. This will be filled by a malloc'ed string of words (the password). Must be non-null | ||
| 169 | * @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 | ||
| 170 | * @retval (int)[-1, words] On success, returns the number of words requested. On error, returns -1 and sets errno | ||
| 171 | */ | ||
| 172 | int genpassword(char **str, unsigned int words); | ||
| 173 | |||
| 174 | /** | ||
| 175 | * @brief sodium_malloc wrapper. | ||
| 176 | * | ||
| 177 | * 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` | ||
| 178 | * | ||
| 179 | * @param size | ||
| 180 | * @retval (void*) A pointer to some data allocated via `sodium_malloc()` | ||
| 181 | */ | ||
| 182 | void* xsodium_malloc(size_t size); | ||
| 183 | |||
| 184 | |||
| 185 | #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 @@ | |||
| 1 | /** | 1 | /** |
| 2 | * @file shared.c | 2 | * @file shared.c |
| 3 | * @author syxhe (https://t.me/syxhe) | 3 | * @author syxhe (https://t.me/syxhe) |
| 4 | * @brief *Implementing `shared.h`* | 4 | * @brief A collection of functions and macros shared between files, or without a better place |
| 5 | * @version 0.1 | 5 | * @version 0.1 |
| 6 | * @date 2025-06-09 | 6 | * @date 2025-06-09 |
| 7 | * | 7 | * |
| 8 | * @copyright Copyright (c) 2025 | 8 | * @copyright Copyright (c) 2025 |
| 9 | * | 9 | * |
| 10 | */ | 10 | */ |
| 11 | 11 | ||
| 12 | #include "shared.h" | 12 | #ifndef __VXGG_REWRITE___SHARED_C___3880294315821___ |
| 13 | #define __VXGG_REWRITE___SHARED_C___3880294315821___ 1 | ||
| 14 | |||
| 15 | #define STATIC_ARRAY_LEN(arr) (sizeof((arr))/sizeof((arr)[0])) | ||
| 16 | |||
| 17 | #define ERRRET(errval, retval) do {\ | ||
| 18 | errno = (errval);\ | ||
| 19 | return (retval);\ | ||
| 20 | } while (0) | ||
| 21 | |||
| 22 | /// Defines how `x___alloc()` functions should exit. `___VXGG___XALLOC_EXIT_ON_ERROR___ > 0` calls `error()`, and thus functions | ||
| 23 | /// registered with `atexit()` and `on_exit()`. `___VXGG___XALLOC_EXIT_ON_ERROR___ <= 0` calls `abort()` on error. `x___alloc()` | ||
| 24 | /// type functions will ALWAYS 'abort', doing otherwise defeats the purpose of the function type | ||
| 25 | #define ___VXGG___XALLOC_EXIT_ON_ERROR___ 1 | ||
| 26 | |||
| 27 | /// Defines whether vxgg functions that can error print out a short warning of the error when one is encountered. | ||
| 28 | /// `___VXGG___VERBOSE_ERRORS___ > 0` will print diagnostic error messages, and will do nothing otherwise | ||
| 29 | #define ___VXGG___VERBOSE_ERRORS___ 1 | ||
| 30 | |||
| 31 | //! Macro to exit on an alloc error instead of doing the terrible nested if statement that was being used previously | ||
| 32 | #define XALLOC_EXIT(msg, ...) do {\ | ||
| 33 | if(!___VXGG___XALLOC_EXIT_ON_ERROR___)\ | ||
| 34 | abort();\ | ||
| 35 | if(!___VXGG___VERBOSE_ERRORS___)\ | ||
| 36 | exit(EXIT_FAILURE);\ | ||
| 37 | error(EXIT_FAILURE, errno, (msg)__VA_ARGS__);\ | ||
| 38 | exit(EXIT_FAILURE); /* Makes gcc happy */\ | ||
| 39 | } while (0) | ||
| 40 | |||
| 41 | //! 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 | ||
| 42 | #define VALLOC(nmemb, size) malloc((nmemb) * (size)) | ||
| 43 | |||
| 44 | //! Error macro that gcc will not complain about | ||
| 45 | #define ERROR(status, errnum, format, ...) do {error((status), (errnum), (format)__VA_ARGS__); exit((status));} while (0) | ||
| 46 | //! Spit out a warning using `error` | ||
| 47 | #define WARN(errnum, format, ...) do {error(0, (errnum), (format)__VA_ARGS__);} while (0) | ||
| 48 | |||
| 49 | |||
| 50 | typedef int (*gcallback)(void*); //!< Generic callback signature | ||
| 51 | typedef void (*fcallback)(void*); //!< free()-like callback signature | ||
| 52 | |||
| 53 | /** | ||
| 54 | * @brief A locally defined structure designed for easier function cleanup | ||
| 55 | * | ||
| 56 | */ | ||
| 57 | typedef struct cl { | ||
| 58 | fcallback *callbacks; //!< An array of free()-like callbacks. Actual Type: fcallback callbacks[] | ||
| 59 | void * *arguments; //!< An array of void pointers. Actual Type: void *arguments[] | ||
| 60 | int size; //!< The size of each array | ||
| 61 | int used; //!< The current number of used elements in each array | ||
| 62 | } cleanup; | ||
| 13 | 63 | ||
| 14 | #include <stdlib.h> | 64 | #include <stdlib.h> |
| 15 | #include <string.h> | 65 | #include <string.h> |
| @@ -17,6 +67,17 @@ | |||
| 17 | #include <errno.h> | 67 | #include <errno.h> |
| 18 | #include <error.h> | 68 | #include <error.h> |
| 19 | 69 | ||
| 70 | #include <threads.h> | ||
| 71 | |||
| 72 | /** | ||
| 73 | * @brief Read the entire contents of a file descriptor into a malloc()'ed buffer | ||
| 74 | * | ||
| 75 | * @param str Pointer to a string. Will be replaced with the malloc()'ed string | ||
| 76 | * @param initsize The initial size of the malloc()'ed string | ||
| 77 | * @param fd A file descriptor to read from | ||
| 78 | * @retval (int)[-1, strlen(str)] Returns the size of the array on success, or -1 on error | ||
| 79 | * @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 | ||
| 80 | */ | ||
| 20 | int rwbuf(char **str, unsigned long int initsize, int fd) { | 81 | int rwbuf(char **str, unsigned long int initsize, int fd) { |
| 21 | // Try to read bytes from fd into str | 82 | // Try to read bytes from fd into str |
| 22 | // Bytes read == 0, return 0 | 83 | // Bytes read == 0, return 0 |
| @@ -32,7 +93,7 @@ int rwbuf(char **str, unsigned long int initsize, int fd) { | |||
| 32 | lstr = calloc(initsize, sizeof(char)); | 93 | lstr = calloc(initsize, sizeof(char)); |
| 33 | if(!lstr) | 94 | if(!lstr) |
| 34 | return -1; | 95 | return -1; |
| 35 | 96 | ||
| 36 | while((bytesread = read(fd, lstr + (csize - ccap), ccap)) > 0) { | 97 | while((bytesread = read(fd, lstr + (csize - ccap), ccap)) > 0) { |
| 37 | ccap -= bytesread; | 98 | ccap -= bytesread; |
| 38 | if(ccap <= 0) { | 99 | if(ccap <= 0) { |
| @@ -43,7 +104,7 @@ int rwbuf(char **str, unsigned long int initsize, int fd) { | |||
| 43 | if(!tmp) { | 104 | if(!tmp) { |
| 44 | if(___VXGG___VERBOSE_ERRORS___) | 105 | if(___VXGG___VERBOSE_ERRORS___) |
| 45 | error(0, errno, "Could not reallocate enough space for lstr"); | 106 | error(0, errno, "Could not reallocate enough space for lstr"); |
| 46 | 107 | ||
| 47 | free(lstr); | 108 | free(lstr); |
| 48 | lstr = NULL; // Need to set this because of the break | 109 | lstr = NULL; // Need to set this because of the break |
| 49 | bytesread = ECODE; | 110 | bytesread = ECODE; |
| @@ -81,6 +142,15 @@ int rwbuf(char **str, unsigned long int initsize, int fd) { | |||
| 81 | } | 142 | } |
| 82 | 143 | ||
| 83 | 144 | ||
| 145 | /** | ||
| 146 | * @brief Write the entire contents of a buffer into a file descriptor | ||
| 147 | * | ||
| 148 | * @param fd The file descriptor to write to | ||
| 149 | * @param buf The buffer to write from | ||
| 150 | * @param len The length of the buffer | ||
| 151 | * @retval (int)[-1, 0] Returns 0 on success, -1 on error | ||
| 152 | */ | ||
| 153 | |||
| 84 | int wwbuf(int fd, const unsigned char *buf, int len) { | 154 | int wwbuf(int fd, const unsigned char *buf, int len) { |
| 85 | if(!buf || len <= 0) ERRRET(EINVAL, -1); | 155 | if(!buf || len <= 0) ERRRET(EINVAL, -1); |
| 86 | 156 | ||
| @@ -89,7 +159,7 @@ int wwbuf(int fd, const unsigned char *buf, int len) { | |||
| 89 | int n = -1; | 159 | int n = -1; |
| 90 | 160 | ||
| 91 | while(total < len) { | 161 | while(total < len) { |
| 92 | if((n = write(fd, buf + total, left)) < 0) | 162 | if((n = write(fd, buf + total, left)) < 0) |
| 93 | break; | 163 | break; |
| 94 | 164 | ||
| 95 | total += n; | 165 | total += n; |
| @@ -102,7 +172,12 @@ int wwbuf(int fd, const unsigned char *buf, int len) { | |||
| 102 | // https://beej.us/guide/bgnet/html/split/slightly-advanced-techniques.html#sendall | 172 | // https://beej.us/guide/bgnet/html/split/slightly-advanced-techniques.html#sendall |
| 103 | // Thanks Beej! | 173 | // Thanks Beej! |
| 104 | 174 | ||
| 105 | // dirname but less retarded hopefully | 175 | /** |
| 176 | * @brief `dirname()` reimplementation that returns a malloc()'ed string | ||
| 177 | * | ||
| 178 | * @param path The filepath to be inspected | ||
| 179 | * @retval (char*)[NULL, char*] Returns a null-terminated string on success, or `null` on error | ||
| 180 | */ | ||
| 106 | char * vxdirname(const char * const path) { | 181 | char * vxdirname(const char * const path) { |
| 107 | char *tmp = NULL; | 182 | char *tmp = NULL; |
| 108 | if(!path) { // Path being null is a special case which should return early, before anything else (as to avoid null dereference) | 183 | 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) { | |||
| 201 | } | 276 | } |
| 202 | 277 | ||
| 203 | 278 | ||
| 279 | /** | ||
| 280 | * @brief Initialize a cleanup object | ||
| 281 | * | ||
| 282 | * @param loc The cleanup object to be initialized | ||
| 283 | * @param callbacks An array of free()-like callbacks. Must be `size` elements long | ||
| 284 | * @param arguments An array of void pointers. Must be `size` elements long | ||
| 285 | * @param size The number of elements the callbacks and arguments array are long | ||
| 286 | * @retval (int)[-1, 0] Returns 0 on success, -1 on error | ||
| 287 | */ | ||
| 204 | int cleanup_init(cleanup *loc, fcallback callbacks[], void *arguments[], int size) { | 288 | int cleanup_init(cleanup *loc, fcallback callbacks[], void *arguments[], int size) { |
| 205 | if(!loc || !callbacks || !arguments || size <= 0) ERRRET(EINVAL, -1); | 289 | if(!loc || !callbacks || !arguments || size <= 0) ERRRET(EINVAL, -1); |
| 206 | 290 | ||
| @@ -212,6 +296,14 @@ int cleanup_init(cleanup *loc, fcallback callbacks[], void *arguments[], int siz | |||
| 212 | return 0; | 296 | return 0; |
| 213 | } | 297 | } |
| 214 | 298 | ||
| 299 | /** | ||
| 300 | * @brief Register a new callback and argument onto a cleanup stack | ||
| 301 | * | ||
| 302 | * @param loc The cleanup object to modify | ||
| 303 | * @param cb A free()-like callback to run | ||
| 304 | * @param arg A piece of data for the callback to run | ||
| 305 | * @retval (int)[-1, 0] Returns 0 on success, -1 on error | ||
| 306 | */ | ||
| 215 | int cleanup_register(cleanup *loc, fcallback cb, void *arg) { | 307 | int cleanup_register(cleanup *loc, fcallback cb, void *arg) { |
| 216 | if(!loc || !cb) ERRRET(EINVAL, -1); | 308 | if(!loc || !cb) ERRRET(EINVAL, -1); |
| 217 | if(loc->used >= loc->size || loc->used < 0) ERRRET(ENOMEM, -1); | 309 | if(loc->used >= loc->size || loc->used < 0) ERRRET(ENOMEM, -1); |
| @@ -223,18 +315,39 @@ int cleanup_register(cleanup *loc, fcallback cb, void *arg) { | |||
| 223 | return 0; | 315 | return 0; |
| 224 | } | 316 | } |
| 225 | 317 | ||
| 226 | // registers if flag is NOT set | 318 | /** |
| 319 | * @brief Conditionally register a callback and argument | ||
| 320 | * | ||
| 321 | * @param loc The cleanup object to modify | ||
| 322 | * @param cb A free()-like callback to run | ||
| 323 | * @param arg A piece of data for the callback to run | ||
| 324 | * @param flag Whether or not the register should take place. Will not run if `flag` is non-zero | ||
| 325 | * @retval (int)[-1, 0] Returns 0 on success or skip, -1 on error | ||
| 326 | */ | ||
| 227 | int cleanup_cndregister(cleanup *loc, fcallback cb, void *arg, unsigned char flag) { | 327 | int cleanup_cndregister(cleanup *loc, fcallback cb, void *arg, unsigned char flag) { |
| 228 | if(flag) return 0; | 328 | if(flag) return 0; |
| 229 | return cleanup_register(loc, cb, arg); | 329 | return cleanup_register(loc, cb, arg); |
| 230 | } | 330 | } |
| 231 | 331 | ||
| 332 | /** | ||
| 333 | * @brief Clear a cleanup object | ||
| 334 | * @attention Does not free any registered callbacks or arguments, just marks them as available space | ||
| 335 | * | ||
| 336 | * @param loc The cleanup object to modify | ||
| 337 | * @retval (int)[-1, 0] Returns 0 on success, -1 on error | ||
| 338 | */ | ||
| 232 | int cleanup_clear(cleanup *loc) { | 339 | int cleanup_clear(cleanup *loc) { |
| 233 | if(!loc) ERRRET(EINVAL, -1); | 340 | if(!loc) ERRRET(EINVAL, -1); |
| 234 | loc->used = 0; | 341 | loc->used = 0; |
| 235 | return 0; | 342 | return 0; |
| 236 | } | 343 | } |
| 237 | 344 | ||
| 345 | /** | ||
| 346 | * @brief Fires all the registered callbacks and arguments in a cleanup object in FIFO (stack) order | ||
| 347 | * | ||
| 348 | * @param loc The cleanup object to fire | ||
| 349 | * @retval (int)[-1, 0] Returns 0 on success, -1 on error | ||
| 350 | */ | ||
| 238 | int cleanup_fire(cleanup *loc) { | 351 | int cleanup_fire(cleanup *loc) { |
| 239 | if(!loc) ERRRET(EINVAL, -1); | 352 | if(!loc) ERRRET(EINVAL, -1); |
| 240 | 353 | ||
| @@ -251,9 +364,49 @@ int cleanup_fire(cleanup *loc) { | |||
| 251 | return 0; | 364 | return 0; |
| 252 | } | 365 | } |
| 253 | 366 | ||
| 254 | // Fires if flag is set | 367 | /** |
| 368 | * @brief Conditionally fires a cleanup object | ||
| 369 | * | ||
| 370 | * @param loc The cleanup object in question | ||
| 371 | * @param flag Whether the object should be fired. Will skip firing if non-zero | ||
| 372 | * @retval (int)[-1, 0] Returns 0 on success, -1 on error | ||
| 373 | */ | ||
| 255 | int cleanup_cndfire(cleanup *loc, unsigned char flag) { | 374 | int cleanup_cndfire(cleanup *loc, unsigned char flag) { |
| 256 | if(flag) | 375 | if(flag) |
| 257 | return cleanup_fire(loc); | 376 | return cleanup_fire(loc); |
| 258 | return 0; | 377 | return 0; |
| 259 | } \ No newline at end of file | 378 | } |
| 379 | |||
| 380 | /** | ||
| 381 | * @brief Initializes a set of variables suitable for use in the cleanup macros | ||
| 382 | * @param size The number of elements long each array should be | ||
| 383 | */ | ||
| 384 | #define cleanup_CREATE(size) \ | ||
| 385 | cleanup __CLEANUP; \ | ||
| 386 | fcallback __CLEANUP_FUNCS[(size)]; \ | ||
| 387 | void *__CLEANUP_ARGS[(size)]; \ | ||
| 388 | unsigned char __FLAG = 0; \ | ||
| 389 | cleanup_init(&__CLEANUP, __CLEANUP_FUNCS, __CLEANUP_ARGS, (size)) | ||
| 390 | |||
| 391 | //! Register a callback-argument pair using the local cleanup object | ||
| 392 | #define cleanup_REGISTER(cb, arg) cleanup_register(&__CLEANUP, (cb), (arg)) | ||
| 393 | //! Conditionally register a callback-argument pair using the local cleanup object | ||
| 394 | #define cleanup_CNDREGISTER(cb, arg) cleanup_cndregister(&__CLEANUP, (cb), (arg), __FLAG) | ||
| 395 | //! Clean the local cleanup object | ||
| 396 | #define cleanup_CLEAR() cleanup_clear(&__CLEANUP) | ||
| 397 | //! Fire the local cleanup object | ||
| 398 | #define cleanup_FIRE() cleanup_fire(&__CLEANUP) | ||
| 399 | //! Conditionally fire the local cleanup object | ||
| 400 | #define cleanup_CNDFIRE() cleanup_cndfire(&__CLEANUP, __FLAG) | ||
| 401 | //! Set the local cleanup flag to a non-zero number | ||
| 402 | #define cleanup_MARK() (__FLAG = 1) | ||
| 403 | //! Set the local cleanup flag to zero | ||
| 404 | #define cleanup_UNMARK() (__FLAG = 0) | ||
| 405 | //! Check if the local cleanup flag is non-zero | ||
| 406 | #define cleanup_ERRORFLAGGED (__FLAG != 0) | ||
| 407 | //! Conditionally execute some `code` if the local cleanup flag has not been marked | ||
| 408 | #define cleanup_CNDEXEC(code) while(!cleanup_ERRORFLAGGED) {code; break;} | ||
| 409 | //! Conditionally fire the local cleanup object and return `ret` | ||
| 410 | #define cleanup_CNDFIRERET(ret) do {cleanup_CNDFIRE(); return ret;} while (0) | ||
| 411 | |||
| 412 | #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 @@ | |||
| 1 | /** | ||
| 2 | * @file shared.h | ||
| 3 | * @author syxhe (https://t.me/syxhe) | ||
| 4 | * @brief A collection of functions and macros shared between files, or without a better place | ||
| 5 | * @version 0.1 | ||
| 6 | * @date 2025-06-09 | ||
| 7 | * | ||
| 8 | * @copyright Copyright (c) 2025 | ||
| 9 | * | ||
| 10 | */ | ||
| 11 | |||
| 12 | #ifndef __VXGG_REWRITE___SHARED_H___3880294315821___ | ||
| 13 | #define __VXGG_REWRITE___SHARED_H___3880294315821___ 1 | ||
| 14 | |||
| 15 | #include <stddef.h> | ||
| 16 | |||
| 17 | typedef int (*gcallback)(void*); //!< Generic callback signature | ||
| 18 | typedef void (*fcallback)(void*); //!< free()-like callback signature | ||
| 19 | |||
| 20 | //! Get the size of a statically/vla defined array. Doesn't work on arrays allocated via `malloc()` | ||
| 21 | #define STATIC_ARRAY_LEN(arr) (sizeof((arr))/sizeof((arr)[0])) | ||
| 22 | |||
| 23 | //! Sets errno to `errval`, then returns `retval`. Defined as a macro because it is meant for use inside functions, not as a function itself | ||
| 24 | #define ERRRET(errval, retval) do {\ | ||
| 25 | errno = (errval);\ | ||
| 26 | return (retval);\ | ||
| 27 | } while (0) | ||
| 28 | |||
| 29 | /// Defines how `x___alloc()` functions should exit. `___VXGG___XALLOC_EXIT_ON_ERROR___ > 0` calls `error()`, and thus functions | ||
| 30 | /// registered with `atexit()` and `on_exit()`. `___VXGG___XALLOC_EXIT_ON_ERROR___ <= 0` calls `abort()` on error. `x___alloc()` | ||
| 31 | /// type functions will ALWAYS 'abort', doing otherwise defeats the purpose of the function type | ||
| 32 | #define ___VXGG___XALLOC_EXIT_ON_ERROR___ 1 | ||
| 33 | |||
| 34 | /// Defines whether vxgg functions that can error print out a short warning of the error when one is encountered. | ||
| 35 | /// `___VXGG___VERBOSE_ERRORS___ > 0` will print diagnostic error messages, and will do nothing otherwise | ||
| 36 | #define ___VXGG___VERBOSE_ERRORS___ 1 | ||
| 37 | |||
| 38 | //! Macro to exit on an alloc error instead of doing the terrible nested if statement that was being used previously | ||
| 39 | #define XALLOC_EXIT(msg, ...) do {\ | ||
| 40 | if(!___VXGG___XALLOC_EXIT_ON_ERROR___)\ | ||
| 41 | abort();\ | ||
| 42 | if(!___VXGG___VERBOSE_ERRORS___)\ | ||
| 43 | exit(EXIT_FAILURE);\ | ||
| 44 | error(EXIT_FAILURE, errno, (msg)__VA_ARGS__);\ | ||
| 45 | exit(EXIT_FAILURE); /* Makes gcc happy */\ | ||
| 46 | } while (0) | ||
| 47 | |||
| 48 | |||
| 49 | //! 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 | ||
| 50 | #define VALLOC(nmemb, size) malloc((nmemb) * (size)) | ||
| 51 | |||
| 52 | //! Error macro that gcc will not complain about | ||
| 53 | #define ERROR(status, errnum, format, ...) do {error((status), (errnum), (format)__VA_ARGS__); exit((status));} while (0) | ||
| 54 | //! Spit out a warning using `error` | ||
| 55 | #define WARN(errnum, format, ...) do {error(0, (errnum), (format)__VA_ARGS__);} while (0) | ||
| 56 | |||
| 57 | |||
| 58 | |||
| 59 | /** | ||
| 60 | * @brief Read the entire contents of a file descriptor into a malloc()'ed buffer | ||
| 61 | * | ||
| 62 | * @param str Pointer to a string. Will be replaced with the malloc()'ed string | ||
| 63 | * @param initsize The initial size of the malloc()'ed string | ||
| 64 | * @param fd A file descriptor to read from | ||
| 65 | * @retval (int)[-1, strlen(str)] Returns the size of the array on success, or -1 on error | ||
| 66 | * @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 | ||
| 67 | */ | ||
| 68 | int rwbuf(char **str, unsigned long int initsize, int fd); | ||
| 69 | |||
| 70 | /** | ||
| 71 | * @brief Write the entire contents of a buffer into a file descriptor | ||
| 72 | * | ||
| 73 | * @param fd The file descriptor to write to | ||
| 74 | * @param buf The buffer to write from | ||
| 75 | * @param len The length of the buffer | ||
| 76 | * @retval (int)[-1, 0] Returns 0 on success, -1 on error | ||
| 77 | */ | ||
| 78 | int wwbuf(int fd, const unsigned char *buf, int len); | ||
| 79 | |||
| 80 | |||
| 81 | |||
| 82 | /** | ||
| 83 | * @brief `dirname()` reimplementation that returns a malloc()'ed string | ||
| 84 | * | ||
| 85 | * @param path The filepath to be inspected | ||
| 86 | * @retval (char*)[NULL, char*] Returns a null-terminated string on success, or `null` on error | ||
| 87 | */ | ||
| 88 | char * vxdirname(const char * const path); | ||
| 89 | |||
| 90 | |||
| 91 | |||
| 92 | /** | ||
| 93 | * @brief A locally defined structure designed for easier function cleanup | ||
| 94 | * | ||
| 95 | */ | ||
| 96 | typedef struct cl { | ||
| 97 | fcallback *callbacks; //!< An array of free()-like callbacks. Actual Type: fcallback callbacks[] | ||
| 98 | void * *arguments; //!< An array of void pointers. Actual Type: void *arguments[] | ||
| 99 | int size; //!< The size of each array | ||
| 100 | int used; //!< The current number of used elements in each array | ||
| 101 | } cleanup; | ||
| 102 | |||
| 103 | /** | ||
| 104 | * @brief Initialize a cleanup object | ||
| 105 | * | ||
| 106 | * @param loc The cleanup object to be initialized | ||
| 107 | * @param callbacks An array of free()-like callbacks. Must be `size` elements long | ||
| 108 | * @param arguments An array of void pointers. Must be `size` elements long | ||
| 109 | * @param size The number of elements the callbacks and arguments array are long | ||
| 110 | * @retval (int)[-1, 0] Returns 0 on success, -1 on error | ||
| 111 | */ | ||
| 112 | int cleanup_init(cleanup *loc, fcallback callbacks[], void *arguments[], int size); | ||
| 113 | |||
| 114 | /** | ||
| 115 | * @brief Register a new callback and argument onto a cleanup stack | ||
| 116 | * | ||
| 117 | * @param loc The cleanup object to modify | ||
| 118 | * @param cb A free()-like callback to run | ||
| 119 | * @param arg A piece of data for the callback to run | ||
| 120 | * @retval (int)[-1, 0] Returns 0 on success, -1 on error | ||
| 121 | */ | ||
| 122 | int cleanup_register(cleanup *loc, fcallback cb, void *arg); | ||
| 123 | |||
| 124 | /** | ||
| 125 | * @brief Conditionally register a callback and argument | ||
| 126 | * | ||
| 127 | * @param loc The cleanup object to modify | ||
| 128 | * @param cb A free()-like callback to run | ||
| 129 | * @param arg A piece of data for the callback to run | ||
| 130 | * @param flag Whether or not the register should take place. Will not run if `flag` is non-zero | ||
| 131 | * @retval (int)[-1, 0] Returns 0 on success or skip, -1 on error | ||
| 132 | */ | ||
| 133 | int cleanup_cndregister(cleanup *loc, fcallback cb, void *arg, unsigned char flag); | ||
| 134 | |||
| 135 | /** | ||
| 136 | * @brief Clear a cleanup object | ||
| 137 | * @attention Does not free any registered callbacks or arguments, just marks them as available space | ||
| 138 | * | ||
| 139 | * @param loc The cleanup object to modify | ||
| 140 | * @retval (int)[-1, 0] Returns 0 on success, -1 on error | ||
| 141 | */ | ||
| 142 | int cleanup_clear(cleanup *loc); | ||
| 143 | |||
| 144 | /** | ||
| 145 | * @brief Fires all the registered callbacks and arguments in a cleanup object in FIFO (stack) order | ||
| 146 | * | ||
| 147 | * @param loc The cleanup object to fire | ||
| 148 | * @retval (int)[-1, 0] Returns 0 on success, -1 on error | ||
| 149 | */ | ||
| 150 | int cleanup_fire(cleanup *loc); | ||
| 151 | |||
| 152 | /** | ||
| 153 | * @brief Conditionally fires a cleanup object | ||
| 154 | * | ||
| 155 | * @param loc The cleanup object in question | ||
| 156 | * @param flag Whether the object should be fired. Will skip firing if non-zero | ||
| 157 | * @retval (int)[-1, 0] Returns 0 on success, -1 on error | ||
| 158 | */ | ||
| 159 | int cleanup_cndfire(cleanup *loc, unsigned char flag); | ||
| 160 | |||
| 161 | /** | ||
| 162 | * @brief Initializes a set of variables suitable for use in the cleanup macros | ||
| 163 | * @param size The number of elements long each array should be | ||
| 164 | */ | ||
| 165 | #define cleanup_CREATE(size) \ | ||
| 166 | cleanup __CLEANUP; \ | ||
| 167 | fcallback __CLEANUP_FUNCS[(size)]; \ | ||
| 168 | void *__CLEANUP_ARGS[(size)]; \ | ||
| 169 | unsigned char __FLAG = 0; \ | ||
| 170 | cleanup_init(&__CLEANUP, __CLEANUP_FUNCS, __CLEANUP_ARGS, (size)) | ||
| 171 | |||
| 172 | //! Register a callback-argument pair using the local cleanup object | ||
| 173 | #define cleanup_REGISTER(cb, arg) cleanup_register(&__CLEANUP, (cb), (arg)) | ||
| 174 | //! Conditionally register a callback-argument pair using the local cleanup object | ||
| 175 | #define cleanup_CNDREGISTER(cb, arg) cleanup_cndregister(&__CLEANUP, (cb), (arg), __FLAG) | ||
| 176 | //! Clean the local cleanup object | ||
| 177 | #define cleanup_CLEAR() cleanup_clear(&__CLEANUP) | ||
| 178 | //! Fire the local cleanup object | ||
| 179 | #define cleanup_FIRE() cleanup_fire(&__CLEANUP) | ||
| 180 | //! Conditionally fire the local cleanup object | ||
| 181 | #define cleanup_CNDFIRE() cleanup_cndfire(&__CLEANUP, __FLAG) | ||
| 182 | //! Set the local cleanup flag to a non-zero number | ||
| 183 | #define cleanup_MARK() (__FLAG = 1) | ||
| 184 | //! Set the local cleanup flag to zero | ||
| 185 | #define cleanup_UNMARK() (__FLAG = 0) | ||
| 186 | //! Check if the local cleanup flag is non-zero | ||
| 187 | #define cleanup_ERRORFLAGGED (__FLAG != 0) | ||
| 188 | //! Conditionally execute some `code` if the local cleanup flag has not been marked | ||
| 189 | #define cleanup_CNDEXEC(code) while(!cleanup_ERRORFLAGGED) {code; break;} | ||
| 190 | |||
| 191 | #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 @@ | |||
| 1 | /** | 1 | /** |
| 2 | * @file threadpool.c | 2 | * @file threadpool.c |
| 3 | * @author syxhe (https://t.me/syxhe) | 3 | * @author syxhe (https://t.me/syxhe) |
| 4 | * @brief *Implementing `threadpool.h`* | 4 | * @brief An implementation of a threadpool using libc threads |
| 5 | * @version 0.1 | 5 | * @version 0.1 |
| 6 | * @date 2025-06-09 | 6 | * @date 2025-06-09 |
| 7 | * | 7 | * |
| 8 | * @copyright Copyright (c) 2025 | 8 | * @copyright Copyright (c) 2025 |
| 9 | * | 9 | * |
| 10 | */ | 10 | */ |
| 11 | 11 | ||
| 12 | #include "threadpool.h" | 12 | #ifndef __VXGG_REWRITE___THREADPOOL_C___193271180830131___ |
| 13 | #define __VXGG_REWRITE___THREADPOOL_C___193271180830131___ 1 | ||
| 14 | |||
| 15 | #include "shared.c" | ||
| 13 | 16 | ||
| 14 | #include <threads.h> | 17 | #include <threads.h> |
| 15 | #include <stdlib.h> | 18 | #include <stdlib.h> |
| @@ -18,7 +21,7 @@ | |||
| 18 | 21 | ||
| 19 | /** | 22 | /** |
| 20 | * @brief A generic task - A function, data for that function, and a way to free the data | 23 | * @brief A generic task - A function, data for that function, and a way to free the data |
| 21 | * | 24 | * |
| 22 | */ | 25 | */ |
| 23 | typedef struct task { | 26 | typedef struct task { |
| 24 | gcallback callback; //!< A generic callback to be ran when executing the task | 27 | gcallback callback; //!< A generic callback to be ran when executing the task |
| @@ -28,7 +31,7 @@ typedef struct task { | |||
| 28 | 31 | ||
| 29 | /** | 32 | /** |
| 30 | * @brief An internal structure used for the `taskqueue`. Analogous to a doubly-linked list's internal node | 33 | * @brief An internal structure used for the `taskqueue`. Analogous to a doubly-linked list's internal node |
| 31 | * | 34 | * |
| 32 | */ | 35 | */ |
| 33 | typedef struct tqnode { | 36 | typedef struct tqnode { |
| 34 | struct tqnode *next; //!< The next element in the `taskqueue` | 37 | struct tqnode *next; //!< The next element in the `taskqueue` |
| @@ -38,7 +41,7 @@ typedef struct tqnode { | |||
| 38 | 41 | ||
| 39 | /** | 42 | /** |
| 40 | * @brief A FIFO queue of tasks | 43 | * @brief A FIFO queue of tasks |
| 41 | * | 44 | * |
| 42 | */ | 45 | */ |
| 43 | typedef struct taskqueue { | 46 | typedef struct taskqueue { |
| 44 | tqnode *start; //!< The first element of the queue | 47 | tqnode *start; //!< The first element of the queue |
| @@ -48,7 +51,7 @@ typedef struct taskqueue { | |||
| 48 | 51 | ||
| 49 | /** | 52 | /** |
| 50 | * @brief A `taskqueue` built for concurrent access. Essentially a threadpool | 53 | * @brief A `taskqueue` built for concurrent access. Essentially a threadpool |
| 51 | * | 54 | * |
| 52 | */ | 55 | */ |
| 53 | typedef struct ctqueue { | 56 | typedef struct ctqueue { |
| 54 | mtx_t mutex; //!< A mutex for locking sensitive resources | 57 | mtx_t mutex; //!< A mutex for locking sensitive resources |
| @@ -60,7 +63,14 @@ typedef struct ctqueue { | |||
| 60 | int talen; //!< The length of the thread array | 63 | int talen; //!< The length of the thread array |
| 61 | } ctqueue; | 64 | } ctqueue; |
| 62 | 65 | ||
| 63 | 66 | /** | |
| 67 | * @brief Create a task | ||
| 68 | * | ||
| 69 | * @param callback Callback function the given data should be ran with. Must be non-null | ||
| 70 | * @param freecb Callback function for freeing the given data. May be null | ||
| 71 | * @param data Data to be passed to the callback. May be null | ||
| 72 | * @retval (task*)[NULL, task*] Returns a task object with set parameters. Returns `null` and sets errno on error | ||
| 73 | */ | ||
| 64 | task * task_init(gcallback callback, fcallback freecb, void *data) { | 74 | task * task_init(gcallback callback, fcallback freecb, void *data) { |
| 65 | if(callback == NULL) ERRRET(EINVAL, NULL); | 75 | if(callback == NULL) ERRRET(EINVAL, NULL); |
| 66 | 76 | ||
| @@ -75,11 +85,16 @@ task * task_init(gcallback callback, fcallback freecb, void *data) { | |||
| 75 | return tsk; | 85 | return tsk; |
| 76 | } | 86 | } |
| 77 | 87 | ||
| 88 | /** | ||
| 89 | * @brief Free a task | ||
| 90 | * | ||
| 91 | * @param tsk A task object to free. Frees data associated with task via `freecb` value specified in its creation. May be null | ||
| 92 | */ | ||
| 78 | void task_free(void *tsk) { | 93 | void task_free(void *tsk) { |
| 79 | task *real = (task *)tsk; | 94 | task *real = (task *)tsk; |
| 80 | if(!real) | 95 | if(!real) |
| 81 | return; | 96 | return; |
| 82 | 97 | ||
| 83 | if(real->freecb != NULL) | 98 | if(real->freecb != NULL) |
| 84 | real->freecb(real->data); | 99 | real->freecb(real->data); |
| 85 | free(real); | 100 | free(real); |
| @@ -87,11 +102,23 @@ void task_free(void *tsk) { | |||
| 87 | return; | 102 | return; |
| 88 | } | 103 | } |
| 89 | 104 | ||
| 105 | /** | ||
| 106 | * @brief Fire a task. Passes the `data` member to the specified `callback` | ||
| 107 | * | ||
| 108 | * @param tsk A task to be fired. Must be non-null | ||
| 109 | * @retval (int) Returns value of the fired callback. Returns -1 and sets errno on error | ||
| 110 | */ | ||
| 90 | int task_fire(task *tsk) { | 111 | int task_fire(task *tsk) { |
| 91 | if(!tsk) ERRRET(EINVAL, -1); | 112 | if(!tsk) ERRRET(EINVAL, -1); |
| 92 | return tsk->callback(tsk->data); | 113 | return tsk->callback(tsk->data); |
| 93 | } | 114 | } |
| 94 | 115 | ||
| 116 | /** | ||
| 117 | * @brief Fire and destroy a task simultaneously. Calls specified callback and free-callback on associated data | ||
| 118 | * | ||
| 119 | * @param tsk Task to be fired and destroyed. Must be non-null | ||
| 120 | * @retval (int) Returns value of the callback. Returns -1 and sets errno on error | ||
| 121 | */ | ||
| 95 | int task_fired(task *tsk) { | 122 | int task_fired(task *tsk) { |
| 96 | int retval = task_fire(tsk); | 123 | int retval = task_fire(tsk); |
| 97 | if(errno == EINVAL && retval == -1) {return -1;} | 124 | if(errno == EINVAL && retval == -1) {return -1;} |
| @@ -125,7 +152,11 @@ void tqnode_free(void *tqn) { | |||
| 125 | 152 | ||
| 126 | 153 | ||
| 127 | 154 | ||
| 128 | 155 | /** | |
| 156 | * @brief Create a FIFO queue of tasks | ||
| 157 | * | ||
| 158 | * @retval (taskqueue*)[NULL, taskqueue*] Returns a new taskqueue object. Returns `null` and sets errno on error | ||
| 159 | */ | ||
| 129 | taskqueue * taskqueue_init(void) { | 160 | taskqueue * taskqueue_init(void) { |
| 130 | taskqueue *tq = calloc(1, sizeof(*tq)); | 161 | taskqueue *tq = calloc(1, sizeof(*tq)); |
| 131 | if(!tq) | 162 | if(!tq) |
| @@ -138,6 +169,11 @@ taskqueue * taskqueue_init(void) { | |||
| 138 | return tq; | 169 | return tq; |
| 139 | } | 170 | } |
| 140 | 171 | ||
| 172 | /** | ||
| 173 | * @brief Free a taskqueue | ||
| 174 | * | ||
| 175 | * @param tq A taskqueue to be freed. May be null | ||
| 176 | */ | ||
| 141 | void taskqueue_free(void *tq) { | 177 | void taskqueue_free(void *tq) { |
| 142 | if(!tq) | 178 | if(!tq) |
| 143 | return; | 179 | return; |
| @@ -148,7 +184,7 @@ void taskqueue_free(void *tq) { | |||
| 148 | p = n; | 184 | p = n; |
| 149 | } | 185 | } |
| 150 | free(tq); | 186 | free(tq); |
| 151 | 187 | ||
| 152 | return; | 188 | return; |
| 153 | } | 189 | } |
| 154 | 190 | ||
| @@ -167,6 +203,13 @@ int taskqueue_handlefirst(taskqueue *tq, task *tsk) { | |||
| 167 | return 1; | 203 | return 1; |
| 168 | } | 204 | } |
| 169 | 205 | ||
| 206 | /** | ||
| 207 | * @brief Push a task onto a taskqueue | ||
| 208 | * | ||
| 209 | * @param tq The taskqueue to be modified. Must be non-null | ||
| 210 | * @param tsk The task to push. Must be non-null | ||
| 211 | * @retval (int)[-1, 0] Returns 0 on success, sets errno and returns -1 on error | ||
| 212 | */ | ||
| 170 | int taskqueue_push(taskqueue *tq, task *tsk) { | 213 | int taskqueue_push(taskqueue *tq, task *tsk) { |
| 171 | if(!tq || !tsk) ERRRET(EINVAL, -1); | 214 | if(!tq || !tsk) ERRRET(EINVAL, -1); |
| 172 | 215 | ||
| @@ -184,12 +227,18 @@ int taskqueue_push(taskqueue *tq, task *tsk) { | |||
| 184 | return 0; | 227 | return 0; |
| 185 | } | 228 | } |
| 186 | 229 | ||
| 230 | /** | ||
| 231 | * @brief Pop a task from a taskqueue | ||
| 232 | * | ||
| 233 | * @param tq A taskqueue to grab a task from. Must be non-null | ||
| 234 | * @retval (task*)[NULL, task*] Returns a task on success, sets errno and returns `null` on error | ||
| 235 | */ | ||
| 187 | task * taskqueue_pop(taskqueue *tq) { | 236 | task * taskqueue_pop(taskqueue *tq) { |
| 188 | if(!tq) ERRRET(EINVAL, NULL); | 237 | if(!tq) ERRRET(EINVAL, NULL); |
| 189 | if(tq->size <= 0) ERRRET(ENODATA, NULL); | 238 | if(tq->size <= 0) ERRRET(ENODATA, NULL); |
| 190 | 239 | ||
| 191 | tqnode *end = tq->end; | 240 | tqnode *end = tq->end; |
| 192 | task *ret = end->task; | 241 | task *ret = end->task; |
| 193 | 242 | ||
| 194 | if(tq->size == 1) { | 243 | if(tq->size == 1) { |
| 195 | tq->end = NULL; | 244 | tq->end = NULL; |
| @@ -204,6 +253,13 @@ task * taskqueue_pop(taskqueue *tq) { | |||
| 204 | return ret; | 253 | return ret; |
| 205 | } | 254 | } |
| 206 | 255 | ||
| 256 | /** | ||
| 257 | * @brief Append a task to the front of a taskqueue | ||
| 258 | * | ||
| 259 | * @param tq The taskqueue to be modified. Must be non-null | ||
| 260 | * @param tsk The task to be appended. Must be non-null | ||
| 261 | * @retval (int)[-1, 0] Returns 0 on success, sets errno and returns -1 on error | ||
| 262 | */ | ||
| 207 | int taskqueue_pushfront(taskqueue *tq, task *tsk) { | 263 | int taskqueue_pushfront(taskqueue *tq, task *tsk) { |
| 208 | if(!tq || !tsk) ERRRET(EINVAL, -1); | 264 | if(!tq || !tsk) ERRRET(EINVAL, -1); |
| 209 | 265 | ||
| @@ -221,6 +277,12 @@ int taskqueue_pushfront(taskqueue *tq, task *tsk) { | |||
| 221 | return 0; | 277 | return 0; |
| 222 | } | 278 | } |
| 223 | 279 | ||
| 280 | /** | ||
| 281 | * @brief Pop a task from the back (most recently pushed task) of a taskqueue | ||
| 282 | * | ||
| 283 | * @param tq A taskqueue to pop from. Must be non-null | ||
| 284 | * @retval (task*)[NULL, task*] Returns a task on success, sets errno and returns `null` on error | ||
| 285 | */ | ||
| 224 | task * taskqueue_popback(taskqueue *tq) { | 286 | task * taskqueue_popback(taskqueue *tq) { |
| 225 | if(!tq) ERRRET(EINVAL, NULL); | 287 | if(!tq) ERRRET(EINVAL, NULL); |
| 226 | if(tq->size <= 0) ERRRET(ENODATA, NULL); | 288 | if(tq->size <= 0) ERRRET(ENODATA, NULL); |
| @@ -273,6 +335,12 @@ static void ___ucl_cnddestroy(void *cond) { | |||
| 273 | return; | 335 | return; |
| 274 | } | 336 | } |
| 275 | 337 | ||
| 338 | /** | ||
| 339 | * @brief Create a concurrent taskqueue with `size` allocated threads | ||
| 340 | * | ||
| 341 | * @param size Number of threads in the threadpool. Must be greater than zero | ||
| 342 | * @retval (ctqueue*)[NULL, ctqueue*] Returns a new ctqueue, sets errno and returns `null` on error | ||
| 343 | */ | ||
| 276 | ctqueue * ctqueue_init(int nthreads) { | 344 | ctqueue * ctqueue_init(int nthreads) { |
| 277 | if(nthreads <= 0) ERRRET(EINVAL, NULL); | 345 | if(nthreads <= 0) ERRRET(EINVAL, NULL); |
| 278 | cleanup_CREATE(6); | 346 | cleanup_CREATE(6); |
| @@ -291,7 +359,7 @@ ctqueue * ctqueue_init(int nthreads) { | |||
| 291 | cleanup_MARK(); | 359 | cleanup_MARK(); |
| 292 | cleanup_CNDREGISTER(taskqueue_free, ctq->tq); | 360 | cleanup_CNDREGISTER(taskqueue_free, ctq->tq); |
| 293 | ); | 361 | ); |
| 294 | 362 | ||
| 295 | cleanup_CNDEXEC( | 363 | cleanup_CNDEXEC( |
| 296 | if(mtx_init(&ctq->mutex, mtx_plain) != thrd_success) | 364 | if(mtx_init(&ctq->mutex, mtx_plain) != thrd_success) |
| 297 | cleanup_MARK(); | 365 | cleanup_MARK(); |
| @@ -303,7 +371,7 @@ ctqueue * ctqueue_init(int nthreads) { | |||
| 303 | cleanup_MARK(); | 371 | cleanup_MARK(); |
| 304 | cleanup_CNDREGISTER(___ucl_cnddestroy, (void*)&ctq->cond); | 372 | cleanup_CNDREGISTER(___ucl_cnddestroy, (void*)&ctq->cond); |
| 305 | ); | 373 | ); |
| 306 | 374 | ||
| 307 | cleanup_CNDEXEC( | 375 | cleanup_CNDEXEC( |
| 308 | ctq->thrdarr = calloc(ctq->talen, sizeof(thrd_t)); | 376 | ctq->thrdarr = calloc(ctq->talen, sizeof(thrd_t)); |
| 309 | if(!ctq->thrdarr) | 377 | if(!ctq->thrdarr) |
| @@ -318,10 +386,16 @@ ctqueue * ctqueue_init(int nthreads) { | |||
| 318 | return ctq; | 386 | return ctq; |
| 319 | } | 387 | } |
| 320 | 388 | ||
| 389 | /** | ||
| 390 | * @brief Cancel all tasks being processed in a currently running concurrent taskqueue | ||
| 391 | * | ||
| 392 | * @param ctq The concurrent taskqueue to be canceled. Must be non-null | ||
| 393 | * @retval (int)[-1, 0] Returns 0 on success, sets errno and returns -1 on error | ||
| 394 | */ | ||
| 321 | int ctqueue_cancel(ctqueue *ctq) { | 395 | int ctqueue_cancel(ctqueue *ctq) { |
| 322 | if(!ctq) ERRRET(EINVAL, -1); | 396 | if(!ctq) ERRRET(EINVAL, -1); |
| 323 | 397 | ||
| 324 | __CTQ_INLOCK(ctq, 1, | 398 | __CTQ_INLOCK(ctq, 1, |
| 325 | ctq->canceled = 1; | 399 | ctq->canceled = 1; |
| 326 | ); | 400 | ); |
| 327 | cnd_broadcast(&ctq->cond); | 401 | cnd_broadcast(&ctq->cond); |
| @@ -329,6 +403,12 @@ int ctqueue_cancel(ctqueue *ctq) { | |||
| 329 | return 0; | 403 | return 0; |
| 330 | } | 404 | } |
| 331 | 405 | ||
| 406 | /** | ||
| 407 | * @brief Free a concurrent taskqueue | ||
| 408 | * @attention This cancels all currently running threads via `ctqueue_cancel` | ||
| 409 | * | ||
| 410 | * @param ctq The concurrent taskqueue to free. May be null | ||
| 411 | */ | ||
| 332 | void ctqueue_free(void *ctq) { | 412 | void ctqueue_free(void *ctq) { |
| 333 | if(!ctq) | 413 | if(!ctq) |
| 334 | return; | 414 | return; |
| @@ -351,11 +431,19 @@ void ctqueue_free(void *ctq) { | |||
| 351 | return; | 431 | return; |
| 352 | } | 432 | } |
| 353 | 433 | ||
| 434 | /** | ||
| 435 | * @brief Push a task onto a concurrent taskqueue | ||
| 436 | * @attention May block for an indefinite amount of time to push the task | ||
| 437 | * | ||
| 438 | * @param ctq The concurrent taskqueue to modify. Must be non-null | ||
| 439 | * @param tsk The task to push. Must be non-null | ||
| 440 | * @retval (int) Returns `thrd_success` on success, returns `thrd_error` or `thrd_nomem` on error | ||
| 441 | */ | ||
| 354 | int ctqueue_waitpush(ctqueue *ctq, task *tsk) { | 442 | int ctqueue_waitpush(ctqueue *ctq, task *tsk) { |
| 355 | if(!ctq || !tsk) ERRRET(EINVAL, -1); | 443 | if(!ctq || !tsk) ERRRET(EINVAL, -1); |
| 356 | int retval = 0; | 444 | int retval = 0; |
| 357 | 445 | ||
| 358 | __CTQ_INLOCK(ctq, -1, | 446 | __CTQ_INLOCK(ctq, -1, |
| 359 | retval = taskqueue_push(ctq->tq, tsk); | 447 | retval = taskqueue_push(ctq->tq, tsk); |
| 360 | ); | 448 | ); |
| 361 | if(retval == 0) | 449 | if(retval == 0) |
| @@ -364,11 +452,18 @@ int ctqueue_waitpush(ctqueue *ctq, task *tsk) { | |||
| 364 | return retval; | 452 | return retval; |
| 365 | } | 453 | } |
| 366 | 454 | ||
| 455 | /** | ||
| 456 | * @brief Pop a task from the concurrent taskqueue | ||
| 457 | * @attention May block for an indefinite amount of time to pop the task | ||
| 458 | * | ||
| 459 | * @param ctq The concurrent taskqueue to pop from. Must be non-null | ||
| 460 | * @retval (task*)[NULL, task*] Returns a task on success, sets errno and returns `null` on error | ||
| 461 | */ | ||
| 367 | task * ctqueue_waitpop(ctqueue *ctq) { | 462 | task * ctqueue_waitpop(ctqueue *ctq) { |
| 368 | if(!ctq) ERRRET(EINVAL, NULL); | 463 | if(!ctq) ERRRET(EINVAL, NULL); |
| 369 | task *retval = NULL; | 464 | task *retval = NULL; |
| 370 | 465 | ||
| 371 | __CTQ_INLOCK(ctq, NULL, | 466 | __CTQ_INLOCK(ctq, NULL, |
| 372 | while(taskqueue_size(ctq->tq) == 0 && !ctq->canceled) | 467 | while(taskqueue_size(ctq->tq) == 0 && !ctq->canceled) |
| 373 | cnd_wait(&ctq->cond, &ctq->mutex); | 468 | cnd_wait(&ctq->cond, &ctq->mutex); |
| 374 | 469 | ||
| @@ -386,7 +481,7 @@ task * ctqueue_waitpop(ctqueue *ctq) { | |||
| 386 | //! Simple consumer for eating and executing tasks from the ctq | 481 | //! Simple consumer for eating and executing tasks from the ctq |
| 387 | static int __CTQ_CONSUMER(void *ctq) { | 482 | static int __CTQ_CONSUMER(void *ctq) { |
| 388 | if(!ctq) {errno = EINVAL; thrd_exit(-1);} | 483 | if(!ctq) {errno = EINVAL; thrd_exit(-1);} |
| 389 | ctqueue *real = (ctqueue *)ctq; | 484 | ctqueue *real = (ctqueue *)ctq; |
| 390 | 485 | ||
| 391 | for(task *ctask = NULL;;) { | 486 | for(task *ctask = NULL;;) { |
| 392 | ctask = ctqueue_waitpop(real); | 487 | ctask = ctqueue_waitpop(real); |
| @@ -399,14 +494,21 @@ static int __CTQ_CONSUMER(void *ctq) { | |||
| 399 | 494 | ||
| 400 | thrd_exit(1); | 495 | thrd_exit(1); |
| 401 | } | 496 | } |
| 402 | // TODO: Make this function return 0 or -1 depending on whether the overall ctq has been canceled or not. Canceling shouldn't | 497 | // TODO: Make this function return 0 or -1 depending on whether the overall ctq has been canceled or not. Canceling shouldn't |
| 403 | // be treated as an error | 498 | // be treated as an error |
| 404 | 499 | ||
| 500 | /** | ||
| 501 | * @brief Start the threads allocated to a concurrent taskqueue | ||
| 502 | * @attention Threads will not consume pushed tasks until this function is ran | ||
| 503 | * | ||
| 504 | * @param ctq A concurrent taskqueue to start. Must be non-null | ||
| 505 | * @retval (int)[-1, 0] Returns 0 on success, sets errno and returns -1 on error | ||
| 506 | */ | ||
| 405 | int ctqueue_start(ctqueue *ctq) { | 507 | int ctqueue_start(ctqueue *ctq) { |
| 406 | if(!ctq) ERRRET(EINVAL, -1); | 508 | if(!ctq) ERRRET(EINVAL, -1); |
| 407 | 509 | ||
| 408 | ctq->canceled = 0; | 510 | ctq->canceled = 0; |
| 409 | 511 | ||
| 410 | int retval = 0; | 512 | int retval = 0; |
| 411 | for(int i = 0; i < ctq->talen; i++) | 513 | for(int i = 0; i < ctq->talen; i++) |
| 412 | if((retval = thrd_create(&ctq->thrdarr[i], __CTQ_CONSUMER, ctq)) != thrd_success) | 514 | if((retval = thrd_create(&ctq->thrdarr[i], __CTQ_CONSUMER, ctq)) != thrd_success) |
| @@ -416,4 +518,6 @@ int ctqueue_start(ctqueue *ctq) { | |||
| 416 | ctqueue_cancel(ctq); | 518 | ctqueue_cancel(ctq); |
| 417 | 519 | ||
| 418 | return (retval == thrd_success) ? 0 : -1; | 520 | return (retval == thrd_success) ? 0 : -1; |
| 419 | } \ No newline at end of file | 521 | } |
| 522 | |||
| 523 | #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 @@ | |||
| 1 | /** | ||
| 2 | * @file threadpool.h | ||
| 3 | * @author syxhe (https://t.me/syxhe) | ||
| 4 | * @brief An implementation of a threadpool using libc threads | ||
| 5 | * @version 0.1 | ||
| 6 | * @date 2025-06-09 | ||
| 7 | * | ||
| 8 | * @copyright Copyright (c) 2025 | ||
| 9 | * | ||
| 10 | */ | ||
| 11 | |||
| 12 | #ifndef __VXGG_REWRITE___THREADPOOL_H___193271180830131___ | ||
| 13 | #define __VXGG_REWRITE___THREADPOOL_H___193271180830131___ 1 | ||
| 14 | |||
| 15 | #include "shared.h" | ||
| 16 | #include <threads.h> | ||
| 17 | |||
| 18 | typedef struct task task; | ||
| 19 | typedef struct taskqueue taskqueue; | ||
| 20 | typedef struct ctqueue ctqueue; | ||
| 21 | |||
| 22 | /** | ||
| 23 | * @brief Create a task | ||
| 24 | * | ||
| 25 | * @param callback Callback function the given data should be ran with. Must be non-null | ||
| 26 | * @param freecb Callback function for freeing the given data. May be null | ||
| 27 | * @param data Data to be passed to the callback. May be null | ||
| 28 | * @retval (task*)[NULL, task*] Returns a task object with set parameters. Returns `null` and sets errno on error | ||
| 29 | */ | ||
| 30 | task * task_init(gcallback callback, fcallback freecb, void *data); | ||
| 31 | |||
| 32 | /** | ||
| 33 | * @brief Free a task | ||
| 34 | * | ||
| 35 | * @param tsk A task object to free. Frees data associated with task via `freecb` value specified in its creation. May be null | ||
| 36 | */ | ||
| 37 | void task_free(void *tsk); | ||
| 38 | |||
| 39 | /** | ||
| 40 | * @brief Fire a task. Passes the `data` member to the specified `callback` | ||
| 41 | * | ||
| 42 | * @param tsk A task to be fired. Must be non-null | ||
| 43 | * @retval (int) Returns value of the fired callback. Returns -1 and sets errno on error | ||
| 44 | */ | ||
| 45 | int task_fire(task *tsk); | ||
| 46 | |||
| 47 | /** | ||
| 48 | * @brief Fire and destroy a task simultaneously. Calls specified callback and free-callback on associated data | ||
| 49 | * | ||
| 50 | * @param tsk Task to be fired and destroyed. Must be non-null | ||
| 51 | * @retval (int) Returns value of the callback. Returns -1 and sets errno on error | ||
| 52 | */ | ||
| 53 | int task_fired(task *tsk); | ||
| 54 | |||
| 55 | |||
| 56 | |||
| 57 | /** | ||
| 58 | * @brief Create a FIFO queue of tasks | ||
| 59 | * | ||
| 60 | * @retval (taskqueue*)[NULL, taskqueue*] Returns a new taskqueue object. Returns `null` and sets errno on error | ||
| 61 | */ | ||
| 62 | taskqueue * taskqueue_init(void); | ||
| 63 | |||
| 64 | /** | ||
| 65 | * @brief Free a taskqueue | ||
| 66 | * | ||
| 67 | * @param tq A taskqueue to be freed. May be null | ||
| 68 | */ | ||
| 69 | void taskqueue_free(void *tq); | ||
| 70 | |||
| 71 | /** | ||
| 72 | * @brief Push a task onto a taskqueue | ||
| 73 | * | ||
| 74 | * @param tq The taskqueue to be modified. Must be non-null | ||
| 75 | * @param tsk The task to push. Must be non-null | ||
| 76 | * @retval (int)[-1, 0] Returns 0 on success, sets errno and returns -1 on error | ||
| 77 | */ | ||
| 78 | int taskqueue_push(taskqueue *tq, task *tsk); | ||
| 79 | |||
| 80 | /** | ||
| 81 | * @brief Pop a task from a taskqueue | ||
| 82 | * | ||
| 83 | * @param tq A taskqueue to grab a task from. Must be non-null | ||
| 84 | * @retval (task*)[NULL, task*] Returns a task on success, sets errno and returns `null` on error | ||
| 85 | */ | ||
| 86 | task * taskqueue_pop(taskqueue *tq); | ||
| 87 | |||
| 88 | /** | ||
| 89 | * @brief Append a task to the front of a taskqueue | ||
| 90 | * | ||
| 91 | * @param tq The taskqueue to be modified. Must be non-null | ||
| 92 | * @param tsk The task to be appended. Must be non-null | ||
| 93 | * @retval (int)[-1, 0] Returns 0 on success, sets errno and returns -1 on error | ||
| 94 | */ | ||
| 95 | int taskqueue_pushfront(taskqueue *tq, task *tsk); | ||
| 96 | |||
| 97 | /** | ||
| 98 | * @brief Pop a task from the back (most recently pushed task) of a taskqueue | ||
| 99 | * | ||
| 100 | * @param tq A taskqueue to pop from. Must be non-null | ||
| 101 | * @retval (task*)[NULL, task*] Returns a task on success, sets errno and returns `null` on error | ||
| 102 | */ | ||
| 103 | task * taskqueue_popback(taskqueue *tq); | ||
| 104 | |||
| 105 | |||
| 106 | |||
| 107 | /** | ||
| 108 | * @brief Create a concurrent taskqueue with `size` allocated threads | ||
| 109 | * | ||
| 110 | * @param size Number of threads in the threadpool. Must be greater than zero | ||
| 111 | * @retval (ctqueue*)[NULL, ctqueue*] Returns a new ctqueue, sets errno and returns `null` on error | ||
| 112 | */ | ||
| 113 | ctqueue * ctqueue_init(int size); | ||
| 114 | |||
| 115 | /** | ||
| 116 | * @brief Cancel all tasks being processed in a currently running concurrent taskqueue | ||
| 117 | * | ||
| 118 | * @param ctq The concurrent taskqueue to be canceled. Must be non-null | ||
| 119 | * @retval (int)[-1, 0] Returns 0 on success, sets errno and returns -1 on error | ||
| 120 | */ | ||
| 121 | int ctqueue_cancel(ctqueue *ctq); | ||
| 122 | |||
| 123 | /** | ||
| 124 | * @brief Free a concurrent taskqueue | ||
| 125 | * @attention This cancels all currently running threads via `ctqueue_cancel` | ||
| 126 | * | ||
| 127 | * @param ctq The concurrent taskqueue to free. May be null | ||
| 128 | */ | ||
| 129 | void ctqueue_free(void *ctq); | ||
| 130 | |||
| 131 | /** | ||
| 132 | * @brief Push a task onto a concurrent taskqueue | ||
| 133 | * @attention May block for an indefinite amount of time to push the task | ||
| 134 | * | ||
| 135 | * @param ctq The concurrent taskqueue to modify. Must be non-null | ||
| 136 | * @param tsk The task to push. Must be non-null | ||
| 137 | * @retval (int) Returns `thrd_success` on success, returns `thrd_error` or `thrd_nomem` on error | ||
| 138 | */ | ||
| 139 | int ctqueue_waitpush(ctqueue *ctq, task *tsk); | ||
| 140 | |||
| 141 | |||
| 142 | /** | ||
| 143 | * @brief Pop a task from the concurrent taskqueue | ||
| 144 | * @attention May block for an indefinite amount of time to pop the task | ||
| 145 | * | ||
| 146 | * @param ctq The concurrent taskqueue to pop from. Must be non-null | ||
| 147 | * @retval (task*)[NULL, task*] Returns a task on success, sets errno and returns `null` on error | ||
| 148 | */ | ||
| 149 | task * ctqueue_waitpop(ctqueue *ctq); | ||
| 150 | |||
| 151 | /** | ||
| 152 | * @brief Start the threads allocated to a concurrent taskqueue | ||
| 153 | * @attention Threads will not consume pushed tasks until this function is ran | ||
| 154 | * | ||
| 155 | * @param ctq A concurrent taskqueue to start. Must be non-null | ||
| 156 | * @retval (int)[-1, 0] Returns 0 on success, sets errno and returns -1 on error | ||
| 157 | */ | ||
| 158 | int ctqueue_start(ctqueue *ctq); | ||
| 159 | |||
| 160 | #endif \ No newline at end of file | ||
