From 04d965d67a7264a1c7c211494aebda1953df7603 Mon Sep 17 00:00:00 2001 From: "info@mode42.com" Date: Fri, 7 Aug 2026 18:25:13 +0000 Subject: Initial push --- stacks/max25-bcpr/src/max25-bcprd-init.c | 255 +++++++++++++++++++++++++++++++ 1 file changed, 255 insertions(+) create mode 100644 stacks/max25-bcpr/src/max25-bcprd-init.c (limited to 'stacks/max25-bcpr/src/max25-bcprd-init.c') diff --git a/stacks/max25-bcpr/src/max25-bcprd-init.c b/stacks/max25-bcpr/src/max25-bcprd-init.c new file mode 100644 index 0000000..6a02240 --- /dev/null +++ b/stacks/max25-bcpr/src/max25-bcprd-init.c @@ -0,0 +1,255 @@ +/* + * max25-bcprd-init — privileged SER12 setup (setuid root), then unprivileged daemon. + * Fork keeps ioperm in child; parent writes pidfile and exits. + */ +#define _GNU_SOURCE +#include "bcpr/bcpr_config.h" +#include "bcpr/bcpr_daemon.h" +#include "bcpr/bcpr_engine.h" +#include "bcpr/bcpr_kiss.h" +#include "bcpr/bcpr_runas.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void usage(const char *argv0) +{ + fprintf(stderr, + "Usage: %s -c [--once] [--seconds N]\n" + " [--cal high|low|alt] [--txd-bias pulse|steady]\n" + " [--ptt-wd|--no-ptt-wd] [--ptt-wd-key-ms N]\n" + " [--ptt-wd-pause-ms N] [--version]\n" + " Privileged init for live SER12 — drops to [max25-bcpr] user= after setup.\n" + " Dry-run: use max25-bcprd --dry-run (unprivileged).\n", + argv0); +} + +static int ensure_dir(const char *path) +{ + char tmp[256]; + char *p; + size_t len; + + if (!path || !path[0]) { + return -1; + } + snprintf(tmp, sizeof(tmp), "%s", path); + len = strlen(tmp); + if (len == 0) { + return -1; + } + if (tmp[len - 1] == '/') { + tmp[len - 1] = '\0'; + } + for (p = tmp + 1; *p; p++) { + if (*p == '/') { + *p = '\0'; + (void)mkdir(tmp, 0755); + *p = '/'; + } + } + return mkdir(tmp, 0755) == 0 || errno == EEXIST ? 0 : -1; +} + +static int write_pidfile(const char *path, pid_t pid) +{ + FILE *f; + + f = fopen(path, "w"); + if (!f) { + fprintf(stderr, "max25-bcprd-init: cannot write pidfile %s\n", path); + return -1; + } + fprintf(f, "%d\n", (int)pid); + fclose(f); + return 0; +} + +int main(int argc, char **argv) +{ + const char *cfg_path = NULL; + int seconds = 0; + int cal_mode = BCPR_CAL_OFF; + int cli_txd_bias = -1; + int cli_ptt_wd = -1; + int cli_ptt_wd_key_ms = -1; + int cli_ptt_wd_pause_ms = -1; + int i; + bcpr_config_t cfg; + bcpr_engine_t engine; + bcpr_kiss_pty_t ptys[BCPR_MAX_DEVICES]; + int npty = 0; + char pidpath[256]; + pid_t child; + bcpr_daemon_opts_t opts; + + for (i = 1; i < argc; i++) { + if ((strcmp(argv[i], "-c") == 0 || strcmp(argv[i], "--config") == 0) && + i + 1 < argc) { + cfg_path = argv[++i]; + } else if (strcmp(argv[i], "--once") == 0) { + if (seconds <= 0) { + seconds = 1; + } + } else if (strcmp(argv[i], "--seconds") == 0 && i + 1 < argc) { + seconds = atoi(argv[++i]); + } else if (strcmp(argv[i], "--cal") == 0 && i + 1 < argc) { + const char *m = argv[++i]; + if (strcmp(m, "high") == 0 || strcmp(m, "1") == 0) { + cal_mode = BCPR_CAL_HIGH; + } else if (strcmp(m, "low") == 0 || strcmp(m, "2") == 0) { + cal_mode = BCPR_CAL_LOW; + } else if (strcmp(m, "alt") == 0 || strcmp(m, "3") == 0) { + cal_mode = BCPR_CAL_ALT; + } else { + fprintf(stderr, "max25-bcprd-init: --cal needs high|low|alt\n"); + return 2; + } + } else if (strcmp(argv[i], "--txd-bias") == 0 && i + 1 < argc) { + const char *m = argv[++i]; + if (strcasecmp(m, "pulse") == 0 || strcasecmp(m, "sailer") == 0) { + cli_txd_bias = BCPR_TXD_PULSE; + } else if (strcasecmp(m, "steady") == 0 || + strcasecmp(m, "tfpcx") == 0 || + strcasecmp(m, "break") == 0) { + cli_txd_bias = BCPR_TXD_STEADY; + } else { + fprintf(stderr, "max25-bcprd-init: --txd-bias needs pulse|steady\n"); + return 2; + } + } else if (strcmp(argv[i], "--ptt-wd") == 0) { + cli_ptt_wd = 1; + } else if (strcmp(argv[i], "--no-ptt-wd") == 0) { + cli_ptt_wd = 0; + } else if (strcmp(argv[i], "--ptt-wd-key-ms") == 0 && i + 1 < argc) { + cli_ptt_wd_key_ms = atoi(argv[++i]); + } else if (strcmp(argv[i], "--ptt-wd-pause-ms") == 0 && + i + 1 < argc) { + cli_ptt_wd_pause_ms = atoi(argv[++i]); + } else if (strcmp(argv[i], "--version") == 0 || + strcmp(argv[i], "-V") == 0) { + printf("bcprd-init 0.1.0\n"); + return 0; + } else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) { + usage(argv[0]); + return 0; + } else { + usage(argv[0]); + return 2; + } + } + if (!cfg_path) { + usage(argv[0]); + return 2; + } + if (geteuid() != 0) { + fprintf(stderr, + "max25-bcprd-init: requires root (setuid) for SER12 lock/ioperm/KISS\n"); + return 1; + } + if (cal_mode != BCPR_CAL_OFF && seconds <= 0) { + seconds = 5; + } + + if (bcpr_config_load(&cfg, cfg_path) != 0) { + fprintf(stderr, "max25-bcprd-init: cannot load %s\n", cfg_path); + return 1; + } + if (cfg.dry_run) { + fprintf(stderr, + "max25-bcprd-init: dry_run=yes — use max25-bcprd --dry-run instead\n"); + return 1; + } + if (!cfg.run_user[0]) { + fprintf(stderr, + "max25-bcprd-init: [max25-bcpr] user= required for privilege drop\n"); + return 1; + } + + for (i = 0; i < BCPR_MAX_DEVICES; i++) { + if (cli_txd_bias >= 0) { + cfg.dev[i].txd_bias = cli_txd_bias; + } + if (cli_ptt_wd >= 0) { + cfg.dev[i].ptt_wd = cli_ptt_wd; + } + if (cli_ptt_wd_key_ms > 0) { + cfg.dev[i].ptt_wd_key_ms = cli_ptt_wd_key_ms; + } + if (cli_ptt_wd_pause_ms > 0) { + cfg.dev[i].ptt_wd_pause_ms = cli_ptt_wd_pause_ms; + } + } + + (void)ensure_dir(cfg.state_dir); + snprintf(pidpath, sizeof(pidpath), "%s/max25-bcprd.pid", cfg.state_dir); + + if (cal_mode == BCPR_CAL_OFF) { + for (i = 0; i < BCPR_MAX_DEVICES; i++) { + if (!cfg.dev[i].enabled) { + continue; + } + if (bcpr_kiss_pty_open(&ptys[npty], i, cfg.dev[i].kiss_link, + cfg.state_dir) != 0) { + while (npty > 0) { + npty--; + bcpr_kiss_pty_close(&ptys[npty]); + } + return 1; + } + npty++; + } + } + + if (bcpr_engine_open(&engine, &cfg) != 0) { + for (i = 0; i < npty; i++) { + bcpr_kiss_pty_close(&ptys[i]); + } + return 1; + } + + child = fork(); + if (child < 0) { + perror("max25-bcprd-init fork"); + bcpr_engine_close(&engine); + for (i = 0; i < npty; i++) { + bcpr_kiss_pty_close(&ptys[i]); + } + return 1; + } + if (child > 0) { + if (write_pidfile(pidpath, child) != 0) { + kill(child, SIGTERM); + waitpid(child, NULL, 0); + return 1; + } + _exit(0); + } + + if (setsid() < 0) { + perror("max25-bcprd-init setsid"); + } + + if (bcpr_runas_drop(&cfg) != 0) { + _exit(1); + } + + memset(&opts, 0, sizeof(opts)); + opts.seconds = seconds; + opts.cal_mode = cal_mode; + opts.cli_txd_bias = cli_txd_bias; + opts.cli_ptt_wd = cli_ptt_wd; + opts.cli_ptt_wd_key_ms = cli_ptt_wd_key_ms; + opts.cli_ptt_wd_pause_ms = cli_ptt_wd_pause_ms; + opts.engine_preopened = 1; + + return bcpr_daemon_run(&cfg, ptys, npty, &engine, &opts); +} -- cgit v1.3.1