diff --git a/Makefile b/Makefile index 39cfd01..49f94d8 100644 --- a/Makefile +++ b/Makefile @@ -2,26 +2,20 @@ # See LICENSE file for copyright and license details. # Note: If you're confused by the makefile, I do emplore you to read the info-page: $ info make .POSIX: - NAME=sci DESCRIPTION=$(NAME) is a simple contiuous integration system. VERSION = 0.1.0 - CC = gcc OUTDIR := out/ OBJDIR := out/obj BINDIR := out/bin -# defs CFLAGS += -DSCI_VERSION="\"$(VERSION)\"" CFLAGS += -DSCI_NAME="\"$(NAME)\"" CFLAGS += -DSCI_DESCRIPTION="\"$(DESCRIPTION)\"" CFLAGS += -D_POSIX_C_SOURCE=2 CFLAGS += -D_GNU_SOURCE -# compiler flags CFLAGS += -Wall -Werror -std=c23 -g -# includes CFLAGS += -Iinclude -# libraries CFLAGS += -lpthread .PHONY: all clean @@ -71,7 +65,3 @@ $(BINDIR): $(OUTDIR) # chmod 644 $(DESTDIR)$(MANPREFIX)/man1/st.1 # tic -sx st.info # @echo Please see the README file regarding the terminfo entry of st. -# -# uninstall: -# rm -f $(DESTDIR)$(PREFIX)/bin/st -# rm -f $(DESTDIR)$(MANPREFIX)/man1/st.1 diff --git a/README.md b/README.md index e5344d8..8ea179a 100644 --- a/README.md +++ b/README.md @@ -84,9 +84,20 @@ If you want `compile_commands.json` files, you should use [bear](https://github. - [x] Third things third, implement a thing that simultaneously watches two different files (multithreading). it should be cancellable with ctrl+c, but it should just contiuously print event notifications. - [x] Fourth things fourth, implement a prototype that reads a space-separated file and populates a struct. - - [ ] Fifth things fifth, implement a prototype that spawns a new thread that executes a shell command. - - [ ] Voila! You're there! - - [ ] Second iteration! Now Reimplement it and do it better! + - [x] Fifth things fifth, implement a prototype that spawns a new thread that executes a shell command. + - [ ] Sixth things sixth, daemonize it! + - [ ] Seventh things seventh, package the sucker (arch, debian, alpine, docker) + - [ ] Eight things eight, try it out! - maybe even write the python webhook extension. + - [ ] Ninth things ninth, fix bugs, see below + - [ ] Tenth things tenth, write manpages + - [ ] Eleventh things last, release! + +#### Bugs + - [ ] command output is being inherited. It should be piped into some random log-file + - [ ] pretty sure that `ctrl+c` / SIGINT is not graceful yet. + - [ ] missing license + - [ ] I am deliberately not using `Restart=on-failure` in the `scid.service` file because we are using `Type=exec` + and not `Type=notify` (yet) - which would require a `sd_notify` call of `READY=1` (see `man systemd.service`) ### Note Regarding `inotify` usage From the manpage: diff --git a/include/cli.h b/include/cli.h index a8cc973..09ac137 100644 --- a/include/cli.h +++ b/include/cli.h @@ -1,7 +1,7 @@ #ifndef SCI_CLI_H #define SCI_CLI_H -#include #include "optional.h" +#include typedef struct { optional_str config_file; diff --git a/include/log.h b/include/log.h index f46554c..0b0d3d3 100644 --- a/include/log.h +++ b/include/log.h @@ -1,9 +1,7 @@ #ifndef SCI_LOG_H #define SCI_LOG_H -#include #include -// TODO: Thread safety! enum { LOG_TRACE = 4, LOG_INFO = 3, diff --git a/include/pipeline.h b/include/pipeline.h index 42c01d7..ef9b83d 100644 --- a/include/pipeline.h +++ b/include/pipeline.h @@ -2,7 +2,6 @@ #define SCI_PIPELINE_H #include "optional.h" #include -#include typedef struct { char* name; diff --git a/include/threadpool.h b/include/threadpool.h index 6a2879e..86ca351 100644 --- a/include/threadpool.h +++ b/include/threadpool.h @@ -1,9 +1,8 @@ #ifndef SCI_THREADPOOL_H #define SCI_THREADPOOL_H -#include -#include +#include "optional.h" #include -// Very inspired by: https://nachtimwald.com/2019/04/12/thread-pool-in-c/ +#include // a work function typedef void (*thread_func)(void *arg); @@ -14,6 +13,7 @@ typedef struct threadpool_work { void* arg; struct threadpool_work* next; } threadpool_work; +typedef optional_type(threadpool_work*) optional_threadpool_work; // thread pool object typedef struct { diff --git a/scid.service b/scid.service new file mode 100644 index 0000000..e2177cd --- /dev/null +++ b/scid.service @@ -0,0 +1,10 @@ +[Unit] +Description=Simple Continuous Integration Service +AssertPathExists=/etc/sci/pipelines.conf + +[Service] +Type=exec +ExecStart=/usr/local/bin/sci + +[Install] +WantedBy=multi-user.target diff --git a/src/log.c b/src/log.c index af1df2f..8df59ef 100644 --- a/src/log.c +++ b/src/log.c @@ -1,8 +1,7 @@ #include "log.h" -#include #include -#include #include +#include pthread_mutex_t log_mutex = PTHREAD_MUTEX_INITIALIZER; log_settings g_log_settings; diff --git a/src/main.c b/src/main.c index 5a0ff1d..25744e6 100644 --- a/src/main.c +++ b/src/main.c @@ -4,14 +4,8 @@ #include "pipeline.h" #include "threadpool.h" #include "util.h" -#include -#include -#include -#include #include -#include #include -#include void executor(void* data) { const char* command = data; @@ -21,7 +15,8 @@ void executor(void* data) { threadpool* pool = NULL; void on_event(pipeline_event* const e) { - threadpool_add_work(pool, executor, (void*)e->command); + if(!threadpool_add_work(pool, executor, (void*)e->command)) + log_error("could not add work to the threadpool"); } void* listen_for_changes_thread(void* data) { @@ -74,7 +69,6 @@ int main(int argc, char** argv) { settings.use_colors = args.use_colors; settings.out_file = args.log_file.has_value ? fopen(args.log_file.value, "w+") : stdout; log_init(settings); - pool = threadpool_create(args.executors); if(args.help) { print_help(stdout, argv[0]); @@ -100,8 +94,14 @@ int main(int argc, char** argv) { exit(EXIT_FAILURE); } + pool = threadpool_create(args.executors); per_line(args.config_file.value, &config_interpret_line); + // BOOKMARK: You were reading :Man system.unit and :Man systemd.service as preperation on making a systemd unit file + // This will be needed for the .deb package, as well as the arch linux package. + // alpine linux is using OpenRC (cool), which complicates things a little bit, but shouldn't be too bad. The wiki is + // generally really well written. Otherwise, I am sure that both wiki.gentoo and wiki.archlinux have great pages too + // docker is super easy, just make a dockerfile - only concern is the trigger files. pipeline_loop(); threadpool_destroy(pool); } diff --git a/src/pipeline.c b/src/pipeline.c index 7b2656c..5338ba1 100644 --- a/src/pipeline.c +++ b/src/pipeline.c @@ -2,7 +2,6 @@ #include "pipeline.h" #include "threadlist.h" #include "util.h" -#include #include #include diff --git a/src/threadpool.c b/src/threadpool.c index 9fd14c0..f7cbb49 100644 --- a/src/threadpool.c +++ b/src/threadpool.c @@ -1,15 +1,20 @@ -#include "threadpool.h" #include "log.h" +#include "threadpool.h" #include -static threadpool_work* threadpool_work_create(thread_func func, void *arg) { - threadpool_work* result; - if (func == NULL) - return NULL; - result = malloc(sizeof(threadpool_work)); - result->func = func; - result->arg = arg; - result->next = NULL; +static optional_threadpool_work threadpool_work_create(thread_func func, void *arg) { + optional_threadpool_work result; + result.value = NULL; + result.has_value = false; + if(func == NULL) { + log_error("cant create threadpool work, function is null"); + return result; + } + result.value = malloc(sizeof(threadpool_work)); + result.value->func = func; + result.value->arg = arg; + result.value->next = NULL; + result.has_value = true; return result; } @@ -91,7 +96,6 @@ threadpool* threadpool_create(size_t num) { } return result; } -// TODO: add log statements void threadpool_destroy(threadpool* pool) { log_trace("destroying threadpool"); @@ -122,21 +126,24 @@ void threadpool_destroy(threadpool* pool) { bool threadpool_add_work(threadpool* pool, thread_func func, void *arg) { log_trace("adding work task to pool"); - threadpool_work* work; - if (pool == NULL) + if(pool == NULL) { + log_error("could not add work to threadpool, pool is null"); return false; + } - work = threadpool_work_create(func, arg); - if (work == NULL) + optional_threadpool_work work = threadpool_work_create(func, arg); + if(!work.has_value) { + log_error("could not add work to threadpool"); return false; + } pthread_mutex_lock(&(pool->work_mutex)); if (pool->work_first == NULL) { - pool->work_first = work; + pool->work_first = work.value; pool->work_last = pool->work_first; } else { - pool->work_last->next = work; - pool->work_last = work; + pool->work_last->next = work.value; + pool->work_last = work.value; } pthread_cond_broadcast(&(pool->work_cond)); pthread_mutex_unlock(&(pool->work_mutex)); @@ -145,8 +152,10 @@ bool threadpool_add_work(threadpool* pool, thread_func func, void *arg) { } void threadpool_wait(threadpool* pool) { - if (pool == NULL) + if(pool == NULL) { + log_error("pool object is null"); return; + } pthread_mutex_lock(&(pool->work_mutex)); while (1) { if (pool->work_first != NULL || (!pool->stop && pool->working_count != 0) || (pool->stop && pool->thread_count != 0))