From 54b4d988f40540019727bf6e71346d15498b7903 Mon Sep 17 00:00:00 2001 From: Gustav Behm Date: Fri, 29 Mar 2024 04:51:45 +0100 Subject: [PATCH] Construct the expected socket path --- c-impl/action.c | 15 +- c-impl/r.h | 547 +++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 559 insertions(+), 3 deletions(-) diff --git a/c-impl/action.c b/c-impl/action.c index b29fe21..06adbe3 100644 --- a/c-impl/action.c +++ b/c-impl/action.c @@ -11,6 +11,7 @@ struct options { enum { NOOP = 0, BIND, + PRINT_PATH, TRIGGER, } cmd; }; @@ -22,6 +23,7 @@ static void print_usage(int fd, const char* prog) dprintf(fd, "commands:\n"); dprintf(fd, " -b bind action\n"); dprintf(fd, " -t trigger action\n"); + dprintf(fd, " -p print socket path\n"); dprintf(fd, " -h print this message\n"); } @@ -30,10 +32,11 @@ static void parse_options(struct options* o, int argc, char* argv[]) memset(o, 0, sizeof(*o)); int res; - while((res = getopt(argc, argv, "bth")) != -1) { + while((res = getopt(argc, argv, "btph")) != -1) { switch(res) { case 't': o->cmd = TRIGGER; break; case 'b': o->cmd = BIND; break; + case 'p': o->cmd = PRINT_PATH; break; case 'h': default: print_usage(res == 'h' ? 1 : 2, argv[0]); @@ -128,6 +131,13 @@ int main(int argc, char* argv[]) struct options o; parse_options(&o, argc, argv); + struct xdg* xdg = xdg_new("action"); + + if(o.cmd == PRINT_PATH) { + printf("%s/%s\n", xdg_runtime(xdg), o.action); + goto exit; + } + struct state st = { .running = 1, }; @@ -159,5 +169,8 @@ int main(int argc, char* argv[]) debug("graceful shutdown"); signalfd_deinit(&st); +exit: + xdg_free(xdg); + return 0; } diff --git a/c-impl/r.h b/c-impl/r.h index 6785f3a..e772839 100644 --- a/c-impl/r.h +++ b/c-impl/r.h @@ -1,5 +1,5 @@ -// libr 0.5.0 (ec3c888754c9966a470ff5a8d1baf1cfaa3045d2) (https://github.com/rootmos/libr.git) (2024-03-29T04:14:36+01:00) -// modules: fail logging now util nonblock +// libr 0.5.0 (ec3c888754c9966a470ff5a8d1baf1cfaa3045d2) (https://github.com/rootmos/libr.git) (2024-03-29T04:47:48+01:00) +// modules: fail logging now util nonblock xdg path #ifndef LIBR_HEADER #define LIBR_HEADER @@ -147,6 +147,68 @@ const char* LIBR(now_iso8601_compact)(void); // libr: nonblock.h void LIBR(set_blocking)(int fd, int blocking); + +// libr: xdg.h + +#include +#include + +// https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html + +struct xdg; + +enum xdg_kind { + XDG_HOME = 0, + XDG_DATA, + XDG_CONFIG, + XDG_STATE, + XDG_CACHE, + XDG_RUNTIME, + XDG_KINDS, +}; + +struct xdg* LIBR(xdg_new)(const char* app); +void LIBR(xdg_free)(struct xdg* xdg); + +const char* LIBR(xdg_dir)(struct xdg* xdg, enum xdg_kind k); +const char** LIBR(xdg_dirs)(struct xdg* xdg, enum xdg_kind k); + +const char* LIBR(xdg_home)(struct xdg* xdg); +const char* LIBR(xdg_data_home)(struct xdg* xdg); +const char* LIBR(xdg_config_home)(struct xdg* xdg); +const char* LIBR(xdg_state_home)(struct xdg* xdg); +const char* LIBR(xdg_cache_home)(struct xdg* xdg); +const char* LIBR(xdg_runtime)(struct xdg* xdg); + +const char** LIBR(xdg_data_dirs)(struct xdg* xdg); +const char** LIBR(xdg_config_dirs)(struct xdg* xdg); + +const char* LIBR(xdg_resolve)(struct xdg* xdg, enum xdg_kind k, char* buf, size_t L, ...); +const char* LIBR(xdg_resolvev)(struct xdg* xdg, enum xdg_kind k, char* buf, size_t L, va_list ps); +const char* LIBR(xdg_resolves)(struct xdg* xdg, enum xdg_kind k, ...); +const char* LIBR(xdg_resolvevs)(struct xdg* xdg, enum xdg_kind k, va_list ps); + +const char* LIBR(xdg_preparev)(struct xdg* xdg, enum xdg_kind k, char* buf, size_t L, va_list ps); +const char* LIBR(xdg_prepare)(struct xdg* xdg, enum xdg_kind k, char* buf, size_t L, ...); +const char* LIBR(xdg_preparevs)(struct xdg* xdg, enum xdg_kind k, va_list ps); +const char* LIBR(xdg_prepares)(struct xdg* xdg, enum xdg_kind k, ...); + +// libr: path.h + +#include +#include +#include + +size_t LIBR(path_join)(char* buf, size_t L, const char* p0, ...); +size_t LIBR(path_joinv)(char* buf, size_t L, const char* p0, va_list ps); + +// using static buffer +#ifdef failwith +const char* LIBR(path_joins)(const char* p0, ...); +const char* LIBR(path_joinvs)(const char* p0, va_list ps); +#endif + +int LIBR(makedirs)(const char* path, mode_t mode); #endif // LIBR_HEADER #ifdef LIBR_IMPLEMENTATION @@ -260,4 +322,485 @@ API void LIBR(set_blocking)(int fd, int blocking) int r = fcntl(fd, F_SETFL, fl); CHECK(r, "fcntl(%d, F_SETFL, %d)", fd, fl); } + +// libr: xdg.c + +#include +#include +#include +#include +#include +#include +#include +#include + +struct xdg { + char app[NAME_MAX]; + + char* dir[XDG_KINDS]; + char** dirs[XDG_KINDS]; +}; + +API struct xdg* LIBR(xdg_new)(const char* app) +{ + struct xdg* xdg = calloc(1, sizeof(*xdg)); + CHECK_MALLOC(xdg); + + if(app) { + const size_t N = sizeof(xdg->app); + size_t n = strnlen(app, N); + if(n >= N) { + failwith("application name truncated at %zu characters", n); + } + + if(n > 0) { + memcpy(xdg->app, app, n); + } + } + + return xdg; +} + +API void LIBR(xdg_free)(struct xdg* xdg) +{ + if(xdg == NULL) return; + + for(int i = 0; i < XDG_KINDS; i++) { + free(xdg->dir[i]); + free(xdg->dirs[i]); + } + + free(xdg); +} + +static size_t xdg_check_path(const char* p) +{ + if(!p) return 0; + size_t r = strnlen(p, PATH_MAX); + if(r >= PATH_MAX) return 0; + if(r == 0) return 0; + if(p[0] != '/') return 0; + return r; +} + +static size_t xdg_append_app(const struct xdg* xdg, char* buf, size_t L, const char* p) +{ + if(xdg->app[0]) { + size_t l = LIBR(path_join)(buf, L, p, xdg->app, NULL); + if(l >= L) { + failwith("buffer overflow"); + } + + return l; + } else { + return strlen(buf); + } +} + +static size_t LIBR(xdg_fallback_runtime_dir)(char* buf, size_t L) +{ + char template[NAME_MAX]; + size_t n = snprintf(LIT(template), "/tmp/xdg-runtime-fallback-%d-XXXXXX", geteuid()); + if(n >= sizeof(template)) { + failwith("buffer overflow"); + } + + // mkdtemp(3): "The directory is then created with permissions 0700." + char* tmp = mkdtemp(template); + CHECK_NOT(tmp, NULL, "mkdtemp(%s)", template); + warning("using fallback directory for an unset XDG_RUNTIME_DIR: %s", tmp); + return snprintf(buf, L, "%s", tmp); +} + +API const char* LIBR(xdg_dir)(struct xdg* xdg, enum xdg_kind k) +{ + if(xdg->dir[k]) return xdg->dir[k]; + + if(k == XDG_HOME) { + char* home = getenv("HOME"); + if(!home) { + failwith("unable to resolve HOME variable"); + } + + xdg->dir[k] = strdup(home); + CHECK_MALLOC(xdg->dir[k]); + return xdg->dir[k]; + } + + char buf[PATH_MAX]; + size_t l; + + const char* v = NULL; + switch(k) { + case XDG_DATA: v = "XDG_DATA_HOME"; break; + case XDG_CONFIG: v = "XDG_CONFIG_HOME"; break; + case XDG_STATE: v = "XDG_STATE_HOME"; break; + case XDG_CACHE: v = "XDG_CACHE_HOME"; break; + case XDG_RUNTIME: v = "XDG_RUNTIME_DIR"; break; + default: failwith("unexpected kind: %d", k); + } + + const char* e = getenv(v); + if(xdg_check_path(e)) { + l = LIBR(path_join)(LIT(buf), e, NULL); + } else { + switch(k) { + case XDG_DATA: + l = LIBR(path_join)(LIT(buf), LIBR(xdg_home)(xdg), ".local", "share", NULL); + break; + case XDG_CONFIG: + l = LIBR(path_join)(LIT(buf), LIBR(xdg_home)(xdg), ".config", NULL); + break; + case XDG_STATE: + l = LIBR(path_join)(LIT(buf), LIBR(xdg_home)(xdg), ".local", "state", NULL); + break; + case XDG_CACHE: + l = LIBR(path_join)(LIT(buf), LIBR(xdg_home)(xdg), ".cache", NULL); + break; + case XDG_RUNTIME: + l = LIBR(xdg_fallback_runtime_dir)(LIT(buf)); + break; + default: + failwith("unexpected kind: %d", k); + } + } + if(l >= sizeof(buf)) { + failwith("buffer overflow"); + } + + xdg_append_app(xdg, LIT(buf), buf); + + xdg->dir[k] = strdup(buf); + CHECK_MALLOC(xdg->dir[k]); + + return xdg->dir[k]; +} + +API const char* LIBR(xdg_home)(struct xdg* xdg) +{ + return LIBR(xdg_dir)(xdg, XDG_HOME); +} + +API const char* LIBR(xdg_data_home)(struct xdg* xdg) +{ + return LIBR(xdg_dir)(xdg, XDG_DATA); +} + +API const char* LIBR(xdg_config_home)(struct xdg* xdg) +{ + return LIBR(xdg_dir)(xdg, XDG_CONFIG); +} + +API const char* LIBR(xdg_state_home)(struct xdg* xdg) +{ + return LIBR(xdg_dir)(xdg, XDG_STATE); +} + +API const char* LIBR(xdg_cache_home)(struct xdg* xdg) +{ + return LIBR(xdg_dir)(xdg, XDG_CACHE); +} + +API const char* LIBR(xdg_runtime)(struct xdg* xdg) +{ + return LIBR(xdg_dir)(xdg, XDG_RUNTIME); +} + +API const char** LIBR(xdg_dirs)(struct xdg* xdg, enum xdg_kind k) +{ + if(xdg->dirs[k]) return (const char**)xdg->dirs[k]; + + const char* e = NULL; + if(k == XDG_DATA || k == XDG_CONFIG) { + const char* v = NULL; + if(k == XDG_DATA) { + v = "XDG_DATA_DIRS"; + } else if(k == XDG_CONFIG) { + v = "XDG_CONFIG_DIRS"; + } + + e = getenv(v); + if(e == NULL || *e == 0) { + if(k == XDG_DATA) { + e = "/usr/local/share:/usr/share"; + } else if(k == XDG_CONFIG) { + e = "/etc/xdg:/etc"; + } + } + } + + struct { + char* path; + size_t len; + void* next; + }* dirs; + + { + dirs = alloca(sizeof(*dirs)); + const char* h = LIBR(xdg_dir)(xdg, k); + dirs->len = strlen(h); + dirs->path = alloca(dirs->len+1); + memcpy(dirs->path, h, dirs->len+1); + dirs->next = NULL; + } + typeof(*dirs)** tail = (void*)&dirs->next; + + size_t n = 1; + if(e) { + const size_t L = strlen(e); + char buf[L+1]; + memcpy(buf, e, L+1); + size_t a = 0, b = 0; + do { + for(; buf[b] != ':' && buf[b] != 0; b++); + buf[b] = 0; + char* p = &buf[a]; + if(xdg_check_path(p)) { + char q[PATH_MAX]; + size_t l = LIBR(xdg_append_app)(xdg, LIT(q), p); + if(l >= sizeof(q)) { + failwith("buffer overflow"); + } + + typeof(*dirs)* t = alloca(sizeof(*dirs)); + t->len = l; + t->path = alloca(l+1); + memcpy(t->path, q, l+1); + t->next = NULL; + + *tail = t; + tail = (void*)&t->next; + n += 1; + } + b += 1; + a = b; + } while(b < L); + } + + size_t N = 0; + for(typeof(*dirs)* p = dirs; p != NULL; p = p->next) { + N += sizeof(char*); + N += p->len+1; + } + N += sizeof(char*); // add space for the NULL guard + + void* buf = malloc(N); + CHECK_MALLOC(buf); + char** index = buf; + + index[n] = NULL; + char* strings = (char*)&index[n+1]; + + size_t i = 0; + for(typeof(*dirs)* p = dirs; p != NULL; i++, p = p->next) { + index[i] = strings; + memcpy(strings, p->path, p->len + 1); + strings += p->len + 1; + } + + xdg->dirs[k] = buf; + return buf; +} + +API const char** LIBR(xdg_data_dirs)(struct xdg* xdg) +{ + return LIBR(xdg_dirs)(xdg, XDG_DATA); +} + +API const char** LIBR(xdg_config_dirs)(struct xdg* xdg) +{ + return LIBR(xdg_dirs)(xdg, XDG_CONFIG); +} + +API const char* LIBR(xdg_resolvev)(struct xdg* xdg, enum xdg_kind k, char* buf, size_t L, va_list ps) +{ + const char** dirs = LIBR(xdg_dirs)(xdg, k); + for(size_t i = 0; dirs[i]; i++) { + va_list qs; + va_copy(qs, ps); + size_t l = LIBR(path_joinv)(buf, L, dirs[i], qs); + if(l >= L) { + failwith("buffer overflow"); + } + va_end(qs); + + struct stat st; + int r = stat(buf, &st); + if(r == -1 && (errno == EACCES || errno == ENOENT || errno == ENOTDIR)) { + continue; + } + CHECK(r, "stat(%s)", buf); + + return buf; + } + + return NULL; +} + +API const char* LIBR(xdg_resolve)(struct xdg* xdg, enum xdg_kind k, char* buf, size_t L, ...) +{ + va_list ps; va_start(ps, L); + const char* p = LIBR(xdg_resolvev)(xdg, k, buf, L, ps); + return va_end(ps), p; +} + +API const char* LIBR(xdg_resolvevs)(struct xdg* xdg, enum xdg_kind k, va_list ps) +{ + static char buf[PATH_MAX]; + return LIBR(xdg_resolvev)(xdg, k, LIT(buf), ps); +} + +API const char* LIBR(xdg_resolves)(struct xdg* xdg, enum xdg_kind k, ...) +{ + va_list ps; va_start(ps, k); + const char* p = LIBR(xdg_resolvevs)(xdg, k, ps); + return va_end(ps), p; +} + +API void LIBR(xdg_makedirs)(const char* path) +{ + int r = LIBR(makedirs)(path, 0700); + CHECK(r, "makedirs(%s, 0700)", path); +} + +API const char* LIBR(xdg_preparev)(struct xdg* xdg, enum xdg_kind k, char* buf, size_t L, va_list ps) +{ + size_t l = LIBR(path_joinv)(buf, L, LIBR(xdg_dir)(xdg, k), ps); + if(l >= L) { + failwith("buffer overflow"); + } + + struct stat st; + int r = stat(buf, &st); + if(r == -1 && errno == ENOENT) { + char d[l+1]; + memcpy(d, buf, l+1); + LIBR(xdg_makedirs)(dirname(d)); + return buf; + } + CHECK(r, "stat(%s)", buf); + + return buf; +} + +API const char* LIBR(xdg_prepare)(struct xdg* xdg, enum xdg_kind k, char* buf, size_t L, ...) +{ + va_list ps; va_start(ps, L); + const char* p = LIBR(xdg_preparev)(xdg, k, buf, L, ps); + return va_end(ps), p; +} + +API const char* LIBR(xdg_preparevs)(struct xdg* xdg, enum xdg_kind k, va_list ps) +{ + static char buf[PATH_MAX]; + return LIBR(xdg_preparev)(xdg, k, LIT(buf), ps); +} + +API const char* LIBR(xdg_prepares)(struct xdg* xdg, enum xdg_kind k, ...) +{ + va_list ps; va_start(ps, k); + const char* p = LIBR(xdg_preparevs)(xdg, k, ps); + return va_end(ps), p; +} + +// libr: path.c + +#include +#include +#include +#include + +API size_t LIBR(path_joinv)(char* buf, size_t L, const char* p0, va_list ps) +{ + size_t n = strlen(p0); + if(n < L) { + memmove(buf, p0, n); + } + + for(;;) { + const char* p = va_arg(ps, const char*); + if(!p) break; + + if(n < L) { + buf[n] = '/'; + } + n += 1; + + size_t l = strlen(p); + if(n + l < L) { + memcpy(&buf[n], p, l); + } + n += l; + } + + if(n < L) { + buf[n] = 0; + } + + return n; +} + +API size_t LIBR(path_join)(char* buf, size_t L, const char* p0, ...) +{ + va_list ps; va_start(ps, p0); + size_t n = LIBR(path_joinv)(buf, L, p0, ps); + return va_end(ps), n; +} + +#ifdef failwith +API const char* LIBR(path_joinvs)(const char* p0, va_list ps) +{ + static char buf[PATH_MAX]; + size_t l = LIBR(path_joinv)(buf, sizeof(buf), p0, ps); + if(l >= sizeof(buf)) { + failwith("buffer overflow"); + } + + return buf; +} + +API const char* LIBR(path_joins)(const char* p0, ...) +{ + va_list ps; va_start(ps, p0); + const char* p = LIBR(path_joinvs)(p0, ps); + return va_end(ps), p; +} +#endif + +API int LIBR(makedirs)(const char* path, mode_t mode) +{ + const size_t L = strlen(path); + char buf[L+1]; + memcpy(buf, path, L+1); + + for(size_t l = (buf[0] == '/' ? 1 : 0); l <= L; l++) { + if(buf[l] == '/') { + buf[l] = 0; + } + + if(buf[l] == 0) { + int r = mkdir(buf, mode); + if(r == -1) { + if(errno != EEXIST) { + return -1; + } + } + + if(l < L) { + buf[l] = '/'; + } + } + } + + struct stat st; + int r = stat(buf, &st); + if(r != 0) return -1; + + if(!(st.st_mode & S_IFDIR)) { + errno = EEXIST; + return -1; + } + + return 0; +} #endif // LIBR_IMPLEMENTATION -- 2.45.2