summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/Makefile19
-rw-r--r--src/encryption.c544
-rw-r--r--src/encryption.h185
-rw-r--r--src/shared.c177
-rw-r--r--src/shared.h191
-rw-r--r--src/threadpool.c148
-rw-r--r--src/threadpool.h160
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
9CFLAGS = -std=c2x -Wall -Wextra -Wpedantic -pedantic-errors -fanalyzer -Wanalyzer-too-complex -ggdb -g3 -O0 9CFLAGS = -std=c2x -Wall -Wextra -Wpedantic -pedantic-errors -fanalyzer -Wanalyzer-too-complex -ggdb -g3 -O0
10DEPFLAGS = -MT $@ -MMD -MP -MF $*.d
11 10
12CFLAGS += $$(pkg-config --cflags libsodium) 11CFLAGS += $$(pkg-config --cflags libsodium)
13LDLIBS += $$(pkg-config --libs-only-l libsodium) 12LDLIBS += $$(pkg-config --libs-only-l libsodium)
14LDFLAGS += $$(pkg-config --libs-only-L libsodium) 13LDFLAGS += $$(pkg-config --libs-only-L libsodium)
15 14
16SOURCES := $(wildcard *.c) 15SOURCES := $(wildcard *.c)
17OBJECTS := $(patsubst %.c,%.o,$(SOURCES))
18DEPS := $(patsubst %.c,%.d,$(SOURCES))
19
20COMPILE.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
26all: main 21all: main
27main: $(OBJECTS) 22main:
28 23 echo "Need to update makefile to work with unity build"
29%.o: %.c %.d
30 $(COMPILE.c) $<
31
32$(DEPS):
33include $(wildcard $(DEPS))
34# Adopted from https://make.mad-scientist.net/papers/advanced-auto-dependency-generation/
35 24
36c clean: 25c clean:
37 @-rm -rv main $(OBJECTS) $(DEPS) $(wildcard *.test*) $(wildcard *.enc) 26 @-rm -rv main $(wildcard *.test*) $(wildcard *.enc)
38 27
39val: 28val:
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
117typedef 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 */
44static void naclfaildefault(void *none) { 124static 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 */
68static int checksodiumcb(vxgg_naclfailcb const callback, void *data, unsigned char set) { 148static 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 */
104static void checksodium(void) { 184static 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 */
121int maketmp(const char * const dest) { 207int 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 */
126int linkto(const char * const target, int tgfd) { 219int 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 */
257int 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 */
309int 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 */
153int encryptviatmp(const char * const target, const char * const output, const unsigned char key[crypto_secretstream_xchacha20poly1305_KEYBYTES]) { 375int 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 */
225int decryptto(const char * const target, const char * const output, const unsigned char key[crypto_secretstream_xchacha20poly1305_KEYBYTES]) { 455int 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
288int 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
332int 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
390int genpassword(char **str, unsigned int words) { 525int 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 */
419void* xsodium_malloc(size_t size) { 562void* 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
464struct __FLC_FKP { 619int __cscan_worker(void *data) {
465 char *target; 620 if(!data) return -1;
466 char *output;
467 const unsigned char *key;
468};
469
470struct __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
483void 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
494static 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}
500static 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
507enum VXGG_FLC { 622 return 0;
508 VXGG_FLC__INVALID,
509 VXGG_FLC__ENCRYPT,
510 VXGG_FLC__DECRYPT,
511 VXGG_FLC__TOOBIG
512};
513
514struct __ucl_namelist_vals {
515 struct dirent **namelist;
516 int entries;
517};
518
519static 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 625ctqueue * cryptscan() {
529static 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);
532static int decryption_filter(const struct dirent *de) { 630 if(!start) ERRRET(errno, NULL);
631 ctqueue_waitpush(working, start);
533 632
534}
535 633
536ctqueue * 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/*
594int main(void) { 638int 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
31typedef 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 */
39void 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
47const 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 */
114int 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 */
124int 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 */
134int 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 */
144int 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 */
154int 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 */
163int 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 */
172int 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 */
182void* 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
50typedef int (*gcallback)(void*); //!< Generic callback signature
51typedef void (*fcallback)(void*); //!< free()-like callback signature
52
53/**
54 * @brief A locally defined structure designed for easier function cleanup
55 *
56 */
57typedef 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 */
20int rwbuf(char **str, unsigned long int initsize, int fd) { 81int 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
84int wwbuf(int fd, const unsigned char *buf, int len) { 154int 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 */
106char * vxdirname(const char * const path) { 181char * 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 */
204int cleanup_init(cleanup *loc, fcallback callbacks[], void *arguments[], int size) { 288int 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 */
215int cleanup_register(cleanup *loc, fcallback cb, void *arg) { 307int 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 */
227int cleanup_cndregister(cleanup *loc, fcallback cb, void *arg, unsigned char flag) { 327int 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 */
232int cleanup_clear(cleanup *loc) { 339int 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 */
238int cleanup_fire(cleanup *loc) { 351int 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 */
255int cleanup_cndfire(cleanup *loc, unsigned char flag) { 374int 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) \
385cleanup __CLEANUP; \
386fcallback __CLEANUP_FUNCS[(size)]; \
387void *__CLEANUP_ARGS[(size)]; \
388unsigned char __FLAG = 0; \
389cleanup_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
17typedef int (*gcallback)(void*); //!< Generic callback signature
18typedef 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 */
68int 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 */
78int 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 */
88char * vxdirname(const char * const path);
89
90
91
92/**
93 * @brief A locally defined structure designed for easier function cleanup
94 *
95 */
96typedef 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 */
112int 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 */
122int 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 */
133int 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 */
142int 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 */
150int 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 */
159int 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) \
166cleanup __CLEANUP; \
167fcallback __CLEANUP_FUNCS[(size)]; \
168void *__CLEANUP_ARGS[(size)]; \
169unsigned char __FLAG = 0; \
170cleanup_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 */
23typedef struct task { 26typedef 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 */
33typedef struct tqnode { 36typedef 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 */
43typedef struct taskqueue { 46typedef 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 */
53typedef struct ctqueue { 56typedef 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 */
64task * task_init(gcallback callback, fcallback freecb, void *data) { 74task * 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 */
78void task_free(void *tsk) { 93void 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 */
90int task_fire(task *tsk) { 111int 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 */
95int task_fired(task *tsk) { 122int 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 */
129taskqueue * taskqueue_init(void) { 160taskqueue * 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 */
141void taskqueue_free(void *tq) { 177void 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 */
170int taskqueue_push(taskqueue *tq, task *tsk) { 213int 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 */
187task * taskqueue_pop(taskqueue *tq) { 236task * 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 */
207int taskqueue_pushfront(taskqueue *tq, task *tsk) { 263int 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 */
224task * taskqueue_popback(taskqueue *tq) { 286task * 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 */
276ctqueue * ctqueue_init(int nthreads) { 344ctqueue * 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 */
321int ctqueue_cancel(ctqueue *ctq) { 395int 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 */
332void ctqueue_free(void *ctq) { 412void 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 */
354int ctqueue_waitpush(ctqueue *ctq, task *tsk) { 442int 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 */
367task * ctqueue_waitpop(ctqueue *ctq) { 462task * 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
387static int __CTQ_CONSUMER(void *ctq) { 482static 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 */
405int ctqueue_start(ctqueue *ctq) { 507int 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
18typedef struct task task;
19typedef struct taskqueue taskqueue;
20typedef 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 */
30task * 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 */
37void 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 */
45int 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 */
53int 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 */
62taskqueue * taskqueue_init(void);
63
64/**
65 * @brief Free a taskqueue
66 *
67 * @param tq A taskqueue to be freed. May be null
68 */
69void 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 */
78int 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 */
86task * 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 */
95int 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 */
103task * 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 */
113ctqueue * 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 */
121int 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 */
129void 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 */
139int 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 */
149task * 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 */
158int ctqueue_start(ctqueue *ctq);
159
160#endif \ No newline at end of file