From 91d74e0bc93fdc5f0a8a63862b179807ae395322 Mon Sep 17 00:00:00 2001 From: Alexander Vdolainen Date: Mon, 12 Oct 2026 23:52:13 +0300 Subject: [PATCH] control utility polishing and minor fixes, renamed; --- src/.gitignore | 1 + src/Makefile.am | 23 +++---- src/sbctrl.c | 143 +++++++++++++++++++++++++++++++++++++++++++ src/subrelay-shell.c | 86 -------------------------- 4 files changed, 153 insertions(+), 100 deletions(-) create mode 100644 src/sbctrl.c delete mode 100644 src/subrelay-shell.c diff --git a/src/.gitignore b/src/.gitignore index 303b6bc..aa68809 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -2,3 +2,4 @@ .deps subrelayd subrelay-shell +sbrlctrl diff --git a/src/Makefile.am b/src/Makefile.am index dff0637..85431c9 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,8 +1,8 @@ -bin_PROGRAMS = subrelayd subrelay-shell +bin_PROGRAMS = subrelayd sbrlctrl noinst_HEADERS = \ $(top_srcdir)/include/ilib/config.h \ - $(top_srcdir)/include/ilib/ipc.h \ + $(top_srcdir)/include/ilib/local.h \ $(top_srcdir)/include/ilib/mail_engine.h \ $(top_srcdir)/include/ilib/mail_processor.h \ $(top_srcdir)/include/ilib/pending.h \ @@ -10,9 +10,7 @@ noinst_HEADERS = \ $(top_srcdir)/include/ilib/subscribers.h \ $(top_srcdir)/include/ilib/template.h -# ------------------------------------------------------------------------------ -# subrelayd Daemon -# ------------------------------------------------------------------------------ +# daemon sources subrelayd_SOURCES = \ subrelayd.c \ splay.c \ @@ -22,18 +20,15 @@ subrelayd_SOURCES = \ template.c \ mail_engine.c \ mail_processor.c \ - ipc.c + local.c -# Include internal header search path and PKG_CHECK libcurl flags subrelayd_CPPFLAGS = -I$(top_srcdir)/include -D_POSIX_C_SOURCE=200809L -D_DEFAULT_SOURCE $(CURL_CFLAGS) subrelayd_CFLAGS = -Wall -Wextra -pedantic -std=c99 subrelayd_LDADD = $(CURL_LIBS) -# ------------------------------------------------------------------------------ -# subrelay-shell CLI Tool -# ------------------------------------------------------------------------------ -subrelay_shell_SOURCES = \ - subrelay-shell.c +# shell CLI for control daemon +sbrlctrl_SOURCES = \ + sbctrl.c -subrelay_shell_CFLAGS = -Wall -Wextra -pedantic -std=c99 -subrelay_shell_CPPFLAGS = -I$(top_srcdir)/include -D_POSIX_C_SOURCE=200809L -D_DEFAULT_SOURCE +sbrlctrl_CFLAGS = -Wall -Wextra -pedantic -std=c99 +sbrlctrl_CPPFLAGS = -I$(top_srcdir)/include -D_POSIX_C_SOURCE=200809L -D_DEFAULT_SOURCE diff --git a/src/sbctrl.c b/src/sbctrl.c new file mode 100644 index 0000000..379b550 --- /dev/null +++ b/src/sbctrl.c @@ -0,0 +1,143 @@ +/* + * this is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published + * by the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * this is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see ."; + * + * (c) Alexander Vdolainen 2026 + * subrelay: a minimalistic maillist server based on external IMAPs/SMTPs + * + * sbctrl: maillist daemon control utility (main entry) + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAXCMD_LEN 1024 +#define READBUF_LEN 2048 +#define DEFAULT_SOCKET "/var/run/subrelay/subrelayd.sock" + +static void print_usage(const char *progname) +{ + printf("Subrelay control utility\n"); + printf("Usage: %s -s [args...]\n\n", progname); + printf("Arguments:\n"); + printf(" -s|--socket Path to subrelay local socket\n"); + printf(" -h|--help Print help\n"); + printf("Commands:\n"); + printf(" ADD Directly subscribe an email address\n"); + printf(" DEL Unsubscribe an email address\n"); + printf(" LIST List all current subscribers\n"); + printf(" STATUS Show daemon operational status\n"); + return; +} + +int main(int argc, char **argv) +{ + char *socket_path = DEFAULT_SOCKET; + char cmd_buf[MAXCMD_LEN]; + size_t used = 0; + int opt; + + while(1) { + int option_index = 0; + static struct option long_options[] = { + {"help", no_argument, NULL, 'h'}, + {"socket", required_argument, NULL, 's'}, + {NULL, 0, NULL, 0}, + }; + + if((opt = getopt_long(argc, argv, "hs:", long_options, &option_index)) == -1) + break; + + switch(opt) { + case 'h': print_usage(argv[0]); return 0; break; + case 's': socket_path = optarg; break; + default: print_usage(argv[0]); return EXIT_FAILURE; break; + } + } + + if (optind >= argc) { + fprintf(stderr, "error: command required\n"); + print_usage(argv[0]); + return EXIT_FAILURE; + } + + /* Build command string from remaining arguments */ + cmd_buf[0] = '\0'; + for (int i = optind; i < argc; i++) { + size_t len = strlen(argv[i]); + size_t extra = len + (i < argc - 1 ? 1 : 0); /* argument + optional space */ + + /* Leave room for the final newline and the terminating nil */ + if (extra > sizeof(cmd_buf) - used - 2) { + fprintf(stderr, "error: command or arguments too long.\n"); + return EXIT_FAILURE; + } + + memcpy(cmd_buf + used, argv[i], len); + used += len; + + if (i < argc - 1) cmd_buf[used++] = ' '; + } + + cmd_buf[used++] = '\n'; + cmd_buf[used] = '\0'; + + /* Connect to subrelayd UNIX domain socket */ + int fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (fd < 0) { + perror("error opening socket"); + return EXIT_FAILURE; + } + + struct sockaddr_un addr; + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + if(strlen(socket_path) > sizeof(addr.sun_path)) { + fprintf(stderr, "Socket pathname is too long.\n"); + close(fd); + return EXIT_FAILURE; + } else strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path) - 1); + + if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) != 0) { + fprintf(stderr, "error: cannot connect to subrelayd at '%s': %s\n", + socket_path, strerror(errno)); + close(fd); + return EXIT_FAILURE; + } + + /* Send command */ + if (write(fd, cmd_buf, strlen(cmd_buf)) < 0) { + perror("error writing command to socket"); + close(fd); + return EXIT_FAILURE; + } + + /* Read and print daemon response */ + char resp[READBUF_LEN]; + ssize_t bytes; + + while ((bytes = read(fd, resp, sizeof resp)) > 0) { + if (fwrite(resp, 1, (size_t)bytes, stdout) != (size_t)bytes) break; + } + + /* ignore read errors -- non critical */ + + close(fd); + return EXIT_SUCCESS; +} diff --git a/src/subrelay-shell.c b/src/subrelay-shell.c deleted file mode 100644 index 085c5ce..0000000 --- a/src/subrelay-shell.c +++ /dev/null @@ -1,86 +0,0 @@ -/* src/subrelay-shell/main.c - Management CLI tool */ -#include -#include -#include -#include -#include -#include -#include - -static void print_usage(const char *progname) -{ - printf("Subrelay Management Shell\n"); - printf("Usage: %s -s [args...]\n\n", progname); - printf("Commands:\n"); - printf(" ADD Directly subscribe an email address\n"); - printf(" DEL Unsubscribe an email address\n"); - printf(" LIST List all current subscribers\n"); - printf(" STATUS Show daemon operational status\n"); -} - -int main(int argc, char **argv) -{ - char *socket_path = "/var/run/subrelay/subrelayd.sock"; - int opt; - - while ((opt = getopt(argc, argv, "s:h")) != -1) { - switch (opt) { - case 's': socket_path = optarg; break; - case 'h': print_usage(argv[0]); return EXIT_SUCCESS; - default: print_usage(argv[0]); return EXIT_FAILURE; - } - } - - if (optind >= argc) { - fprintf(stderr, "error: command required\n"); - print_usage(argv[0]); - return EXIT_FAILURE; - } - - /* Build command string from remaining arguments */ - char cmd_buf[1024]; - cmd_buf[0] = '\0'; - - for (int i = optind; i < argc; i++) { - strcat(cmd_buf, argv[i]); - if (i < argc - 1) strcat(cmd_buf, " "); - } - strcat(cmd_buf, "\n"); - - /* Connect to subrelayd UNIX domain socket */ - int fd = socket(AF_UNIX, SOCK_STREAM, 0); - if (fd < 0) { - perror("error opening socket"); - return EXIT_FAILURE; - } - - struct sockaddr_un addr; - memset(&addr, 0, sizeof(addr)); - addr.sun_family = AF_UNIX; - strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path) - 1); - - if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) != 0) { - fprintf(stderr, "error: cannot connect to subrelayd at '%s': %s\n", - socket_path, strerror(errno)); - close(fd); - return EXIT_FAILURE; - } - - /* Send command */ - if (write(fd, cmd_buf, strlen(cmd_buf)) < 0) { - perror("error writing command to socket"); - close(fd); - return EXIT_FAILURE; - } - - /* Read and print daemon response */ - char resp[2048]; - ssize_t bytes; - while ((bytes = read(fd, resp, sizeof(resp) - 1)) > 0) { - resp[bytes] = '\0'; - printf("%s", resp); - } - - close(fd); - return EXIT_SUCCESS; -}