@@ 0,0 1,104 @@
+#include <poll.h>
+#include <signal.h>
+#include <sys/signalfd.h>
+
+#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;
+}
@@ 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 <stdarg.h>
+
+#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 <stdlib.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+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 <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#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 <time.h>
+#include <stdlib.h>
+
+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 <fcntl.h>
+
+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