From 9472e08262d270dae5fad6908e89261a6660d298 Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Mon, 16 Feb 2026 07:19:39 +0100 Subject: [PATCH] confd: clean up, simplify use common log framework - Use same log frameworks as reset of confd - Use existing primitives from libite + libsrx - Drop remaining pthreads - Coding style fixes Signed-off-by: Joachim Wiberg --- package/confd/confd.conf | 3 +- src/confd/src/Makefile.am | 4 +- src/confd/src/main.c | 459 +++++++++++++------------------------- 3 files changed, 158 insertions(+), 308 deletions(-) diff --git a/package/confd/confd.conf b/package/confd/confd.conf index 42385ad8..cc8d0572 100644 --- a/package/confd/confd.conf +++ b/package/confd/confd.conf @@ -3,10 +3,9 @@ # Single daemon handles gen-config, datastore init, config load, and plugins # log:prio:daemon.err service log:console env:/etc/default/confd \ - [S12345] confd -f -n -v warning \ + [S12345] confd -f -v warning \ -F /etc/factory-config.cfg \ -S /cfg/startup-config.cfg \ -E /etc/failure-config.cfg \ - -p /run/confd.pid \ -t $CONFD_TIMEOUT \ -- Configuration daemon diff --git a/src/confd/src/Makefile.am b/src/confd/src/Makefile.am index 4ab20ba0..f457f1ad 100644 --- a/src/confd/src/Makefile.am +++ b/src/confd/src/Makefile.am @@ -6,8 +6,8 @@ plugin_LTLIBRARIES = confd-plugin.la sbin_PROGRAMS = confd -confd_CFLAGS = $(sysrepo_CFLAGS) $(libyang_CFLAGS) $(jansson_CFLAGS) $(libite_CFLAGS) -confd_LDADD = $(sysrepo_LIBS) $(libyang_LIBS) $(jansson_LIBS) $(libite_LIBS) $(EV_LIBS) -ldl +confd_CFLAGS = $(sysrepo_CFLAGS) $(libyang_CFLAGS) $(jansson_CFLAGS) $(libite_CFLAGS) $(libsrx_CFLAGS) +confd_LDADD = $(sysrepo_LIBS) $(libyang_LIBS) $(jansson_LIBS) $(libite_LIBS) $(libsrx_LIBS) $(EV_LIBS) -ldl confd_SOURCES = main.c confd_plugin_la_LDFLAGS = -module -avoid-version -shared diff --git a/src/confd/src/main.c b/src/confd/src/main.c index 7e454e71..26566f5f 100644 --- a/src/confd/src/main.c +++ b/src/confd/src/main.c @@ -18,10 +18,9 @@ #include #include #include -#include +#include #include #include -#include #include #include #include @@ -33,9 +32,13 @@ #include #include #include +#include #include #include +/* Maximum number of sysrepo event pipe file descriptors across all plugins */ +#define MAX_EVENT_FDS 64 + /* Callback type names from sysrepo plugin API */ #define SRP_INIT_CB "sr_plugin_init_cb" #define SRP_CLEANUP_CB "sr_plugin_cleanup_cb" @@ -60,31 +63,17 @@ struct plugin { int initialized; }; -/* Maximum number of sysrepo event pipe file descriptors across all plugins */ -#define MAX_EVENT_FDS 64 +static sig_atomic_t pump_running = 1; +int debug = 0; -static void error_print(int sr_error, const char *format, ...) -{ - va_list ap; - char msg[2048]; - - if (!sr_error) - snprintf(msg, sizeof(msg), "confd error: %s\n", format); - else - snprintf(msg, sizeof(msg), "confd error: %s (%s)\n", format, sr_strerror(sr_error)); - - va_start(ap, format); - vfprintf(stderr, msg, ap); - va_end(ap); -} /* Finit style progress output on console */ static void conout(int rc, const char *fmt, ...) { const char *sta = "%s\e[1m[\e[1;%dm%s\e[0m\e[1m]\e[0m %s"; const char *msg[] = { " OK ", "FAIL", "WARN", " ⋯ " }; - const int col[] = { 32, 31, 33, 33 }; const char *cr = rc == 3 ? "" : "\r"; + const int col[] = { 32, 31, 33, 33 }; char buf[80]; va_list ap; @@ -102,46 +91,28 @@ static void version_print(void) static void help_print(void) { - printf( - "Usage:\n" - " confd [-h] [-V] [-v ] [-d] [-n] [-f] [-p pidfile]\n" - " [-F factory-config] [-S startup-config] [-E failure-config]\n" - " [-t timeout]\n" - "\n" - "Options:\n" - " -h, --help Prints usage help.\n" - " -V, --version Prints version information.\n" - " -v, --verbosity \n" - " Change verbosity to a level (none, error, warning, info, debug) or\n" - " number (0, 1, 2, 3, 4).\n" - " -d, --debug Debug mode - not daemonized and logs to stderr.\n" - " -n, --foreground Run in foreground and log to syslog.\n" - " -f, --fatal-plugin-fail\n" - " Terminate if any plugin initialization fails.\n" - " -p, --pid-file \n" - " Create a PID file at the specified path.\n" - " -F, --factory-config \n" - " Factory default config file (default: /etc/factory-config.cfg).\n" - " -S, --startup-config \n" - " Startup config file (default: /cfg/startup-config.cfg).\n" - " -E, --failure-config \n" - " Failure fallback config file (default: /etc/failure-config.cfg).\n" - " -t, --timeout Sysrepo operation timeout in seconds (default: 60).\n" - "\n" - "Environment variable $SRPD_PLUGINS_PATH overwrites the default plugins directory.\n" - "\n"); -} - -static void ignore_signals(void) -{ - struct sigaction action; - - memset(&action, 0, sizeof(action)); - action.sa_handler = SIG_IGN; - sigaction(SIGPIPE, &action, NULL); - sigaction(SIGTSTP, &action, NULL); - sigaction(SIGTTIN, &action, NULL); - sigaction(SIGTTOU, &action, NULL); + printf("Usage:\n" + " confd [-h] [-V] [-v ] [-f]\n" + " [-F factory-config] [-S startup-config] [-E failure-config]\n" + " [-t timeout]\n" + "\n" + "Options:\n" + " -h, --help Prints usage help.\n" + " -V, --version Prints version information.\n" + " -v, --verbosity \n" + " Change verbosity to a level (none, error, warning, info, debug).\n" + " -f, --fatal-plugin-fail\n" + " Terminate if any plugin initialization fails.\n" + " -F, --factory-config \n" + " Factory default config file (default: /etc/factory-config.cfg).\n" + " -S, --startup-config \n" + " Startup config file (default: /cfg/startup-config.cfg).\n" + " -E, --failure-config \n" + " Failure fallback config file (default: /etc/failure-config.cfg).\n" + " -t, --timeout Sysrepo operation timeout in seconds (default: 60).\n" + "\n" + "Environment variable $SRPD_PLUGINS_PATH overwrites the default plugins directory.\n" + "\n"); } /* libev callbacks for steady-state operation */ @@ -160,28 +131,27 @@ static void sr_event_cb(struct ev_loop *loop, struct ev_io *w, int revents) } /* - * Temporary event pump thread for bootstrap. + * Temporary event pump process for bootstrap. * * With SR_SUBSCR_NO_THREAD, sysrepo writes events to a pipe and waits * for the application to call sr_subscription_process_events(). During * bootstrap, sr_replace_config() blocks waiting for callbacks — this - * thread ensures those callbacks get dispatched. + * child process ensures those callbacks get dispatched. */ -struct event_pump { - struct plugin *plugins; - int plugin_count; - int running; -}; +static void pump_sigterm(int sig) +{ + (void)sig; + pump_running = 0; +} -static void *event_pump_thread(void *arg) +static void event_pump(struct plugin *plugins, int plugin_count) { - struct event_pump *pump = arg; - struct pollfd fds[MAX_EVENT_FDS]; sr_subscription_ctx_t *subs[MAX_EVENT_FDS]; + struct pollfd fds[MAX_EVENT_FDS]; int nfds = 0; - for (int i = 0; i < pump->plugin_count; i++) { - struct plugin *p = &pump->plugins[i]; + for (int i = 0; i < plugin_count; i++) { + struct plugin *p = &plugins[i]; if (p->sub && sr_get_event_pipe(p->sub, &fds[nfds].fd) == SR_ERR_OK) { fds[nfds].events = POLLIN; @@ -195,7 +165,9 @@ static void *event_pump_thread(void *arg) } } - while (pump->running) { + signal(SIGTERM, pump_sigterm); + + while (pump_running) { if (poll(fds, nfds, 100) > 0) { for (int i = 0; i < nfds; i++) if (fds[i].revents & POLLIN) @@ -203,12 +175,12 @@ static void *event_pump_thread(void *arg) } } - return NULL; + _exit(0); } static void quiet_now(void) { - int fd = -1; + int fd; fd = open("/dev/null", O_RDWR, 0); if (fd != -1) { @@ -217,89 +189,6 @@ static void quiet_now(void) dup2(fd, STDERR_FILENO); close(fd); } - - nice(0); -} - -static void daemon_init(int debug, sr_log_level_t log_level) -{ - pid_t pid = 0, sid = 0; - - nice(-20); - - ignore_signals(); - - if (debug) { - if (debug < 0) - goto done; - sr_log_stderr(log_level); - return; - } - - pid = fork(); - if (pid < 0) { - error_print(0, "fork() failed (%s).", strerror(errno)); - exit(EXIT_FAILURE); - } - if (pid > 0) - exit(EXIT_SUCCESS); - - sid = setsid(); - if (sid < 0) { - error_print(0, "setsid() failed (%s).", strerror(errno)); - exit(EXIT_FAILURE); - } - - if (chdir("/") < 0) { - error_print(0, "chdir() failed (%s).", strerror(errno)); - exit(EXIT_FAILURE); - } - - quiet_now(); -done: - sr_log_syslog("confd", log_level); -} - -static int open_pidfile(const char *pidfile) -{ - int pidfd; - - pidfd = open(pidfile, O_RDWR | O_CREAT, 0640); - if (pidfd < 0) { - error_print(0, "Unable to open the PID file \"%s\" (%s).", pidfile, strerror(errno)); - return -1; - } - - if (lockf(pidfd, F_TLOCK, 0) < 0) { - if (errno == EACCES || errno == EAGAIN) - error_print(0, "Another instance of confd is running."); - else - error_print(0, "Unable to lock the PID file \"%s\" (%s).", pidfile, strerror(errno)); - close(pidfd); - return -1; - } - - return pidfd; -} - -static int write_pidfile(int pidfd) -{ - char pid[30] = {0}; - int pid_len; - - if (ftruncate(pidfd, 0)) { - error_print(0, "Failed to truncate pid file (%s).", strerror(errno)); - return -1; - } - - snprintf(pid, sizeof(pid) - 1, "%ld\n", (long)getpid()); - pid_len = strlen(pid); - if (write(pidfd, pid, pid_len) < pid_len) { - error_print(0, "Failed to write PID into pid file (%s).", strerror(errno)); - return -1; - } - - return 0; } /* @@ -318,14 +207,14 @@ static size_t path_len_no_ext(const char *path) static int load_plugins(struct plugin **plugins, int *plugin_count) { - void *mem, *handle; - struct plugin *plugin; - DIR *dir; - struct dirent *ent; const char *plugins_dir; - char *path; + struct dirent *ent; + struct plugin *plugin; + void *mem, *handle; size_t name_len; int rc = 0; + char *path; + DIR *dir; *plugins = NULL; *plugin_count = 0; @@ -336,7 +225,7 @@ static int load_plugins(struct plugin **plugins, int *plugin_count) dir = opendir(plugins_dir); if (!dir) { - error_print(0, "Opening \"%s\" directory failed (%s).", plugins_dir, strerror(errno)); + ERRNO("Opening \"%s\" directory failed", plugins_dir); return -1; } @@ -345,13 +234,13 @@ static int load_plugins(struct plugin **plugins, int *plugin_count) continue; if (asprintf(&path, "%s/%s", plugins_dir, ent->d_name) == -1) { - error_print(0, "asprintf() failed (%s).", strerror(errno)); + ERRNO("asprintf() failed"); rc = -1; break; } handle = dlopen(path, RTLD_LAZY); if (!handle) { - error_print(0, "Opening plugin \"%s\" failed (%s).", path, dlerror()); + ERROR("Opening plugin \"%s\" failed: %s", path, dlerror()); free(path); rc = -1; break; @@ -360,7 +249,7 @@ static int load_plugins(struct plugin **plugins, int *plugin_count) mem = realloc(*plugins, (*plugin_count + 1) * sizeof(**plugins)); if (!mem) { - error_print(0, "realloc() failed (%s).", strerror(errno)); + ERRNO("realloc() failed"); dlclose(handle); rc = -1; break; @@ -371,8 +260,7 @@ static int load_plugins(struct plugin **plugins, int *plugin_count) *(void **)&plugin->init_cb = dlsym(handle, SRP_INIT_CB); if (!plugin->init_cb) { - error_print(0, "Failed to find function \"%s\" in plugin \"%s\".", - SRP_INIT_CB, ent->d_name); + ERROR("Failed to find \"%s\" in plugin \"%s\".", SRP_INIT_CB, ent->d_name); dlclose(handle); rc = -1; break; @@ -380,8 +268,7 @@ static int load_plugins(struct plugin **plugins, int *plugin_count) *(void **)&plugin->cleanup_cb = dlsym(handle, SRP_CLEANUP_CB); if (!plugin->cleanup_cb) { - error_print(0, "Failed to find function \"%s\" in plugin \"%s\".", - SRP_CLEANUP_CB, ent->d_name); + ERROR("Failed to find \"%s\" in plugin \"%s\".", SRP_CLEANUP_CB, ent->d_name); dlclose(handle); rc = -1; break; @@ -394,7 +281,7 @@ static int load_plugins(struct plugin **plugins, int *plugin_count) name_len = path_len_no_ext(ent->d_name); if (name_len == 0) { - error_print(0, "Wrong filename \"%s\".", ent->d_name); + ERROR("Wrong filename \"%s\".", ent->d_name); dlclose(handle); rc = -1; break; @@ -402,7 +289,7 @@ static int load_plugins(struct plugin **plugins, int *plugin_count) plugin->name = strndup(ent->d_name, name_len); if (!plugin->name) { - error_print(0, "strndup() failed."); + ERRNO("strndup() failed"); dlclose(handle); rc = -1; break; @@ -490,8 +377,7 @@ static int maybe_migrate(const char *path) } json_decref(root); - SRPLG_LOG_INF("confd", "%s config version %s vs confd %s, migrating ...", - path, file_ver, CONFD_VERSION); + NOTE("%s config version %s vs confd %s, migrating ...", path, file_ver, CONFD_VERSION); mkpath(backup_dir, 0770); chown(backup_dir, 0, 10); /* root:wheel */ @@ -499,16 +385,11 @@ static int maybe_migrate(const char *path) snprintf(backup, sizeof(backup), "%s/%s", backup_dir, basenm(path)); rc = systemf("migrate -i -b \"%s\" \"%s\"", backup, path); if (rc) - SRPLG_LOG_ERR("confd", "Migration of %s failed (rc=%d)", path, rc); + ERROR("Migration of %s failed (rc=%d)", path, rc); return rc; } -static int file_exists(const char *path) -{ - return access(path, F_OK) == 0; -} - /* * Load a JSON config file into the running datastore. * Mirrors what sysrepocfg -I does: lyd_parse_data() + sr_replace_config(). @@ -531,18 +412,17 @@ static int load_config(sr_conn_ctx_t *conn, sr_session_ctx_t *sess, ly_in_new_memory(empty, &in); } else if (lyrc) { - error_print(0, "Failed to open \"%s\" for reading", path); + ERROR("Failed to open \"%s\" for reading", path); sr_release_context(conn); return -1; } lyrc = lyd_parse_data(ly_ctx, NULL, in, LYD_JSON, - LYD_PARSE_NO_STATE | LYD_PARSE_ONLY | LYD_PARSE_STRICT, - 0, &data); + LYD_PARSE_NO_STATE | LYD_PARSE_ONLY | LYD_PARSE_STRICT, 0, &data); ly_in_free(in, 1); if (lyrc) { - SRPLG_LOG_ERR("confd", "Parsing %s failed", path); + ERROR("Parsing %s failed", path); sr_release_context(conn); return -1; } @@ -551,7 +431,7 @@ static int load_config(sr_conn_ctx_t *conn, sr_session_ctx_t *sess, r = sr_replace_config(sess, NULL, data, timeout_ms); if (r != SR_ERR_OK) { - SRPLG_LOG_ERR("confd", "sr_replace_config failed: %s", sr_strerror(r)); + ERROR("sr_replace_config failed: %s", sr_strerror(r)); return -1; } @@ -569,14 +449,14 @@ static int export_running(sr_session_ctx_t *sess, const char *path, uint32_t tim r = sr_get_data(sess, "/*", 0, timeout_ms, 0, &data); if (r != SR_ERR_OK) { - SRPLG_LOG_ERR("confd", "sr_get_data failed: %s", sr_strerror(r)); + ERROR("sr_get_data failed: %s", sr_strerror(r)); return -1; } umask(0006); fp = fopen(path, "w"); if (!fp) { - SRPLG_LOG_ERR("confd", "Failed to open %s for writing: %s", path, strerror(errno)); + ERRNO("Failed to open %s for writing", path); sr_release_data(data); return -1; } @@ -599,21 +479,21 @@ static void handle_startup_failure(sr_session_ctx_t *sess, const char *failure_p { int r; - SRPLG_LOG_ERR("confd", "Failed loading startup-config, reverting to Fail Secure mode!"); + ERROR("Failed loading startup-config, reverting to Fail Secure mode!"); /* Reset to factory-default */ r = sr_copy_config(sess, NULL, SR_DS_FACTORY_DEFAULT, timeout_ms); if (r != SR_ERR_OK) { - SRPLG_LOG_ERR("confd", "sr_copy_config(factory-default) failed: %s", sr_strerror(r)); + ERROR("sr_copy_config(factory-default) failed: %s", sr_strerror(r)); /* Nuclear option: wipe everything */ systemf("rm -f /etc/sysrepo/data/*startup* /etc/sysrepo/data/*running* /dev/shm/sr_*"); return; } /* Load failure-config on top */ - if (file_exists(failure_path)) { + if (fexist(failure_path)) { if (load_config(conn, sess, failure_path, timeout_ms)) { - SRPLG_LOG_ERR("confd", "Failed loading failure-config, aborting!"); + ERROR("Failed loading failure-config, aborting!"); banner_append("CRITICAL ERROR: Logins are disabled, no credentials available"); systemf("initctl -nbq runlevel 9"); return; @@ -628,10 +508,10 @@ static void handle_startup_failure(sr_session_ctx_t *sess, const char *failure_p */ static void maybe_enable_test_mode(void) { - if (file_exists("/mnt/aux/test-mode")) { + if (fexist("/mnt/aux/test-mode")) { int rc; - conout(3, "Enbling test mode"); + conout(3, "Enabling test mode"); rc = systemf("sysrepoctl -c infix-test -e test-mode-enable"); conout(rc ? 1 : 0, "\n"); } @@ -652,86 +532,78 @@ static int bootstrap_config(sr_conn_ctx_t *conn, sr_session_ctx_t *sess, int r; /* Test mode support */ - if (file_exists("/mnt/aux/test-mode")) { - if (file_exists("/mnt/aux/test-override-startup")) { + if (fexist("/mnt/aux/test-mode")) { + if (fexist("/mnt/aux/test-override-startup")) { unlink("/mnt/aux/test-override-startup"); config_path = startup_path; } else { - SRPLG_LOG_INF("confd", "Test mode detected, switching to test-config"); + NOTE("Test mode detected, switching to test-config"); config_path = test_path; } } else { config_path = startup_path; } - if (file_exists(config_path)) { + if (fexist(config_path)) { /* Run migration if needed */ maybe_migrate(config_path); /* Load startup (or test) config */ - SRPLG_LOG_INF("confd", "Loading %s ...", config_path); + NOTE("Loading %s ...", config_path); if (load_config(conn, sess, config_path, timeout_ms)) { handle_startup_failure(sess, failure_path, conn, timeout_ms); return 0; /* continue running even in fail-secure */ } - SRPLG_LOG_INF("confd", "Loaded %s successfully, syncing startup datastore.", config_path); + NOTE("Loaded %s successfully, syncing startup datastore.", config_path); sr_session_switch_ds(sess, SR_DS_STARTUP); r = sr_copy_config(sess, NULL, SR_DS_RUNNING, timeout_ms); sr_session_switch_ds(sess, SR_DS_RUNNING); if (r != SR_ERR_OK) - SRPLG_LOG_WRN("confd", "Failed to sync startup datastore: %s", sr_strerror(r)); + WARN("Failed to sync startup datastore: %s", sr_strerror(r)); return 0; } /* First boot: no startup-config, initialize from factory */ - SRPLG_LOG_INF("confd", "startup-config missing, initializing from factory-config"); + NOTE("startup-config missing, initializing from factory-config"); r = sr_copy_config(sess, NULL, SR_DS_FACTORY_DEFAULT, timeout_ms); if (r != SR_ERR_OK) { - SRPLG_LOG_ERR("confd", "sr_copy_config(factory-default) failed: %s", sr_strerror(r)); + ERROR("sr_copy_config(factory-default) failed: %s", sr_strerror(r)); return -1; } /* Export running → startup file */ if (export_running(sess, startup_path, timeout_ms)) - SRPLG_LOG_WRN("confd", "Failed to export running to %s", startup_path); + WARN("Failed to export running to %s", startup_path); return 0; } -static void *gen_config_thread(void *arg) -{ - (void)arg; - return (void *)(intptr_t)systemf("/usr/libexec/confd/gen-config"); -} - int main(int argc, char **argv) { + const char *failure_path = "/etc/failure-config.cfg"; + const char *startup_path = "/cfg/startup-config.cfg"; + const char *factory_path = "/etc/factory-config.cfg"; + const char *test_path = "/etc/test-config.cfg"; + int log_opts = LOG_PID | LOG_NDELAY; + int rc = EXIT_FAILURE, opt, i, r; + sr_session_ctx_t *sess = NULL; struct plugin *plugins = NULL; sr_conn_ctx_t *conn = NULL; - sr_session_ctx_t *sess = NULL; - sr_log_level_t log_level = SR_LL_ERR; - int plugin_count = 0, i, r, rc = EXIT_FAILURE, opt, debug = 0; - int pidfd = -1, fatal_fail = 0; - pthread_t tid; - void *tret; - const char *pidfile = NULL; - const char *factory_path = "/etc/factory-config.cfg"; - const char *startup_path = "/cfg/startup-config.cfg"; - const char *failure_path = "/etc/failure-config.cfg"; - const char *test_path = "/etc/test-config.cfg"; + int log_level = LOG_ERR; + pid_t gen_pid, pump_pid; uint32_t timeout_s = 60; + int plugin_count = 0; + int fatal_fail = 0; uint32_t timeout_ms; + int status; struct option options[] = { {"help", no_argument, NULL, 'h'}, {"version", no_argument, NULL, 'V'}, {"verbosity", required_argument, NULL, 'v'}, - {"debug", no_argument, NULL, 'd'}, - {"foreground", no_argument, NULL, 'n'}, - {"pid-file", required_argument, NULL, 'p'}, {"fatal-plugin-fail", no_argument, NULL, 'f'}, {"factory-config", required_argument, NULL, 'F'}, {"startup-config", required_argument, NULL, 'S'}, @@ -741,44 +613,31 @@ int main(int argc, char **argv) }; opterr = 0; - while ((opt = getopt_long(argc, argv, "hVv:dnp:fF:S:E:t:", options, NULL)) != -1) { + while ((opt = getopt_long(argc, argv, "hVv:fF:S:E:t:", options, NULL)) != -1) { switch (opt) { case 'h': version_print(); help_print(); - rc = EXIT_SUCCESS; - goto cleanup; + return EXIT_SUCCESS; case 'V': version_print(); - rc = EXIT_SUCCESS; - goto cleanup; + return EXIT_SUCCESS; case 'v': if (!strcmp(optarg, "none")) - log_level = SR_LL_NONE; + log_level = LOG_EMERG; else if (!strcmp(optarg, "error")) - log_level = SR_LL_ERR; + log_level = LOG_ERR; else if (!strcmp(optarg, "warning")) - log_level = SR_LL_WRN; + log_level = LOG_WARNING; else if (!strcmp(optarg, "info")) - log_level = SR_LL_INF; + log_level = LOG_NOTICE; else if (!strcmp(optarg, "debug")) - log_level = SR_LL_DBG; - else if (strlen(optarg) == 1 && optarg[0] >= '0' && optarg[0] <= '4') - log_level = atoi(optarg); + log_level = LOG_DEBUG; else { - error_print(0, "Invalid verbosity \"%s\"", optarg); - goto cleanup; + fprintf(stderr, "confd error: Invalid verbosity \"%s\"\n", optarg); + return EXIT_FAILURE; } break; - case 'd': - debug = 1; - break; - case 'n': - debug = -1; - break; - case 'p': - pidfile = optarg; - break; case 'f': fatal_fail = 1; break; @@ -795,35 +654,44 @@ int main(int argc, char **argv) timeout_s = (uint32_t)atoi(optarg); break; default: - error_print(0, "Invalid option or missing argument: -%c", optopt); - goto cleanup; + fprintf(stderr, "confd error: Invalid option or missing argument: -%c\n", optopt); + return EXIT_FAILURE; } } if (optind < argc) { - error_print(0, "Redundant parameters"); - goto cleanup; + fprintf(stderr, "confd error: Redundant parameters\n"); + return EXIT_FAILURE; } timeout_ms = timeout_s * 1000; - if (pidfile && (pidfd = open_pidfile(pidfile)) < 0) - goto cleanup; + nice(-20); + signal(SIGPIPE, SIG_IGN); - /* Load plugins from disk (dlopen) before daemonizing */ + if (getenv("DEBUG")) { + log_opts |= LOG_PERROR; + debug = 1; + } + openlog("confd", log_opts, LOG_DAEMON); + setlogmask(LOG_UPTO(log_level)); + + pidfile(NULL); + + /* Load plugins from disk (dlopen) */ if (load_plugins(&plugins, &plugin_count)) - error_print(0, "load_plugins failed (continuing)"); + ERROR("load_plugins failed (continuing)"); - /* Daemonize -- after this point, confd no longer logs to stderr */ - daemon_init(debug, log_level); - - /* Start gen-config in parallel -- thread is joined before we need the result */ + /* Start gen-config in parallel — child is reaped before we need the result */ conout(3, "Generating factory-config and failure-config"); - if (pthread_create(&tid, NULL, gen_config_thread, NULL)) { - SRPLG_LOG_ERR("confd", "Failed to create gen-config thread: %s", strerror(errno)); + gen_pid = fork(); + if (gen_pid < 0) { + ERRNO("Failed to fork gen-config"); conout(1, "\n"); goto cleanup; } + if (gen_pid == 0) + _exit(systemf("/usr/libexec/confd/gen-config")); /* Phase 1: Wipe stale SHM for a clean slate */ wipe_sysrepo_shm(); @@ -831,25 +699,25 @@ int main(int argc, char **argv) /* Phase 2: Connect to sysrepo (rebuilds SHM from installed YANG modules) */ r = sr_connect(0, &conn); if (r != SR_ERR_OK) { - error_print(r, "Failed to connect"); + ERROR("Failed to connect: %s", sr_strerror(r)); goto cleanup; } - /* Phase 3: Wait for gen-config thread to finish */ - pthread_join(tid, &tret); - if ((intptr_t)tret != 0) { - SRPLG_LOG_ERR("confd", "gen-config failed (rc=%d)", (int)(intptr_t)tret); + /* Phase 3: Wait for gen-config to finish */ + waitpid(gen_pid, &status, 0); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { + ERROR("gen-config failed (status=%d)", status); conout(1, "\n"); goto cleanup; } conout(0, "\n"); /* Phase 4: Install factory defaults into all datastores */ - SRPLG_LOG_INF("confd", "Loading factory-default datastore from %s ...", factory_path); + NOTE("Loading factory-default datastore from %s ...", factory_path); conout(3, "Loading factory-default datastore"); r = sr_install_factory_config(conn, factory_path); if (r != SR_ERR_OK) { - SRPLG_LOG_ERR("confd", "sr_install_factory_config failed: %s", sr_strerror(r)); + ERROR("sr_install_factory_config failed: %s", sr_strerror(r)); conout(1, "\n"); goto cleanup; } @@ -858,7 +726,7 @@ int main(int argc, char **argv) /* Phase 5: Start running-datastore session */ r = sr_session_start(conn, SR_DS_RUNNING, &sess); if (r != SR_ERR_OK) { - error_print(r, "Failed to start new session"); + ERROR("Failed to start new session: %s", sr_strerror(r)); goto cleanup; } @@ -869,7 +737,7 @@ int main(int argc, char **argv) * what the plugin callbacks expect. */ r = sr_replace_config(sess, NULL, NULL, timeout_ms); if (r != SR_ERR_OK) { - SRPLG_LOG_ERR("confd", "Failed to clear running datastore: %s", sr_strerror(r)); + ERROR("Failed to clear running datastore: %s", sr_strerror(r)); goto cleanup; } @@ -881,14 +749,13 @@ int main(int argc, char **argv) for (i = 0; i < plugin_count; i++) { r = plugins[i].init_cb(sess, &plugins[i].private_data); if (r) { - SRPLG_LOG_ERR("confd", "Plugin \"%s\" initialization failed (%s).", - plugins[i].name, sr_strerror(r)); + ERROR("Plugin \"%s\" initialization failed (%s).", plugins[i].name, sr_strerror(r)); if (fatal_fail) { conout(1, "\n"); goto cleanup; } } else { - SRPLG_LOG_INF("confd", "Plugin \"%s\" initialized.", plugins[i].name); + NOTE("Plugin \"%s\" initialized.", plugins[i].name); plugins[i].initialized = 1; } } @@ -897,43 +764,36 @@ int main(int argc, char **argv) /* Phase 8: Collect subscription contexts from plugins */ for (i = 0; i < plugin_count; i++) { if (plugins[i].initialized && plugins[i].get_subs) - plugins[i].get_subs(plugins[i].private_data, - &plugins[i].sub, &plugins[i].fsub); + plugins[i].get_subs(plugins[i].private_data, &plugins[i].sub, &plugins[i].fsub); } - /* Phase 9: Start event pump thread for bootstrap. + /* Phase 9: Fork event pump process for bootstrap. * With SR_SUBSCR_NO_THREAD, sr_replace_config() blocks waiting - * for callbacks. The pump thread processes those events. */ - struct event_pump pump = { - .plugins = plugins, - .plugin_count = plugin_count, - .running = 1, - }; - pthread_t pump_tid; - - if (pthread_create(&pump_tid, NULL, event_pump_thread, &pump)) { - SRPLG_LOG_ERR("confd", "Failed to create event pump thread: %s", strerror(errno)); + * for callbacks. The pump process processes those events. */ + pump_pid = fork(); + if (pump_pid < 0) { + ERRNO("Failed to fork event pump"); goto cleanup; } + if (pump_pid == 0) + event_pump(plugins, plugin_count); - /* - * Phase 10: Load startup config -- plugins are now subscribed, so + /* Phase 10: Load startup config -- plugins are now subscribed, so * sr_replace_config() will trigger their change callbacks. - * The event pump thread processes those callbacks. - */ + * The event pump process processes those callbacks. */ conout(3, "Loading startup-config"); if (bootstrap_config(conn, sess, factory_path, startup_path, failure_path, test_path, timeout_ms)) { - pump.running = 0; - pthread_join(pump_tid, NULL); + kill(pump_pid, SIGTERM); + waitpid(pump_pid, NULL, 0); conout(1, "\n"); goto cleanup; } conout(0, "\n"); /* Phase 11: Stop event pump — bootstrap is done */ - pump.running = 0; - pthread_join(pump_tid, NULL); + kill(pump_pid, SIGTERM); + waitpid(pump_pid, NULL, 0); /* No more progress to show, go to quiet daemon mode */ quiet_now(); @@ -941,15 +801,11 @@ int main(int argc, char **argv) /* Signal that bootstrap is complete (dbus, resolvconf depend on this) */ symlink("/run/finit/cond/reconf", "/run/finit/cond/usr/bootstrap"); - /* Write PID file after everything is ready */ - if (pidfile && write_pidfile(pidfd) < 0) - goto cleanup; - - /* Phase 12: Steady-state — libev event loop replaces pthread_cond_wait */ + /* Phase 12: Steady-state — libev event loop */ { - struct ev_loop *loop = EV_DEFAULT; struct ev_signal sigterm_w, sigint_w, sighup_w, sigquit_w; struct ev_io io_watchers[MAX_EVENT_FDS]; + struct ev_loop *loop = EV_DEFAULT; int nio = 0; ev_signal_init(&sigterm_w, signal_cb, SIGTERM); @@ -995,11 +851,6 @@ cleanup: } free(plugins); - if (pidfd >= 0) { - close(pidfd); - unlink(pidfile); - } - sr_disconnect(conn); return rc; }