summaryrefslogtreecommitdiff
path: root/stacks/max25-bcpr/src/max25-bcprd-init.c
diff options
context:
space:
mode:
authorinfo@mode42.com <info@mode42.com>2026-08-07 18:23:28 +0000
committerinfo@mode42.com <info@mode42.com>2026-08-07 18:23:28 +0000
commitfa05a5f8238e9e1417235711656e5062ccc843a7 (patch)
tree46fbbd18de54492b59307c16efcc7f3152723238 /stacks/max25-bcpr/src/max25-bcprd-init.c
Initial push
Diffstat (limited to 'stacks/max25-bcpr/src/max25-bcprd-init.c')
-rw-r--r--stacks/max25-bcpr/src/max25-bcprd-init.c255
1 files changed, 255 insertions, 0 deletions
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 <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+static void usage(const char *argv0)
+{
+ fprintf(stderr,
+ "Usage: %s -c <bcpr.ini> [--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);
+}
git clone -b <branch> https://cgit.mode42.com/<repo>.git
git clone -b <branch> git://cgit.mode42.com/<repo>.git

info@mode42.com