diff --git a/package/confd/confd.conf b/package/confd/confd.conf index c39ab50a..42385ad8 100644 --- a/package/confd/confd.conf +++ b/package/confd/confd.conf @@ -1,19 +1,12 @@ #set DEBUG=1 -# Phase 1: Generate factory/failure/test configs (shell) -run name:bootstrap log:prio:user.notice norestart \ - [S] /usr/libexec/confd/gen-config \ - -- Generating factory configuration - -run name:error :1 log:console norestart if: \ - [S] /usr/libexec/confd/error -- - -# Phase 2: Single daemon handles datastore init, config load, and plugins +# Single daemon handles gen-config, datastore init, config load, and plugins # log:prio:daemon.err -service name:confd env:/etc/default/confd \ - [S12345] confd -f -n -v warning -p /run/confd.pid \ +service log:console env:/etc/default/confd \ + [S12345] confd -f -n -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/bin/Makefile.am b/src/confd/bin/Makefile.am index 373ed27b..9df1ebd6 100644 --- a/src/confd/bin/Makefile.am +++ b/src/confd/bin/Makefile.am @@ -1,3 +1,3 @@ -pkglibexec_SCRIPTS = gen-hostname gen-interfaces gen-motd gen-hardware \ +pkglibexec_SCRIPTS = gen-config gen-hostname gen-interfaces gen-motd gen-hardware \ gen-version mstpd-wait-online wait-interface sbin_SCRIPTS = dagger migrate firewall diff --git a/src/confd/bin/error b/src/confd/bin/error deleted file mode 100755 index 4ed1ca3e..00000000 --- a/src/confd/bin/error +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh -# Override using an overlay in your br2-external to change the behavior - -logger -sik -p user.error "The device has reached an unrecoverable error, please RMA." diff --git a/src/confd/src/core.c b/src/confd/src/core.c index 4acce329..b2bdc843 100644 --- a/src/confd/src/core.c +++ b/src/confd/src/core.c @@ -431,11 +431,8 @@ static int change_cb(sr_session_ctx_t *session, uint32_t sub_id, const char *mod if (event == SR_EV_DONE) { /* skip reload in bootstrap, implicit reload in runlevel change */ - if (systemf("runlevel >/dev/null 2>&1")) { - /* trigger any tasks waiting for confd to have applied *-config */ - system("initctl -nbq cond set bootstrap"); + if (systemf("runlevel >/dev/null 2>&1")) return SR_ERR_OK; - } if (systemf("initctl -b reload")) { EMERG("initctl reload: failed applying new configuration!"); diff --git a/src/confd/src/main.c b/src/confd/src/main.c index dbf77e5a..38966a47 100644 --- a/src/confd/src/main.c +++ b/src/confd/src/main.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -74,6 +75,22 @@ static void error_print(int sr_error, const char *format, ...) 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"; + char buf[80]; + va_list ap; + + snprintf(buf, sizeof(buf), sta, cr, col[rc], msg[rc], fmt); + va_start(ap, fmt); + vfprintf(stderr, buf, ap); + va_end(ap); +} + static void version_print(void) { printf("confd - Infix configuration daemon v%s, compiled with libsysrepo v%s\n\n", @@ -158,10 +175,26 @@ static void handle_signals(void) sigaction(SIGTTOU, &action, NULL); } +static void quiet_now(void) +{ + int fd = -1; + + fd = open("/dev/null", O_RDWR, 0); + if (fd != -1) { + dup2(fd, STDIN_FILENO); + dup2(fd, STDOUT_FILENO); + 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; - int fd = -1; + + nice(-20); if (debug) { handle_signals(); @@ -192,14 +225,7 @@ static void daemon_init(int debug, sr_log_level_t log_level) exit(EXIT_FAILURE); } - fd = open("/dev/null", O_RDWR, 0); - if (fd != -1) { - dup2(fd, STDIN_FILENO); - dup2(fd, STDOUT_FILENO); - dup2(fd, STDERR_FILENO); - close(fd); - } - + quiet_now(); done: sr_log_syslog("confd", log_level); } @@ -514,6 +540,7 @@ static int export_running(sr_session_ctx_t *sess, const char *path, uint32_t tim return -1; } + umask(0006); fp = fopen(path, "w"); if (!fp) { SRPLG_LOG_ERR("confd", "Failed to open %s for writing: %s", path, strerror(errno)); @@ -525,11 +552,7 @@ static int export_running(sr_session_ctx_t *sess, const char *path, uint32_t tim fclose(fp); sr_release_data(data); - chown(path, 0, 0); /* root:root initially */ - chmod(path, 0660); - - /* Set group to 'wheel' for admin access */ - systemf("chgrp wheel '%s' 2>/dev/null", path); + chown(path, 0, 10); /* root:wheel for admin group access */ return 0; } @@ -572,8 +595,13 @@ 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")) - systemf("sysrepoctl -c infix-test -e test-mode-enable"); + if (file_exists("/mnt/aux/test-mode")) { + int rc; + + conout(3, "Enbling test mode"); + rc = systemf("sysrepoctl -c infix-test -e test-mode-enable"); + conout(rc ? 1 : 0, "\n"); + } } /* @@ -640,6 +668,12 @@ static int bootstrap_config(sr_conn_ctx_t *conn, sr_session_ctx_t *sess, 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) { struct plugin *plugins = NULL; @@ -648,6 +682,8 @@ int main(int argc, char **argv) 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"; @@ -748,6 +784,14 @@ int main(int argc, char **argv) /* 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 */ + 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)); + conout(1, "\n"); + goto cleanup; + } + /* Phase 1: Wipe stale SHM for a clean slate */ wipe_sysrepo_shm(); @@ -758,22 +802,34 @@ int main(int argc, char **argv) goto cleanup; } - /* Phase 3: Install factory defaults into all datastores */ + /* 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); + 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); + 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)); + conout(1, "\n"); goto cleanup; } + conout(0, "\n"); - /* Phase 4: Start running-datastore session */ + /* 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"); goto cleanup; } - /* Phase 4b: Clear running datastore so plugin init sees an empty + /* Phase 6: Clear running datastore so plugin init sees an empty * tree. This matches the original bootstrap flow where running * was cleared with '{}' before sysrepo-plugind started. When we * later load startup-config, the diff will be all-create which is @@ -787,25 +843,41 @@ int main(int argc, char **argv) /* Enable test-mode YANG feature if needed */ maybe_enable_test_mode(); - /* Phase 5: Initialize plugins (subscribe to YANG module changes) */ + /* Phase 7: Initialize plugins (subscribe to YANG module changes) */ + conout(3, "Loading confd plugins"); 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)); - if (fatal_fail) + if (fatal_fail) { + conout(1, "\n"); goto cleanup; + } } else { SRPLG_LOG_INF("confd", "Plugin \"%s\" initialized.", plugins[i].name); plugins[i].initialized = 1; } } + conout(0, "\n"); - /* Phase 6: Load startup config -- plugins are now subscribed, so - * sr_replace_config() will trigger their change callbacks. */ + /* + * Phase 8: Load startup config -- plugins are now subscribed, so + * sr_replace_config() will trigger their change callbacks. + */ + conout(3, "Loading startup-config"); if (bootstrap_config(conn, sess, factory_path, startup_path, - failure_path, test_path, timeout_ms)) + failure_path, test_path, timeout_ms)) { + conout(1, "\n"); goto cleanup; + } + conout(0, "\n"); + + /* No more progress to show, go to quiet daemon mode */ + quiet_now(); + + /* 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)