commit 8fff88de81a48b1dd32ae121542e09e3bc7de5bb from: Stefan Sperling date: Mon Oct 02 16:41:48 2023 UTC add rcctl framework commit - 1e0d215510627c58cc63bdfe4282801251ee9c75 commit + 8fff88de81a48b1dd32ae121542e09e3bc7de5bb blob - fea4dad3c0d4a46ec4cb57de7adf7c590c028fb0 blob + 96cf3b14b0c85458806353e72e65656a9bcdcdfb --- Makefile +++ Makefile @@ -29,7 +29,7 @@ SRCS+=ctl.c show.c if.c version.c route.c conf.c compl SRCS+=bridge.c tunnel.c media.c sysctl.c passwd.c pfsync.c carp.c SRCS+=trunk.c who.c more.c stringlist.c utils.c sqlite3.c ppp.c prompt.c SRCS+=nopt.c pflow.c wg.c nameserver.c ndp.c umb.c utf8.c cmdargs.c ctlargs.c -SRCS+=helpcommands.c makeargv.c hashtable.c mantab.c +SRCS+=helpcommands.c makeargv.c hashtable.c mantab.c rcctl.c CLEANFILES+=compile.c mantab.c LDADD=-lutil -ledit -ltermcap -lsqlite3 -L/usr/local/lib #-static blob - b2f75596c7a2341b035284c80351db2dd73c1944 blob + 8b7578ecc43d1ab35f18bb10d0cee8dd99f424e8 --- conf.c +++ conf.c @@ -92,7 +92,6 @@ int default_rxprio(char *); int default_llpriority(char *); int islateif(char *); int isdefaultroute(struct sockaddr *, struct sockaddr *); -int scantext(char *, char *); int ipv6ll_db_compare(struct sockaddr_in6 *, struct sockaddr_in6 *, char *); blob - 2e4977cfe088faa4a96086ab42f1262d2a78f9c1 blob + 94b17ae5e3a5d3c069e228469ed61a5106204519 --- externs.h +++ externs.h @@ -96,6 +96,7 @@ u_long default_mtu(char *); int conf_routes(FILE *, char *, int, int, int); int conf_dhcrelay(char *, char *, int); int dhcpleased_has_address(char *, const char *, const char *); +int scantext(char *, char *); /* show.c */ void p_rttables(int, u_int, int); @@ -187,6 +188,7 @@ extern pid_t child; #define HALT "/sbin/halt" #define SU "/usr/bin/su" #define DOAS "/usr/bin/doas" +#define RCCTL "/usr/sbin/rcctl" #define SAVESCRIPT "/usr/local/bin/save.sh" #ifndef DHCPLEASES #define DHCPLEASES "/var/db/dhcpd.leases" @@ -529,13 +531,16 @@ char *format_k(uint64_t amt); int db_create_table_rtables(void); int db_create_table_flag_x(char *); int db_create_table_nameservers(void); +int db_create_table_rcctl_flags(void); int db_insert_flag_x(char *, char *, int, int, char *); int db_insert_rtables(int, char *); +int db_insert_rcctl_flag(char *, char *, int, char *, char *); int db_insert_nameserver(char *); int db_delete_rtables_rtable(int); int db_delete_flag_x_ctl(char *, char *, int); int db_delete_flag_x_ctl_data(char *, char *, char *); int db_delete_nameservers(void); +int db_delete_rcctl_flag(char *, int, char *, char *); #ifdef _STRINGLIST_H int db_select_flag_x_ctl_data(StringList *, char *, char *, char *); int db_select_flag_x_ctl(StringList *, char *, char *); @@ -546,6 +551,9 @@ int db_select_name_rtable(StringList *, int); int db_select_flag_x_ctl_rtable(StringList *, char *, int); int db_select_flag_x_data_ctl_rtable(StringList *, char *, char *, int); int db_select_nameservers(StringList *); +int db_select_rcctl_options(StringList *, char *, int); +int db_select_rcctl_subcmd(StringList *, char *, int, char *); +int db_select_rcctl_optarg(StringList *, char *, int, char *); #endif int db_select_flag_x_dbflag_rtable(char *, char *, int); @@ -594,3 +602,17 @@ int hashtable_foreach(struct hashtable *, int (*cb)(void *, size_t, void *, size_t, void *), void *cb_arg); int hashtable_num_entries(struct hashtable *); + +/* rcctl.c */ +void rcctl_enable(char *, char *); +void rcctl_disable(char *, char *); +void rcctl_flag_clear(char *, char *, char *, char *); +void rcctl_flag_set(char *, char *, char *, char *); +void rcctl_flag_set_require_arg(char *, char *, char *, char *); +void rcctl_flag_replace(char *, char *, char *, char *, char *, char *); +void rcctl_flag_clear6(char *, char *, char *, char *, char *, char*); +void conf_rcctl(char *, char *, int, FILE *); +void rcctl_stop(char *); +void rcctl_start(char *); +int rcctl_has_status(char *, char *); +void rcctl_reload(char *, char *); blob - 5ac4ac0033e7778afc6cfc5541952605609804d1 blob + 5f21c7e2add436fe0866b6779663d4cc9886378a --- main.c +++ main.c @@ -172,6 +172,8 @@ create_db(void) printf("%% database pppoeipaddrmode creation failed\n"); if (db_create_table_flag_x("pin") < 0) printf("%% database pin creation failed\n"); + if (db_create_table_rcctl_flags() < 0) + printf("%% database rcctl flags creation failed\n"); } int blob - /dev/null blob + 141ecc46a5c345428227128f0e0031dda7815fc1 (mode 644) --- /dev/null +++ rcctl.c @@ -0,0 +1,407 @@ +/* + * Copyright (c) 2023 Stefan Sperling + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include + +#include "stringlist.h" +#include "externs.h" +#include "ctl.h" + +static int +is_enabled_in_db(char *nshcmd) +{ + int dbflag; + + dbflag = db_select_flag_x_dbflag_rtable("ctl", nshcmd, cli_rtable); + return (dbflag == DB_X_ENABLE || dbflag == DB_X_ENABLE_DEFAULT); +} + +void +rcctl_enable(char *daemon, char *nshcmd) +{ + if (is_enabled_in_db(nshcmd)) + return; + + if (interactive_mode) + printf("%% run \"%s reload\" to start %s\n", nshcmd, daemon); +} + +static int +is_disabled_in_db(char *nshcmd) +{ + int dbflag; + + dbflag = db_select_flag_x_dbflag_rtable("ctl", nshcmd, cli_rtable); + return (dbflag == DB_X_DISABLE || dbflag == DB_X_DISABLE_ALWAYS); +} + +void +rcctl_disable(char *daemon, char *nshcmd) +{ + if (is_disabled_in_db(nshcmd)) + return; + + if (interactive_mode) + printf("%% run \"%s reload\" to stop %s\n", nshcmd, daemon); +} + +void +rcctl_flag_clear(char *daemon, char *subcmd, char *option, char *optarg) +{ + if (db_delete_rcctl_flag(daemon, cli_rtable, option, optarg) < 0) + printf("%% database delete failure %s %s\n", daemon, option); +} + +/* + * Set an rcctl flag. Both the corresponding nsh sub command name and + * the option which rcctl should set must be specified. + * + * Sample ctl2 arguments array, where the option expects no argument: + * { "daemon", "subcmd", "-X", "", NULL } + * + * Sample ctl2 arguments array, where the option expects an argument: + * { "daemon", "subcmd", "-X", REQ, NULL } + */ +void +rcctl_flag_set(char *daemon, char *subcmd, char *option, char *optarg) +{ + /* prevent duplicate rows */ + rcctl_flag_clear(daemon, subcmd, option, optarg); + + if (db_insert_rcctl_flag(daemon, subcmd, cli_rtable, option, optarg) < 0) + printf("%% database insert failure: %s %s%s%s\n", daemon, option, + optarg ? " " : "", optarg ? optarg : ""); +} + +void +rcctl_flag_set_require_arg(char *daemon, char *subcmd, char *option, + char *optarg) +{ + if (optarg == NULL || optarg[0] == '\0') { + printf("%% missing argument to %s option\n", option); + return; + } + + rcctl_flag_set(daemon, subcmd, option, optarg); +} +/* + * Replace passing of one rcctl flag with another. + * For use with options that are mutually exclusive, such as -4/-6 in syslogd. + * + * Sample ctl2 arguments array, where both old and new options expect + * no arguments: { "daemon", "subcmd", "-X", "", "-Y", "", NULL } + * + * Sample ctl2 arguments array, where both old and new options expect + * a mandatory argument: { "daemon", "subcmd", "-X", REQ, "-Y", REQ, NULL } + */ +void +rcctl_flag_replace(char *daemon, char *subcmd, char *oldflag, char *oldvalue, + char *newflag, char *newvalue) +{ + rcctl_flag_clear(daemon, subcmd, oldflag, oldvalue); + rcctl_flag_set(daemon, subcmd, newflag, newvalue); +} + +/* + * Accept 6 arguments for symmetry with rcctl_flag_replace(), and behave + * just like rcctl_flag_clear(). + */ +void +rcctl_flag_clear6(char *daemon, char *subcmd, char *oldflag, char *oldvalue, + char *newflag, char *newvalue) +{ + rcctl_flag_clear(daemon, subcmd, oldflag, oldvalue); +} + +void +conf_rcctl(char *daemon, char *nshcmd, int rdomain, FILE *output) +{ + int dbflag; + int reload = 0; + int i; + StringList *options; + + dbflag = db_select_flag_x_dbflag_rtable("ctl", nshcmd, rdomain); + if (dbflag <= 0) { + struct daemons2 *daemons2; + struct ctl2 *ctl2; + + daemons2 = (struct daemons2 *)genget(nshcmd, + (char **)ctl_daemons2, sizeof(struct daemons2)); + if (daemons2 == NULL || Ambiguous(daemons2)) { + printf("%% Internal error - Invalid argument %s\n", nshcmd); + return; + } + + ctl2 = daemons2->table; + dbflag = ctl2->flag_x; /* default 'enabled' value */ + } + + switch (dbflag) { + case DB_X_ENABLE: + /* daemon is disabled by rc.d(8) unless explicitly enabled */ + fprintf(output, "%s enable\n", nshcmd); + reload = 1; + break; + case DB_X_ENABLE_DEFAULT: + /* daemon is enabled by rc.d(8) unless explicitly disabled */ + break; + case DB_X_DISABLE: + case DB_X_DISABLE_ALWAYS: + fprintf(output, "%s disable\n", nshcmd); + reload = 1; + break; + default: + printf("%% bad ctl %s flag 0x%x in database\n", + nshcmd, dbflag); + return; + } + + options = sl_init(); + + if (db_select_rcctl_options(options, daemon, cli_rtable) < 0) { + printf("%% database select failure: %s\n", daemon); + sl_free(options, 1); + return; + } + for (i = 0; i < options->sl_cur; i++) { + StringList *subcmd = sl_init(); + StringList *optarg = sl_init(); + char *option = options->sl_str[i]; + + if (db_select_rcctl_subcmd(subcmd, daemon, cli_rtable, option) < 0) { + printf("%% database select failure: %s %s\n", daemon, option); + continue; + } + + if (db_select_rcctl_optarg(optarg, daemon, cli_rtable, + option) < 0) { + printf("%% database select failure: %s %s\n", daemon, option); + sl_free(subcmd, 1); + continue; + } + + if (subcmd->sl_cur) { + if (optarg->sl_cur && optarg->sl_str[0][0] != '\0') { + fprintf(output, "%s %s \"%s\"\n", nshcmd, + subcmd->sl_str[0], optarg->sl_str[0]); + } else + fprintf(output, "%s %s\n", nshcmd, subcmd->sl_str[0]); + } + + reload = 1; + sl_free(subcmd, 1); + sl_free(optarg, 1); + } + + if (reload) + fprintf(output, "%s reload\n", nshcmd); +} + +static int +run_rcctl(char *argv[], int outfd) +{ + int ret; + + ret = cmdargs_output(RCCTL, argv, outfd, -1); + if (ret < 0) + printf("%% could not run %s\n", RCCTL); + return ret; +} + +static int +get_rcscript(char *buf, size_t bufsize, char *daemon) +{ + int ret; + + if (cli_rtable > 0) + ret = snprintf(buf, bufsize, "%s%d", daemon, cli_rtable); + else + ret = snprintf(buf, bufsize, "%s", daemon); + + if (ret < 0) { + printf("%% snprintf %s\n", strerror(errno)); + return -1; + } else if ((size_t)ret >= bufsize) { + printf("%% daemon name too long: %s\n", daemon); + return -1; + } + + return 0; +} + +void +rcctl_stop(char *daemon) +{ + char rcscript[64]; + char *stop_argv[] = { RCCTL, "stop", rcscript, NULL }; + + if (get_rcscript(rcscript, sizeof(rcscript), daemon)) + return; + + printf("%% stopping %s\n", rcscript); + run_rcctl(stop_argv, -1); +} + +void +rcctl_start(char *daemon) +{ + char rcscript[64]; + char *start_argv[] = { RCCTL, "start", rcscript, NULL }; + + if (get_rcscript(rcscript, sizeof(rcscript), daemon)) + return; + + printf("%% starting %s\n", rcscript); + run_rcctl(start_argv, -1); +} + +int +rcctl_has_status(char *daemon, char *status) +{ + char rcscript[64]; + char *argv[] = { RCCTL, "ls", status, NULL }; + char outpath[PATH_MAX]; + char text[128]; + int fd = -1, found = 0, len; + + if (get_rcscript(rcscript, sizeof(rcscript), daemon)) + return 0; + + len = snprintf(text, sizeof(text), "%s\n", rcscript); + if (len < 0) { + printf("%% snprintf: %s\n", strerror(errno)); + return 0; + } + if ((size_t)len >= sizeof(text)) { + printf("%% daemon name too long: %s\n", rcscript); + return 0; + } + + strlcpy(outpath, "/tmp/nsh-XXXXXX", sizeof(outpath)); + fd = mkstemp(outpath); + if (fd == -1) { + printf("%% mkstemp: %s\n", strerror(errno)); + return 0; + } + + /* rcctl will exit with code 1 if a daemon is matching */ + if (run_rcctl(argv, fd) == 1 && scantext(outpath, text)) + found = 1; + + unlink(outpath); + close(fd); + return found; +} + +void +rcctl_reload(char *daemon, char *nshcmd) +{ + char rcscript[64]; + char *setflags_argv[] = { RCCTL, "set", rcscript, "flags", "", NULL }; + char *reload_argv[] = { RCCTL, "reload", rcscript, NULL }; + char *enable_argv[] = { RCCTL, "enable", rcscript, NULL }; + char *disable_argv[] = { RCCTL, "disable", rcscript, NULL }; + StringList *dbflags, *flags; + char *s = NULL; + int i; + + if (get_rcscript(rcscript, sizeof(rcscript), daemon)) + return; + + if (is_disabled_in_db(nshcmd)) { + run_rcctl(disable_argv, -1); + if (rcctl_has_status(daemon, "rogue")) + rcctl_stop(daemon); + return; + } + + dbflags = sl_init(); + flags = sl_init(); + + if (db_select_rcctl_options(dbflags, daemon, cli_rtable) < 0) { + printf("%% database select failure: %s\n", daemon); + goto done; + } + + for (i = 0; i < dbflags->sl_cur; i++) { + char *option = dbflags->sl_str[i]; + StringList *optarg; + + optarg = sl_init(); + if (db_select_rcctl_optarg(optarg, daemon, cli_rtable, + option) < 0) { + printf("%% database select failure: %s %s\n", daemon, option); + sl_free(optarg, 1); + goto done; + } + + s = strdup(option); + if (s == NULL) { + printf("%% strdup: %s\n", strerror(errno)); + goto done; + } + sl_add(flags, s); + s = NULL; + + if (optarg->sl_cur) { + s = strdup(optarg->sl_str[0]); + if (s == NULL) { + printf("%% strdup: %s\n", strerror(errno)); + sl_free(optarg, 1); + goto done; + } + sl_add(flags, s); + s = NULL; + } + + sl_free(optarg, 1); + } + + s = sl_str(flags, " "); + if (s == NULL) { + printf("%% sl_str failure\n"); + goto done; + } + setflags_argv[4] = s; + + if (run_rcctl(enable_argv, -1) != 0) { + printf("%% could not enable %s\n", rcscript); + goto done; + } + + printf("%% setting %s flags: %s\n", rcscript, setflags_argv[4]); + if (run_rcctl(setflags_argv, -1) != 0) { + printf("%% could not set %s flags\n", rcscript); + goto done; + } + + if (rcctl_has_status(daemon, "failed")) { + rcctl_start(daemon); + } else { + printf("%% reloading %s\n", rcscript); + run_rcctl(reload_argv, -1); + } +done: + sl_free(dbflags, 1); + sl_free(flags, 1); + free(s); +} blob - edaaafa80021cd6627de6cf130ea941d0e55ed69 blob + 9921fb2860d5b5a5de6e088028d6959fca9e4a66 --- sqlite3.c +++ sqlite3.c @@ -52,6 +52,17 @@ db_create_table_flag_x(char *name) } int +db_create_table_rcctl_flags(void) +{ + char query[QSZ]; + + snprintf(query, QSZ, "CREATE TABLE IF NOT EXISTS rcctlflag " + "(daemon TEXT, subcmd TEXT, rtable INTEGER, " + "option TEXT, optarg TEXT)"); + return(sq3simple(query, NULL)); +} + +int db_insert_flag_x(char *name, char *ctl, int rtableid, int flag, char *data) { char query[QSZ]; @@ -61,7 +72,24 @@ db_insert_flag_x(char *name, char *ctl, int rtableid, return(sq3simple(query, NULL)); } +/* + * Add an option and its argument to the set of rcctl flags we pass to + * a given daemon. The sub-command nsh uses to represent the flag is + * recorded as well. This is useful e.g. when we generate an nshrc config + * which sets rcctl options via nsh commands. + */ int +db_insert_rcctl_flag(char *daemon, char *subcmd, int rtableid, + char *option, char *optarg) +{ + char query[QSZ]; + + snprintf(query, QSZ, "INSERT INTO rcctlflag VALUES('%s', '%s', " + "%d, '%s', '%s')", daemon, subcmd, rtableid, option, optarg); + return(sq3simple(query, NULL)); +} + +int db_insert_rtables(int rtableid, char *name) { char query[QSZ]; @@ -104,6 +132,24 @@ db_delete_flag_x_ctl_data(char *name, char *ctl, char char query[QSZ]; snprintf(query, QSZ, "DELETE FROM '%s' WHERE ctl='%s' AND data='%s'", name, ctl, data); + return(sq3simple(query, NULL)); +} + +/* Remove an option from the set of rcctl flags we pass to a given daemon .*/ +int +db_delete_rcctl_flag(char *daemon, int rtableid, char *option, char *optarg) +{ + char query[QSZ]; + + if (optarg && optarg[0] != '\0') { + snprintf(query, QSZ, "DELETE FROM rcctlflag WHERE daemon='%s' " + "AND rtable=%d AND option='%s' AND optarg='%s'", + daemon, rtableid, option, optarg); + } else { + snprintf(query, QSZ, "DELETE FROM rcctlflag WHERE daemon='%s' " + "AND rtable=%d AND option='%s'", daemon, rtableid, option); + } + return(sq3simple(query, NULL)); } @@ -134,7 +180,49 @@ db_select_flag_x_ctl(StringList *words, char *name, ch return(sq3simple(query, words)); } +/* Obtain the list of rcctl option flags we set for a given daemon. */ int +db_select_rcctl_options(StringList *words, char *daemon, int rtable) +{ + char query [QSZ]; + + snprintf(query, QSZ, "SELECT option FROM rcctlflag WHERE daemon='%s' " + "AND rtable=%d", daemon, rtable); + return(sq3simple(query, words)); +} + +/* + * Map an rcctl option flag to an nsh sub-command which sets the flag or + * clears the flag when "no ..." is used. + */ +int +db_select_rcctl_subcmd(StringList *words, char *daemon, int rtable, + char *option) +{ + char query [QSZ]; + + snprintf(query, QSZ, "SELECT subcmd FROM rcctlflag WHERE daemon='%s' " + "AND rtable=%d AND option='%s'", daemon, rtable, option); + return(sq3simple(query, words)); +} + +/* + * Look up an option argument for a given option. Not all options accept + * arguments but handling this is up to the caller (e.g. insert an empty + * string as optarg to represent "no argument"). + */ +int +db_select_rcctl_optarg(StringList *words, char *daemon, int rtable, + char *option) +{ + char query [QSZ]; + + snprintf(query, QSZ, "SELECT optarg FROM rcctlflag WHERE daemon='%s' " + "AND rtable=%d AND option='%s'", daemon, rtable, option); + return(sq3simple(query, words)); +} + +int db_select_rtable_rtables(StringList *words) { char query[]="SELECT rtable FROM rtables";