control utility polishing and minor fixes, renamed;

This commit is contained in:
Alexander Vdolainen 2026-10-12 23:52:13 +03:00
parent 747f30df68
commit 91d74e0bc9
4 changed files with 153 additions and 100 deletions

1
src/.gitignore vendored
View File

@ -2,3 +2,4 @@
.deps
subrelayd
subrelay-shell
sbrlctrl

View File

@ -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

143
src/sbctrl.c Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.";
*
* (c) Alexander Vdolainen 2026 <alex@vapaa.xyz>
* subrelay: a minimalistic maillist server based on external IMAPs/SMTPs
*
* sbctrl: maillist daemon control utility (main entry)
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <getopt.h>
#include <sys/socket.h>
#include <sys/un.h>
#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 <socket_path> <command> [args...]\n\n", progname);
printf("Arguments:\n");
printf(" -s|--socket <path> Path to subrelay local socket\n");
printf(" -h|--help Print help\n");
printf("Commands:\n");
printf(" ADD <email> Directly subscribe an email address\n");
printf(" DEL <email> 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;
}

View File

@ -1,86 +0,0 @@
/* src/subrelay-shell/main.c - Management CLI tool */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/socket.h>
#include <sys/un.h>
static void print_usage(const char *progname)
{
printf("Subrelay Management Shell\n");
printf("Usage: %s -s <socket_path> <command> [args...]\n\n", progname);
printf("Commands:\n");
printf(" ADD <email> Directly subscribe an email address\n");
printf(" DEL <email> 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;
}