From 0ee3159ac66622e95ddf41cb07feb564de2e202d Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Tue, 17 Mar 2026 09:02:29 +0100 Subject: [PATCH] confd: replace initctl shell-outs with direct finit_ C API Add a finit_enable/disable/reload() family in core.c that directly manipulates Finit's service state without fork+exec overhead: finit_enable(svc) -- create symlink in /etc/finit.d/enabled/ finit_disable(svc) -- remove symlink from /etc/finit.d/enabled/ finit_delete(svc) -- remove both symlink and service entirely finit_reload(svc) -- utimensat() on .conf to schedule reload Printf-style variants (finit_enablef/disablef/reloadf) handle template instance names such as container@foo and hostapd@wlan0. All systemf("initctl ... enable/disable/touch ...") call sites across containers, dhcp-server, firewall, hardware, ntp, routing, services, syslog, and system are converted to the new API. As a related cleanup in services.c, drop the remaining srx_enabled() calls in favour of reading the already-fetched config tree directly via lydx_is_enabled(lydx_get_xpathf(config, ...)), eliminating the last sysrepo round-trips from that module. Signed-off-by: Joachim Wiberg --- src/confd/src/containers.c | 11 +-- src/confd/src/core.c | 125 +++++++++++++++++++++++++++++++++- src/confd/src/core.h | 15 ++++ src/confd/src/dhcp-client.c | 9 +-- src/confd/src/dhcp-server.c | 2 +- src/confd/src/dhcpv6-client.c | 9 +-- src/confd/src/firewall.c | 6 +- src/confd/src/hardware.c | 10 +-- src/confd/src/ntp.c | 2 +- src/confd/src/routing.c | 10 +-- src/confd/src/services.c | 27 ++++---- src/confd/src/syslog.c | 8 +-- src/confd/src/system.c | 6 +- 13 files changed, 192 insertions(+), 48 deletions(-) diff --git a/src/confd/src/containers.c b/src/confd/src/containers.c index 59eae7fb..f8f219b7 100644 --- a/src/confd/src/containers.c +++ b/src/confd/src/containers.c @@ -322,9 +322,12 @@ static int add(const char *name, struct lyd_node *cif) fchmod(fileno(fp), 0700); fclose(fp); - systemf("initctl -bnq touch container@%s.conf", name); - systemf("initctl -bnq %s container@%s.conf", lydx_is_enabled(cif, "enabled") - ? "enable" : "disable", name); + if (lydx_is_enabled(cif, "enabled")) { + finit_enablef("container@%s", name); + finit_reloadf("container@%s", name); + } else { + finit_disablef("container@%s", name); + } return 0; } @@ -341,7 +344,7 @@ static int del(const char *name) FILE *pp; erasef("%s/%s.sh", _PATH_CONT, name); - systemf("initctl -bnq disable container@%s.conf", name); + finit_disablef("container@%s", name); /* Schedule a cleanup job for this container as soon as it has stopped */ snprintf(prune_dir, sizeof(prune_dir), "%s/%s", _PATH_CLEAN, name); diff --git a/src/confd/src/core.c b/src/confd/src/core.c index dd035a3a..5e4fddaa 100644 --- a/src/confd/src/core.c +++ b/src/confd/src/core.c @@ -9,6 +9,129 @@ struct confd confd; +/* + * Touch a Finit service .conf file to schedule a synchronized reload. + * Equivalent to 'initctl touch ' but without the fork+exec overhead. + * The service name should be given without the .conf suffix. + */ +int finit_reload(const char *svc) +{ + char path[256]; + + /* Prefer the enabled/ symlink -- that's what Finit watches */ + snprintf(path, sizeof(path), FINIT_RCSD "/enabled/%s.conf", svc); + if (!utimensat(AT_FDCWD, path, NULL, AT_SYMLINK_NOFOLLOW)) + return 0; + + snprintf(path, sizeof(path), FINIT_RCSD "/available/%s.conf", svc); + if (!utimensat(AT_FDCWD, path, NULL, AT_SYMLINK_NOFOLLOW)) + return 0; + + ERRNO("failed marking %s for reload", svc); + return -1; +} + +int finit_reloadf(const char *fmt, ...) +{ + char svc[64]; + va_list ap; + + va_start(ap, fmt); + vsnprintf(svc, sizeof(svc), fmt, ap); + va_end(ap); + + return finit_reload(svc); +} + +int finit_enable(const char *svc) +{ + char src[256], dst[256]; + const char *at; + + snprintf(src, sizeof(src), FINIT_RCSD "/available/%s.conf", svc); + if (!fexist(src)) { + /* Template instance (e.g. container@foo): point to base template */ + at = strchr(svc, '@'); + if (at) + snprintf(src, sizeof(src), FINIT_RCSD "/available/%.*s@.conf", + (int)(at - svc), svc); + } + + snprintf(dst, sizeof(dst), FINIT_RCSD "/enabled/%s.conf", svc); + if (symlink(src, dst) && errno != EEXIST) { + ERRNO("failed enabling %s", svc); + return -1; + } + return 0; +} + +int finit_disable(const char *svc) +{ + char path[256]; + + snprintf(path, sizeof(path), FINIT_RCSD "/enabled/%s.conf", svc); + if (remove(path) && errno != ENOENT) { + ERRNO("failed disabling %s", svc); + return -1; + } + return 0; +} + +int finit_delete(const char *svc) +{ + char path[256]; + + snprintf(path, sizeof(path), FINIT_RCSD "/enabled/%s.conf", svc); + if (remove(path) && errno != ENOENT) { + ERRNO("failed removing enabled symlink for %s", svc); + return -1; + } + + snprintf(path, sizeof(path), FINIT_RCSD "/available/%s.conf", svc); + if (remove(path) && errno != ENOENT) { + ERRNO("failed removing available conf for %s", svc); + return -1; + } + + return 0; +} + +int finit_deletef(const char *fmt, ...) +{ + char svc[64]; + va_list ap; + + va_start(ap, fmt); + vsnprintf(svc, sizeof(svc), fmt, ap); + va_end(ap); + + return finit_delete(svc); +} + +int finit_enablef(const char *fmt, ...) +{ + char svc[64]; + va_list ap; + + va_start(ap, fmt); + vsnprintf(svc, sizeof(svc), fmt, ap); + va_end(ap); + + return finit_enable(svc); +} + +int finit_disablef(const char *fmt, ...) +{ + char svc[64]; + va_list ap; + + va_start(ap, fmt); + vsnprintf(svc, sizeof(svc), fmt, ap); + va_end(ap); + + return finit_disable(svc); +} + static int startup_save(sr_session_ctx_t *session, uint32_t sub_id, const char *model, const char *xpath, sr_event_t event, unsigned request_id, void *priv) @@ -433,7 +556,7 @@ static int change_cb(sr_session_ctx_t *session, uint32_t sub_id, const char *mod client = srx_enabled(session, "/ietf-system:system/ntp/enabled"); server = lydx_get_xpathf(config, "/ietf-ntp:ntp") != NULL; - systemf("initctl -nbq %s chronyd", client || server ? "enable" : "disable"); + (client || server) ? finit_enable("chronyd") : finit_disable("chronyd"); } if (cfg) diff --git a/src/confd/src/core.h b/src/confd/src/core.h index 9262fde6..f25d91bd 100644 --- a/src/confd/src/core.h +++ b/src/confd/src/core.h @@ -4,11 +4,14 @@ #define CONFD_CORE_H_ #include +#include +#include #include #include #include #include #include +#include #include #include #include @@ -31,6 +34,8 @@ #include "dagger.h" +#define FINIT_RCSD "/etc/finit.d" + #define SSH_HOSTKEYS "/etc/ssh/hostkeys" #define SSH_HOSTKEYS_NEXT SSH_HOSTKEYS"+" #define SSL_CERT_DIR "/etc/ssl/certs" @@ -188,6 +193,16 @@ static inline int register_rpc(sr_session_ctx_t *session, const char *xpath, } +/* core.c */ +int finit_enable(const char *svc); +int finit_disable(const char *svc); +int finit_delete(const char *svc); +int finit_reload(const char *svc); +int finit_enablef(const char *fmt, ...) __attribute__((format(printf, 1, 2))); +int finit_disablef(const char *fmt, ...) __attribute__((format(printf, 1, 2))); +int finit_deletef(const char *fmt, ...) __attribute__((format(printf, 1, 2))); +int finit_reloadf(const char *fmt, ...) __attribute__((format(printf, 1, 2))); + /* interfaces.c */ int interfaces_change(sr_session_ctx_t *session, struct lyd_node *config, struct lyd_node *diff, sr_event_t event, struct confd *confd); int interfaces_cand_init(struct confd *confd); diff --git a/src/confd/src/dhcp-client.c b/src/confd/src/dhcp-client.c index 1d0f347a..2ef8d1cc 100644 --- a/src/confd/src/dhcp-client.c +++ b/src/confd/src/dhcp-client.c @@ -99,7 +99,7 @@ static void add(const char *ifname, struct lyd_node *cfg) const char *metric = lydx_get_cattr(cfg, "route-preference"); const char *client_id = lydx_get_cattr(cfg, "client-id"); char *cid = NULL, *options = NULL; - const char *action = "disable"; + int ena = 0; const char *vendor_class; char vendor[128] = { 0 }; char do_arp[20] = { 0 }; @@ -153,9 +153,10 @@ static void add(const char *ifname, struct lyd_node *cfg) options ? "-o " : "", options, ifname, cid ?: "", vendor, ifname); fclose(fp); - action = "enable"; + ena = 1; err: - systemf("initctl -bfqn %s dhcp-client-%s", action, ifname); + ena ? finit_enablef("dhcp-client-%s", ifname) + : finit_disablef("dhcp-client-%s", ifname); if (options) free(options); if (cid) @@ -164,7 +165,7 @@ err: static void del(const char *ifname) { - systemf("initctl -bfq delete dhcp-client-%s", ifname); + finit_deletef("dhcp-client-%s", ifname); } int dhcp_client_change(sr_session_ctx_t *session, struct lyd_node *config, struct lyd_node *diff, diff --git a/src/confd/src/dhcp-server.c b/src/confd/src/dhcp-server.c index cdf641f3..8e464d09 100644 --- a/src/confd/src/dhcp-server.c +++ b/src/confd/src/dhcp-server.c @@ -377,7 +377,7 @@ int dhcp_server_change(sr_session_ctx_t *session, struct lyd_node *config, struc err_done: if (added || deleted) - system("initctl -nbq touch dnsmasq"); + finit_reload("dnsmasq"); return err; } diff --git a/src/confd/src/dhcpv6-client.c b/src/confd/src/dhcpv6-client.c index d2942627..69746acf 100644 --- a/src/confd/src/dhcpv6-client.c +++ b/src/confd/src/dhcpv6-client.c @@ -80,7 +80,7 @@ static void add_v6(const char *ifname, struct lyd_node *cfg) const char *metric = lydx_get_cattr(cfg, "route-preference"); const char *duid = lydx_get_cattr(cfg, "duid"); char *client_duid = NULL, *options = NULL; - const char *action = "disable"; + int ena = 0; const char *addr_mode = "-N try"; /* Default: stateful mode */ char prefix_del[16] = { 0 }; bool request_pd = false; @@ -127,9 +127,10 @@ static void add_v6(const char *ifname, struct lyd_node *cfg) options ?: "", client_duid ?: "", ifname, ifname); fclose(fp); - action = "enable"; + ena = 1; err: - systemf("initctl -bfqn %s dhcpv6-client-%s", action, ifname); + ena ? finit_enablef("dhcpv6-client-%s", ifname) + : finit_disablef("dhcpv6-client-%s", ifname); if (options) free(options); if (client_duid) @@ -138,7 +139,7 @@ err: static void del_v6(const char *ifname) { - systemf("initctl -bfq delete dhcpv6-client-%s", ifname); + finit_deletef("dhcpv6-client-%s", ifname); } int dhcpv6_client_change(sr_session_ctx_t *session, struct lyd_node *config, struct lyd_node *diff, diff --git a/src/confd/src/firewall.c b/src/confd/src/firewall.c index b6653d87..f563a38f 100644 --- a/src/confd/src/firewall.c +++ b/src/confd/src/firewall.c @@ -510,7 +510,7 @@ int firewall_change(sr_session_ctx_t *session, struct lyd_node *config, struct l case SR_EV_DONE: if (!fisdir(FIREWALLD_DIR_NEXT)) { /* Firewall is disabled */ - systemf("initctl -nbq disable firewalld"); + finit_disable("firewalld"); return SR_ERR_OK; } @@ -521,8 +521,8 @@ int firewall_change(sr_session_ctx_t *session, struct lyd_node *config, struct l return SR_ERR_SYS; } - systemf("initctl -nbq touch firewalld"); - systemf("initctl -nbq enable firewalld"); + finit_reload("firewalld"); + finit_enable("firewalld"); return SR_ERR_OK; default: diff --git a/src/confd/src/hardware.c b/src/confd/src/hardware.c index 11fa7ed4..5a5be02c 100644 --- a/src/confd/src/hardware.c +++ b/src/confd/src/hardware.c @@ -685,14 +685,14 @@ int hardware_change(sr_session_ctx_t *session, struct lyd_node *config, struct l ap_interfaces++; if (running) - systemf("initctl -bfq touch hostapd@%s", name); + finit_reloadf("hostapd@%s", name); else - systemf("initctl -bfq enable hostapd@%s", name); + finit_enablef("hostapd@%s", name); } } } if (!ap_interfaces) { - systemf("initctl -bfq disable hostapd@%s", name); + finit_disablef("hostapd@%s", name); erasef(HOSTAPD_CONF, name); erasef(HOSTAPD_CONF_NEXT, name); } @@ -784,10 +784,10 @@ int hardware_change(sr_session_ctx_t *session, struct lyd_node *config, struct l if (fexist(GPSD_CONF_NEXT)) { unlink(GPSD_CONF); rename(GPSD_CONF_NEXT, GPSD_CONF); - systemf("initctl -nbq enable gpsd"); + finit_enable("gpsd"); } else { unlink(GPSD_CONF); - systemf("initctl -nbq disable gpsd"); + finit_disable("gpsd"); } break; } diff --git a/src/confd/src/ntp.c b/src/confd/src/ntp.c index 618de339..818138de 100644 --- a/src/confd/src/ntp.c +++ b/src/confd/src/ntp.c @@ -48,7 +48,7 @@ static int change(sr_session_ctx_t *session, struct lyd_node *config, struct lyd if (systemf("chronyc reload sources >/dev/null 2>&1")) ERRNO("Failed reloading chronyd sources"); - systemf("initctl -nbq touch chronyd"); + finit_reload("chronyd"); return SR_ERR_OK; default: diff --git a/src/confd/src/routing.c b/src/confd/src/routing.c index 8c01c932..c9b2a24d 100644 --- a/src/confd/src/routing.c +++ b/src/confd/src/routing.c @@ -605,17 +605,17 @@ activate: /* Enable/disable FRR daemons as standalone finit services. * Harmless no-op when using watchfrr (services not installed). */ - systemf("initctl -nbq %s ospfd", ospfd_enabled ? "enable" : "disable"); + ospfd_enabled ? finit_enable("ospfd") : finit_disable("ospfd"); if (ospfd_enabled) - systemf("initctl -nbq touch ospfd"); - systemf("initctl -nbq %s ripd", ripd_enabled ? "enable" : "disable"); - systemf("initctl -nbq %s bfdd", bfdd_enabled ? "enable" : "disable"); + finit_reload("ospfd"); + ripd_enabled ? finit_enable("ripd") : finit_disable("ripd"); + bfdd_enabled ? finit_enable("bfdd") : finit_disable("bfdd"); /* * Signal netd to reload - it assembles /etc/frr/frr.conf and * Finit propagates the restart to the frr sysv service */ - if (systemf("initctl -bfq touch netd")) + if (finit_reload("netd")) ERROR("Failed to signal netd for reload"); return rc; diff --git a/src/confd/src/services.c b/src/confd/src/services.c index 4c50cd9b..7a8870e7 100644 --- a/src/confd/src/services.c +++ b/src/confd/src/services.c @@ -276,8 +276,8 @@ static int put(sr_data_t *cfg) /* * Enable or disable a named service: manage nginx symlinks and mDNS * records, then start or stop via initctl. Does NOT touch (restart) - * the service -- call 'initctl touch ' separately when only - * config changes and the service is already running. + * the service -- call finit_reload() separately when only config + * changes and the service is already running. */ static void svc_enable(int ena, svc type, const char *svcname) { @@ -297,7 +297,7 @@ static void svc_enable(int ena, svc type, const char *svcname) systemf("rm -f /etc/nginx/app/%s.conf", svcname); } - systemf("initctl -nbq %s %s", ena ? "enable" : "disable", svcname); + ena ? finit_enable(svcname) : finit_disable(svcname); if (type != none) mdns_records(ena ? MDNS_ADD : MDNS_DELETE, type); @@ -397,7 +397,7 @@ static int mdns_change(sr_session_ctx_t *session, struct lyd_node *config, struc svc_enable(ena, none, "mdns-alias"); } if (ena) - systemf("initctl -nbq touch avahi"); + finit_reload("avahi"); return put(cfg); } @@ -456,9 +456,9 @@ static int lldp_change(sr_session_ctx_t *session, struct lyd_node *config, struc int lldp_ena = lydx_is_enabled(lldp, "enabled"); if (lydx_get_xpathf(diff, LLDP_XPATH "/enabled")) - systemf("initctl -nbq %s lldpd", lldp_ena ? "enable" : "disable"); + lldp_ena ? finit_enable("lldpd") : finit_disable("lldpd"); else if (lldp_ena) - systemf("initctl -nbq touch lldpd"); + finit_reload("lldpd"); } break; @@ -489,7 +489,7 @@ static int ttyd_change(sr_session_ctx_t *session, struct lyd_node *config, struc ena = lydx_is_enabled(srv, "enabled") && lydx_is_enabled(lydx_get_xpathf(config, WEB_XPATH), "enabled"); svc_enable(ena, ttyd, NULL); - systemf("initctl -nbq touch nginx"); + finit_reload("nginx"); return put(cfg); } @@ -524,7 +524,8 @@ static int netbrowse_change(sr_session_ctx_t *session, struct lyd_node *config, lydx_is_enabled(lydx_get_xpathf(config, WEB_XPATH), "enabled"); svc_enable(ena, netbrowse, NULL); mdns_alias_conf(ena); - systemf("initctl -nbq touch nginx mdns-alias"); + finit_reload("nginx"); + finit_reload("mdns-alias"); return put(cfg); } @@ -545,7 +546,7 @@ static int restconf_change(sr_session_ctx_t *session, struct lyd_node *config, s ena = lydx_is_enabled(srv, "enabled") && lydx_is_enabled(lydx_get_xpathf(config, WEB_XPATH), "enabled"); svc_enable(ena, restconf, "restconf"); - systemf("initctl -nbq touch nginx"); + finit_reload("nginx"); return put(cfg); } @@ -566,9 +567,9 @@ static int ssh_change(sr_session_ctx_t *session, struct lyd_node *config, struct int ssh_ena = lydx_is_enabled(ssh, "enabled"); if (lydx_get_xpathf(diff, SSH_XPATH "/enabled")) - systemf("initctl -nbq %s sshd", ssh_ena ? "enable" : "disable"); + ssh_ena ? finit_enable("sshd") : finit_disable("sshd"); else if (ssh_ena) - systemf("initctl -nbq touch sshd"); + finit_reload("sshd"); } return SR_ERR_OK; case SR_EV_ENABLED: @@ -685,7 +686,7 @@ static int web_change(sr_session_ctx_t *session, struct lyd_node *config, struct /* Certificate changed: regenerate ssl.conf and reload nginx */ if (lydx_get_xpathf(diff, WEB_XPATH "/certificate")) { web_ssl_conf(srv, config); - systemf("initctl -nbq touch nginx"); + finit_reload("nginx"); } /* Web master on/off: propagate to nginx and all sub-services */ @@ -699,7 +700,7 @@ static int web_change(sr_session_ctx_t *session, struct lyd_node *config, struct restconf, "restconf"); svc_enable(ena, web, "nginx"); mdns_alias_conf(nb_ena); - systemf("initctl -nbq touch mdns-alias"); + finit_reload("mdns-alias"); } return put(cfg); diff --git a/src/confd/src/syslog.c b/src/confd/src/syslog.c index af8921fb..8f621dce 100644 --- a/src/confd/src/syslog.c +++ b/src/confd/src/syslog.c @@ -344,7 +344,7 @@ static int file_change(sr_session_ctx_t *session, struct lyd_node *config,struct } srx_free_changes(tree); - systemf("initctl -nbq touch sysklogd"); + finit_reload("sysklogd"); return SR_ERR_OK; } @@ -377,7 +377,7 @@ static int remote_change(sr_session_ctx_t *session, struct lyd_node *config, str } } - systemf("initctl -nbq touch sysklogd"); + finit_reload("sysklogd"); return SR_ERR_OK; } @@ -410,7 +410,7 @@ static int rotate_change(sr_session_ctx_t *session, struct lyd_node *config, str } fclose(fp); - systemf("initctl -nbq touch sysklogd"); + finit_reload("sysklogd"); return SR_ERR_OK; } @@ -458,7 +458,7 @@ static int server_change(sr_session_ctx_t *session, struct lyd_node *config, str sr_free_values(list, count); fclose(fp); done: - systemf("initctl -nbq touch sysklogd"); + finit_reload("sysklogd"); return SR_ERR_OK; } diff --git a/src/confd/src/system.c b/src/confd/src/system.c index b0b3701a..1991e194 100644 --- a/src/confd/src/system.c +++ b/src/confd/src/system.c @@ -308,7 +308,7 @@ static int change_ntp_client(sr_session_ctx_t *session, struct lyd_node *config, (void)remove(NTP_CLIENT_CONF); systemf("rm -f /etc/chrony/sources.d/*"); /* Note: chronyd enable/disable is managed centrally in core.c */ - systemf("initctl -nbq touch chronyd"); + finit_reload("chronyd"); return SR_ERR_OK; } @@ -318,7 +318,7 @@ static int change_ntp_client(sr_session_ctx_t *session, struct lyd_node *config, } /* Note: chronyd enable/disable is managed centrally in core.c */ - systemf("initctl -nbq touch chronyd"); + finit_reload("chronyd"); return SR_ERR_OK; default: @@ -466,7 +466,7 @@ static int change_dns(sr_session_ctx_t *session, struct lyd_node *config, struct (void)rename(RESOLV_NEXT, RESOLV_CONF); } - systemf("initctl -bq touch resolvconf"); + finit_reload("resolvconf"); return SR_ERR_OK;