From e8e0f1ea6f17641075679dcd8db11533f68e8884 Mon Sep 17 00:00:00 2001 From: overflowerror Date: Thu, 8 Feb 2018 18:26:14 +0100 Subject: [PATCH] program structure (init, loop, ...) event handling, timer, error handling --- Makefile.am | 8 ++ configure.ac | 11 ++ src/example.c | 41 ++++++ src/seaboot.c | 366 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/seaboot.h | 95 +++++++++++++ 5 files changed, 521 insertions(+) create mode 100644 Makefile.am create mode 100644 configure.ac create mode 100644 src/example.c create mode 100644 src/seaboot.c create mode 100644 src/seaboot.h diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..5148702 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,8 @@ +noinst_LIBRARIES = bin/libseaboot.a +bin_libseaboot_a_SOURCES = src/seaboot.c + + +bin_PROGRAMS = bin/example +bin_example_SOURCES = src/example.c +bin_example_LDADD = bin/libseaboot.a +bin_example_LDFLAGS = -lrt diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..5a0f941 --- /dev/null +++ b/configure.ac @@ -0,0 +1,11 @@ +AC_INIT([seaboot], [0.1]) +AM_INIT_AUTOMAKE([-Wall -Werror foreign subdir-objects]) +AC_PROG_CC +AM_PROG_AR +AC_PROG_RANLIB +#LT_INIT +AC_CONFIG_HEADERS([config.h]) +AC_CONFIG_FILES([ + Makefile +]) +AC_OUTPUT diff --git a/src/example.c b/src/example.c new file mode 100644 index 0000000..39dfe40 --- /dev/null +++ b/src/example.c @@ -0,0 +1,41 @@ +#include +#include "seaboot.h" + +#include +#include + +BOOT(init); + +void eventHandler(event_t event) { + fprintf(stderr, "Got event %d.\n", event); +} +void shutdownHandler(event_t event) { + fprintf(stderr, "Shuting down.\n"); +} + +void intervalHandler() { + static int count = 0; + count++; + fprintf(stderr, "interval count %d.\n", count); + if (count > 4) { + fprintf(stderr, "Provoking error.\n"); + boot.enableSignal(SHUTDOWN); // SHUTDOWN is not a signal + } +} + +void init() { + printf("Hello World!\n"); + + boot.mode = WAIT; + boot.debug = true; + + boot.addEventListener(SHUTDOWN, shutdownHandler); + boot.addEventListener(SIGUSR1, eventHandler); + boot.enableSignal(SIGUSR1); + + timer_t timer = boot.createSignalTimer(SIGUSR1); + boot.startTimer(timer, 5000); + + timer_t interval = boot.createThreadTimer(&intervalHandler); + boot.startInterval(interval, 2000); +} diff --git a/src/seaboot.c b/src/seaboot.c new file mode 100644 index 0000000..721a50e --- /dev/null +++ b/src/seaboot.c @@ -0,0 +1,366 @@ +#include +#include "seaboot.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static const char* getEventName(event_t event) { + if (IS_SIGNAL(event)) + return strsignal(event); + switch(event) { + case SHUTDOWN: return "Shutdown"; break; + case LIBERROR: return "Lib-Error"; break; + default: return "Unknown"; break; + } +} + +static bool addEventListener(event_t, eventListener_t); +static bool removeEventListener(event_t, eventListener_t); +static bool enableSignal(event_t); +static bool disableSignal(event_t); + +static nstime_t getRealTime(void); +static nstime_t getRelativeTime(void); +static nstime_t getProcessTime(void); +static nstime_t getThreadTime(void); +static nstime_t timer(void (*)(void)); + +static timer_t createSignalTimer(event_t); +static timer_t createThreadTimer(void (*)(void)); +static void startTimer(timer_t, unsigned long); +static void startInterval(timer_t, unsigned long); +static void stopTimer(timer_t); +static void deleteTimer(timer_t); + +static void eventHandler(event_t); + +struct boot boot = { + .addEventListener = addEventListener, + .removeEventListener = removeEventListener, + .enableSignal = enableSignal, + .disableSignal = disableSignal, + + .getRealTime = getRealTime, + .getRelativeTime = getRelativeTime, + .getProcessTime = getProcessTime, + .getThreadTime = getThreadTime, + .timer = timer, + + .createSignalTimer = createSignalTimer, + .createThreadTimer = createThreadTimer, + .startTimer = startTimer, + .startInterval = startInterval, + .stopTimer = stopTimer, + .deleteTimer = deleteTimer, + + .mode = STANDARD, + .debug = false +}; + +struct listener { + int number; + eventListener_t* listeners; + bool override; + + bool isSignalHandler; + struct sigaction defaultHandler; + struct sigaction handler; +}; + +struct listener events[NUMBER_OF_EVENTS]; + +static void debug(const char* format, ...) { + if (!boot.debug) + return; + + va_list arguments; + + int done; + + va_start(arguments, format); + + fprintf(stderr, "[seaboot] "); + done = vfprintf(stderr, format, arguments); + + va_end(arguments); +} + +bool addEventListener(event_t event, eventListener_t listener) { + if (event >= NUMBER_OF_EVENTS) { + boot.error = "No such event (addEventListener)."; + eventHandler(LIBERROR); + return false; + } + if (events[event].override) { + events[event].override = false; + events[event].number = 0; + } + events[event].listeners = realloc(events[event].listeners, (events[event].number + 1) * sizeof(eventListener_t)); // TODO replace with own function + events[event].listeners[events[event].number] = listener; + debug("New event listener for event %d (%s) on position %d.\n", event, getEventName(event), events[event].number); + events[event].number++; +} + +bool removeEventListener(event_t event, eventListener_t listener) { + if (event >= NUMBER_OF_EVENTS) { + boot.error = "No such event (removeEventListener)."; + eventHandler(LIBERROR); + return false; + } + // TODO +} + +bool enableSignal(event_t signal) { + if (!IS_SIGNAL(signal)) { + boot.error = "Not a signal (enableSignal)."; + eventHandler(LIBERROR); + return false; + } + if (sigaction(signal, &(events[signal].handler), NULL) < 0) { + boot.error = "Setting signal failed (sigaction)."; + eventHandler(LIBERROR); + return false; + } + debug("Signal %d (%s) is enabled.\n", signal, getEventName(signal)); + events[signal].isSignalHandler = true; + return true; +} + +bool disableSignal(event_t signal) { + if (!IS_SIGNAL(signal)) { + boot.error = "Not a signal (enableSignal)."; + eventHandler(LIBERROR); + return false; + } + if (sigaction(signal, &(events[signal].defaultHandler), NULL) < 0) { + boot.error = "Setting signal failed (sigaction)."; + eventHandler(LIBERROR); + return false; + } + debug("Signal %d (%s) is disabled.\n", signal, getEventName(signal)); + events[signal].isSignalHandler = false; + return true; +} + +void eventHandler(event_t event) { + debug("Event handler for event %d (%s).\n", event, getEventName(event)); + debug("Event has %d registered handler(s).\n", events[event].number); + for (int i = 0; i < events[event].number; i++) { + if (events[event].listeners[i] == NULL) + continue; + debug("Found handler %d.\n", i); + events[event].listeners[i](event); + } +} + +nstime_t getRealTime() { + struct timespec time; + clock_gettime(CLOCK_REALTIME, &time); + return time.tv_sec * 1000000000 + time.tv_nsec; +} + +nstime_t getRelativeTime() { + struct timespec time; + clock_gettime(CLOCK_MONOTONIC, &time); + return time.tv_sec * 1000000000 + time.tv_nsec; +} + +nstime_t getProcessTime() { + struct timespec time; + clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &time); + return time.tv_sec * 1000000000 + time.tv_nsec; +} + +nstime_t getThreadTime() { + struct timespec time; + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &time); + return time.tv_sec * 1000000000 + time.tv_nsec; +} + +nstime_t timer(void (*function)(void)) { + nstime_t start = getRelativeTime(); + function(); + nstime_t end = getRelativeTime(); + return end - start; +} + +void timerHandler(union sigval target) { + debug("Timer handler: Starting target.\n"); + ((void (*)(void))(target.sival_ptr))(); +} + +timer_t createSignalTimer(event_t signal) { + debug("Creating signal timer (%d)\n", signal); + + struct sigevent sevp; + sevp.sigev_notify = SIGEV_SIGNAL; + sevp.sigev_signo = signal; + + timer_t timer; + + if (timer_create(CLOCK_BOOTTIME, &sevp, &timer) < 0) { + boot.error = strerror(errno); + eventHandler(LIBERROR); + + timer = NULL; + } + + return timer; +} + +timer_t createThreadTimer(void (*handler)(void)) { + debug("Creating thread timer\n"); + + struct sigevent sevp; + sevp.sigev_notify = SIGEV_THREAD; + sevp.sigev_notify_function = timerHandler; + sevp.sigev_value.sival_ptr = handler; + + timer_t timer; + + if (timer_create(CLOCK_BOOTTIME, &sevp, &timer) < 0) { + boot.error = strerror(errno); + eventHandler(LIBERROR); + timer = NULL; + } + return timer; +} + +void startTimer(timer_t timer, unsigned long ms) { + debug("Starting timer %x (%d ms)\n", timer, ms); + + struct itimerspec time, old; + + time.it_value.tv_sec = ms / 1000; + time.it_value.tv_nsec = ((ms % 1000) * 1000000); + time.it_interval.tv_sec = 0; + time.it_interval.tv_nsec = 0; + + if (timer_settime(timer, 0, &time, &old) < 0) { + boot.error = strerror(errno); + eventHandler(LIBERROR); + } +} + +void startInterval(timer_t timer, unsigned long ms) { + debug("Starting timer (interval) %x (%d ms)\n", timer, ms); + + struct itimerspec time, old; + + time.it_value.tv_sec = ms / 1000; + time.it_value.tv_nsec = ((ms % 1000) * 1000000); + time.it_interval.tv_sec = ms / 1000; + time.it_interval.tv_nsec = ((ms % 1000) * 1000000); + + if (timer_settime(timer, 0, &time, &old) < 0) { + boot.error = strerror(errno); + eventHandler(LIBERROR); + } +} + +void stopTimer(timer_t timer) { + debug("Stoping timer %x\n", timer); + + struct itimerspec time, old; + + time.it_value.tv_sec = 0; + time.it_value.tv_nsec = 0; + time.it_interval.tv_sec = 0; + time.it_interval.tv_nsec = 0; + + if (timer_settime(timer, 0, &time, &old) < 0) { + boot.error = strerror(errno); + eventHandler(LIBERROR); + } +} + +void deleteTimer(timer_t timer) { + debug("Deleting timer %x\n", timer); + if (timer_delete(timer) < 0) { + boot.error = strerror(errno); + eventHandler(LIBERROR); + } +} + +static void signalHandler(int signal) { + debug("Got signal %d (%s). Involking event handler.\n", signal, getEventName(signal)); + eventHandler(signal); +} + +static void exitHandler(void) { + eventHandler(SHUTDOWN); +} + +static void errorHandler(event_t event) { + fprintf(stderr, "\n[seaboot] Error: %s\n", boot.error); + fprintf(stderr, "[seaboot] No error handler given.\n"); + fprintf(stderr, "[seaboot] Shuting down.\n"); + exit(EXIT_ERROR); +} + +int main(char** argv, int argc) { + for(int i = 0; i < NUMBER_OF_EVENTS; i++) { + debug("Setup %s %d (%s)...\n", IS_SIGNAL(i) ? "signal" : "event", i, getEventName(i)); + events[i].number = 0; + events[i].listeners = malloc(1 * sizeof(eventListener_t)); // TODO replace by own function + events[i].listeners[0] = NULL; + events[i].override = false; + events[i].isSignalHandler = false; + if (IS_SIGNAL(i)) { + sigaction(i, NULL, &events[i].defaultHandler); + + boot.error = "Setting exit handler failed (atexit)."; + eventHandler(LIBERROR); + events[i].handler.sa_handler = &signalHandler; + sigemptyset(&(events[i].handler.sa_mask)); + + } + } + + debug("Setup default error handler.\n"); + addEventListener(LIBERROR, errorHandler); + events[LIBERROR].override = true; + + debug("Setup exit handler.\n"); + if (atexit(exitHandler) != 0) { + boot.error = "Setting exit handler failed (atexit)."; + eventHandler(LIBERROR); + } + + + // start init + if (boot.init == NULL) { + boot.error = "No init handler given (main)."; + eventHandler(LIBERROR); + } else { + boot.init(); + } + + if (boot.mode == LOOP) { + if (boot.loop == NULL) { + boot.error = "No loop handler given (loop)."; + eventHandler(LIBERROR); + } else { + nstime_t last = getRelativeTime(); + nstime_t current; + while(true) { + current = getRelativeTime(); + boot.loop(current - last); + last = current; + } + } + } else if (boot.mode == WAIT) { + while(true) + sleep(60); + } + + return EXIT_SUCCESS; +} diff --git a/src/seaboot.h b/src/seaboot.h new file mode 100644 index 0000000..6e21672 --- /dev/null +++ b/src/seaboot.h @@ -0,0 +1,95 @@ +#ifndef SEABOOT_H +#define SEABOOT_H + +#include +#include + +#define EXIT_SUCCESS 0 +#define EXIT_ERROR 3 + +#define BOOT(i) void i(void); __attribute__((constructor)) static void seaboot_init() { boot.init = &i; } +#define LOOP(i) void i(void); __attribute__((constructor)) static void seaboot_loop() { boot.loop = &i; } + +#define IS_SIGNAL(v) (v >= 1 && v <= 31) + +typedef unsigned long long int nstime_t; + +typedef enum bootmode { + STANDARD, + LOOP, + WAIT +} bootmode_t; + +#define NUMBER_OF_EVENTS 33 +typedef enum event { + SIGHUP = 1, + SIGINT = 2, + SIGQUIT = 3, + SIGILL = 4, + SIGTRAP = 5, + SIGABRT = 6, + SIGIOT = 6, + SIGBUS = 7, + SIGFPE = 8, + SIGKILL = 9, + SIGUSR1 = 10, + SIGSEGV = 11, + SIGUSR2 = 12, + SIGPIPE = 13, + SIGALRM = 14, + SIGTERM = 15, + SIGSTKFLT = 16, + SIGCHLD = 17, + SIGCLD = 17, + SIGCONT = 18, + SIGSTOP = 19, + SIGTSTP = 20, + SIGTTIN = 21, + SIGTTOU = 22, + SIGURG = 23, + SIGXCPU = 24, + SIGXFSZ = 25, + SIGVTALRM = 26, + SIGPROF = 27, + SIGWINCH = 28, + SIGIO = 29, + SIGPOLL = 29, + SIGPWR = 30, + SIGSYS = 31, + + SHUTDOWN = 0, + LIBERROR = 32 +} event_t; + +typedef void (*eventListener_t)(event_t); +typedef void (*init_t)(void); +typedef void (*loop_t)(nstime_t); + +extern struct boot { + init_t init; + loop_t loop; + bool (*addEventListener)(event_t, eventListener_t); + bool (*removeEventListener)(event_t, eventListener_t); + bool (*enableSignal)(event_t); + bool (*disableSignal)(event_t); + + nstime_t (*getRealTime)(void); + nstime_t (*getRelativeTime)(void); + nstime_t (*getProcessTime)(void); + nstime_t (*getThreadTime)(void); + nstime_t (*timer)(void (*)(void)); + + timer_t (*createSignalTimer)(event_t); + timer_t (*createThreadTimer)(void (*)(void)); + void (*startTimer)(timer_t, unsigned long); + void (*startInterval)(timer_t, unsigned long); + void (*stopTimer)(timer_t); + void (*deleteTimer)(timer_t); + + bootmode_t mode; + bool debug; + + const char* error; +} boot; + +#endif