From b19c837e9d80907856ae00db985e902c50092a55 Mon Sep 17 00:00:00 2001 From: Gustav Behm Date: Fri, 29 Mar 2024 04:22:55 +0100 Subject: [PATCH] Add basic C infrastructure --- c-impl/.gitignore | 1 + c-impl/Makefile | 25 +++++ c-impl/action.c | 104 ++++++++++++++++++ c-impl/r.h | 263 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 393 insertions(+) create mode 100644 c-impl/.gitignore create mode 100644 c-impl/Makefile create mode 100644 c-impl/action.c create mode 100644 c-impl/r.h diff --git a/c-impl/.gitignore b/c-impl/.gitignore new file mode 100644 index 0000000..b883f1f --- /dev/null +++ b/c-impl/.gitignore @@ -0,0 +1 @@ +*.exe diff --git a/c-impl/Makefile b/c-impl/Makefile new file mode 100644 index 0000000..a534f30 --- /dev/null +++ b/c-impl/Makefile @@ -0,0 +1,25 @@ +CC = gcc + +CFLAGS = -Wall -Werror -O1 +LDFLAGS = +LOG_LEVEL ?= WARN +EXTRA_CFLAGS ?= -DLOG_LEVEL=LOG_$(LOG_LEVEL) +EXTRA_LDFLAGS ?= + +export PREFIX ?= $(HOME)/.local + +EXE = action.exe + +.PHONY: install +install: build + install --strip -D $(EXE) $(PREFIX)/bin/$(EXE:%.exe=%) + +.PHONY: build +build: $(EXE) + +%.exe: %.c r.h + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -o $@ $< $(LDFLAGS) $(EXTRA_LDFLAGS) + +.PHONY: clean +clean: + rm -f *.exe diff --git a/c-impl/action.c b/c-impl/action.c new file mode 100644 index 0000000..49e0ea8 --- /dev/null +++ b/c-impl/action.c @@ -0,0 +1,104 @@ +#include +#include +#include + +#define LIBR_IMPLEMENTATION +#include "r.h" + +struct state { + int running; + + int sfd; +}; + +static int signalfd_init(struct state* st) +{ + sigset_t m; + sigemptyset(&m); + sigaddset(&m, SIGINT); + sigaddset(&m, SIGTERM); + + int fd = st->sfd = signalfd(-1, &m, 0); + CHECK(fd, "signalfd"); + + int r = sigprocmask(SIG_BLOCK, &m, NULL); + CHECK(r, "sigprocmask"); + + set_blocking(fd, 0); + + return fd; +} + +static int signalfd_fd(const struct state* st) +{ + return st->sfd; +} + +static void signalfd_deinit(struct state* st) +{ + int r = close(st->sfd); CHECK(r, "close"); + st->sfd = -1; +} + +static void signalfd_handle_event(struct state* st) +{ + while(1) { + struct signalfd_siginfo si; + + ssize_t s = read(st->sfd, &si, sizeof(si)); + if(s == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) { + break; + } + CHECK(s, "read"); + + if(s != sizeof(si)) { + failwith("unexpected partial read"); + } + + if(si.ssi_signo == SIGINT) { + debug("SIGINT"); + st->running = 0; + } else if(si.ssi_signo == SIGTERM) { + debug("SIGTERM"); + st->running = 0; + } else { + warning("unhandled signal: %u", si.ssi_signo); + } + } +} + +int main(int argc, char* argv[]) +{ + struct state st = { + .running = 1, + }; + + signalfd_init(&st); + + struct pollfd fds[] = { + { .fd = signalfd_fd(&st), .events = POLLIN }, + }; + + while(st.running) { + int r = poll(fds, LENGTH(fds), -1); + CHECK(r, "poll"); + + if(fds[0].revents & POLLIN) { + signalfd_handle_event(&st); + fds[0].revents &= ~POLLIN; + } + + for(size_t i = 0; i < LENGTH(fds); i++) { + if(fds[i].revents != 0) { + failwith("unhandled poll events: " + "fds[%zu] = { .fd = %d, .revents = %hd }", + i, fds[i].fd, fds[i].revents); + } + } + } + + debug("graceful shutdown"); + signalfd_deinit(&st); + + return 0; +} diff --git a/c-impl/r.h b/c-impl/r.h new file mode 100644 index 0000000..6785f3a --- /dev/null +++ b/c-impl/r.h @@ -0,0 +1,263 @@ +// libr 0.5.0 (ec3c888754c9966a470ff5a8d1baf1cfaa3045d2) (https://github.com/rootmos/libr.git) (2024-03-29T04:14:36+01:00) +// modules: fail logging now util nonblock + +#ifndef LIBR_HEADER +#define LIBR_HEADER + +#define LIBR(x) x +#define PRIVATE __attribute__((visibility("hidden"))) +#define PUBLIC __attribute__((visibility("default"))) +#define API PRIVATE + + +// libr: fail.h + +#define CHECK(res, format, ...) CHECK_NOT(res, -1, format, ##__VA_ARGS__) + +#define CHECK_NOT(res, err, format, ...) \ + CHECK_IF(res == err, format, ##__VA_ARGS__) + +#define CHECK_IF(cond, format, ...) do { \ + if(cond) { \ + LIBR(failwith0)(__extension__ __FUNCTION__, __extension__ __FILE__, \ + __extension__ __LINE__, 1, \ + format "\n", ##__VA_ARGS__); \ + } \ +} while(0) + +#define CHECK_MALLOC(x) CHECK_NOT(x, NULL, "memory allocation failed") +#define CHECK_MMAP(x) CHECK_NOT(x, MAP_FAILED, "memory mapping failed") + +#define failwith(format, ...) \ + LIBR(failwith0)(__extension__ __FUNCTION__, __extension__ __FILE__, \ + __extension__ __LINE__, 0, format "\n", ##__VA_ARGS__) + +#define not_implemented() LIBR(failwith0)("not implemented") + +void LIBR(failwith0)( + const char* const caller, + const char* const file, + const unsigned int line, + const int include_errno, + const char* const fmt, ...) +__attribute__ ((noreturn, format (printf, 5, 6))); + +// libr: logging.h + +#include + +#define LOG_QUIET 0 +#define LOG_ERROR 1 +#define LOG_WARNING 2 +#define LOG_INFO 3 +#define LOG_DEBUG 4 +#define LOG_TRACE 5 + +#ifndef LOG_LEVEL +#define LOG_LEVEL LOG_INFO +#endif + +extern int LIBR(logger_fd); + +#define __r_log(level, format, ...) do { \ + LIBR(logger)(level, __extension__ __FUNCTION__, __extension__ __FILE__, \ + __extension__ __LINE__, format "\n", ##__VA_ARGS__); \ +} while(0) + +#ifdef __cplusplus +void LIBR(dummy)(...); +#else +void LIBR(dummy)(); +#endif + +#if LOG_LEVEL >= LOG_ERROR +#define error(format, ...) __r_log(LOG_ERROR, format, ##__VA_ARGS__) +#else +#define error(format, ...) do { if(0) LIBR(dummy)(__VA_ARGS__); } while(0) +#endif + +#if LOG_LEVEL >= LOG_WARNING +#define warning(format, ...) __r_log(LOG_WARNING, format, ##__VA_ARGS__) +#else +#define warning(format, ...) do { if(0) LIBR(dummy)(__VA_ARGS__); } while(0) +#endif + +#if LOG_LEVEL >= LOG_INFO +#define info(format, ...) __r_log(LOG_INFO, format, ##__VA_ARGS__) +#else +#define info(format, ...) do { if(0) LIBR(dummy)(__VA_ARGS__); } while(0) +#endif + +#if LOG_LEVEL >= LOG_DEBUG +#define debug(format, ...) __r_log(LOG_DEBUG, format, ##__VA_ARGS__) +#else +#define debug(format, ...) do { if(0) LIBR(dummy)(__VA_ARGS__); } while(0) +#endif + +#if LOG_LEVEL >= LOG_TRACE +#define trace(format, ...) __r_log(LOG_TRACE, format, ##__VA_ARGS__) +#else +#define trace(format, ...) do { if(0) LIBR(dummy)(__VA_ARGS__); } while(0) +#endif + +void LIBR(logger)( + int level, + const char* const caller, + const char* const file, + const unsigned int line, + const char* const fmt, ...) +__attribute__ ((format (printf, 5, 6))); + +void LIBR(vlogger)( + int level, + const char* const caller, + const char* const file, + const unsigned int line, + const char* const fmt, + va_list vl +); + +// libr: now.h + +// returns current time formated as compact ISO8601: 20190123T182628Z +const char* LIBR(now_iso8601_compact)(void); + +// libr: util.h + +#ifndef LENGTH +#define LENGTH(xs) (sizeof(xs)/sizeof((xs)[0])) +#endif + +#ifndef LIT +#define LIT(x) x,sizeof(x) +#endif + +#ifndef STR +#define STR(x) x,strlen(x) +#endif + +#ifndef MAX +#define MAX(a,b) ((a) > (b) ? (a) : (b)) +#endif + +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + +// libr: nonblock.h + +void LIBR(set_blocking)(int fd, int blocking); +#endif // LIBR_HEADER + +#ifdef LIBR_IMPLEMENTATION + +// libr: fail.c + +#include +#include +#include +#include + +API void LIBR(failwith0)( + const char* const caller, + const char* const file, + const unsigned int line, + const int include_errno, + const char* const fmt, ...) +{ + va_list vl; + va_start(vl, fmt); + + if(include_errno) { + LIBR(logger)(LOG_ERROR, caller, file, line, "(%s) ", strerror(errno)); + if(vdprintf(LIBR(logger_fd), fmt, vl) < 0) { + abort(); + } + } else { + LIBR(vlogger)(LOG_ERROR, caller, file, line, fmt, vl); + } + va_end(vl); + + abort(); +} + +// libr: logging.c + +#include +#include +#include + +#ifdef __cplusplus +API void LIBR(dummy)(...) +#else +API void LIBR(dummy)() +#endif +{ + abort(); +} + +int LIBR(logger_fd) API = 2; + +API void LIBR(vlogger)( + int level, + const char* const caller, + const char* const file, + const unsigned int line, + const char* const fmt, va_list vl) +{ + int r = dprintf(LIBR(logger_fd), "%s:%d:%s:%s:%u ", + LIBR(now_iso8601_compact)(), getpid(), caller, file, line); + if(r < 0) { + abort(); + } + + r = vdprintf(LIBR(logger_fd), fmt, vl); + if(r < 0) { + abort(); + } +} + +API void LIBR(logger)( + int level, + const char* const caller, + const char* const file, + const unsigned int line, + const char* const fmt, ...) +{ + va_list vl; + va_start(vl, fmt); + LIBR(vlogger)(level, caller, file, line, fmt, vl); + va_end(vl); +} + +// libr: now.c + +#include +#include + +PRIVATE const char* LIBR(now_iso8601_compact)(void) +{ + static char buf[17]; + const time_t t = time(NULL); + size_t r = strftime(buf, sizeof(buf), "%Y%m%dT%H%M%SZ", gmtime(&t)); + if(r <= 0) abort(); + return buf; +} + +// libr: nonblock.c + +#include + +API void LIBR(set_blocking)(int fd, int blocking) +{ + int fl = fcntl(fd, F_GETFL, 0); + if(blocking) { + fl &= ~O_NONBLOCK; + } else { + fl |= O_NONBLOCK; + } + + int r = fcntl(fd, F_SETFL, fl); + CHECK(r, "fcntl(%d, F_SETFL, %d)", fd, fl); +} +#endif // LIBR_IMPLEMENTATION -- 2.45.2