commit a514ecfd177a7e0843b548370bc8cd608e2d3ecc from: Stefan Sperling date: Fri Jun 02 11:51:06 2023 UTC add crontab commands: show crontab, crontab edit, crontab install For now, display and edit the crontab file of the root user only. The "show crontab" command displays the root user's crontab as shown by the crontab -l command. Editing is implemented via a new CTL handler which knows about the semantics of the crontab(1) command. If an NSH configuration contains crontab rules then the "crontab install" command appears in the running-config in order to install the crontab file managed by nsh to the system with crontab(1). commit - 015e4f4fbea6473a68e4556b811f26161299464b commit + a514ecfd177a7e0843b548370bc8cd608e2d3ecc blob - 4a9419a0ddac0891c65b10ea7ab7e5150a51e051 blob + 89ae78b9bca7ee05828cec1bc00d99562a2fc8f4 --- commands.c +++ commands.c @@ -82,6 +82,7 @@ static int doconfig(int, char**); static int exitconfig(int, char**); int rtable(int, char**); int group(int, char**); +static int pr_crontab(int, char **, FILE *); static int pr_routes(int, char **); static int pr_routes6(int, char **); static int pr_arp(int, char **); @@ -448,6 +449,7 @@ Menu showlist[] = { { "monitor", "Monitor routing/arp table changes", CMPL0 0, 0, 0, 0, monitor }, { "version", "Software information", CMPL0 0, 0, 0, 0, version }, { "users", "System users", CMPL0 0, 0, 0, 0, who }, + { "crontab", "Scheduled background jobs", CMPL0 0, 0, 0, 0, pr_crontab }, { "running-config", "Operating configuration", CMPL0 0, 0, 0, 0, pr_conf }, { "startup-config", "Startup configuration", CMPL0 0, 0, 0, 0, pr_s_conf }, { "active-config", "Configuration of active context", CMPL0 0, 0, 0, 0, pr_a_conf }, @@ -1062,6 +1064,7 @@ static char tracerthelp[]; static char tracert6help[]; static char sshhelp[]; static char telnethelp[]; +static char crontabhelp[]; static char showhelp[]; static char whohelp[]; static char dohelp[]; @@ -1698,6 +1701,7 @@ static char tracert6help[] ="Print the route to IPv6 host", sshhelp[] = "SSH connection to remote host", telnethelp[] = "Telnet connection to remote host", + crontabhelp[] = "Configure scheduled background jobs", quithelp[] = "Close current connection", exithelp[] = "Leave configuration mode and return to privileged mode", verbosehelp[] = "Set verbose diagnostics", @@ -1755,6 +1759,7 @@ struct ghs mantab[] = { { "carp", "Search for tag carp", CMPL0 NULL, 0 }, { "config", "Search for tag config", CMPL0 NULL, 0 }, { "configure", "Search for tag configure", CMPL0 NULL, 0 }, + { "crontab", "Search for tag crontab", CMPL0 NULL, 0 }, { "csh", "Search for tag csh", CMPL0 NULL, 0 }, { "ddb", "Search for tag ddb", CMPL0 NULL, 0 }, { "dhcp", "Search for tag dhcp", CMPL0 NULL, 0 }, @@ -1939,6 +1944,7 @@ Command cmdtab[] = { { "tftp", tftphelp, CMPL(t) (char **)ctl_tftp, ssctl, ctlhandler, 1, 1, 0, 1 }, { "resolv", resolvhelp, CMPL(t) (char **)ctl_resolv, ssctl, ctlhandler, 1, 1, 0, 1 }, { "motd", motdhelp, CMPL(t) (char **)ctl_motd, ssctl, ctlhandler, 1, 1, 0, 1 }, + { "crontab", crontabhelp, CMPL(t) (char **)ctl_crontab, ssctl, ctlhandler, 1, 1, 0, 1 }, { "inet", inethelp, CMPL(t) (char **)ctl_inet, ssctl, ctlhandler, 1, 1, 0, 1 }, { "ping", pinghelp, CMPL0 0, 0, ping, 0, 0, 0, 0 }, { "ping6", ping6help, CMPL0 0, 0, ping6, 0, 0, 0, 0 }, @@ -3212,7 +3218,27 @@ done: if (conf_fd != -1) { unlink(confpath); close(conf_fd); + } + return 0; +} + +static int +pr_crontab(int argc, char **argv, FILE *outfile) +{ + char *crontab_argv[] = { CRONTAB, "-l", "-u", "root", NULL }; + + if (priv != 1) { + printf("%% Privilege required\n"); + return 0 ; } + + fprintf(outfile, "%% To view crontab syntax documentation in NSH, " + "run: !man 5 crontab\n\n"); + fflush(outfile); + + if (cmdargs_output(CRONTAB, crontab_argv, fileno(outfile), -1) != 0) + printf("%% crontab command failed\n"); + return 0; } blob - 224415e3acd5fe0a9b7ad75a76edc348fd0c37e5 blob + 84ff64c42be556f8392417b7ba5f35c45332fff5 --- conf.c +++ conf.c @@ -187,6 +187,7 @@ conf(FILE *output) { char cpass[_PASSWORD_LEN+1]; char hostbuf[MAXHOSTNAMELEN]; + off_t offset; fprintf(output, "!\n"); @@ -285,6 +286,11 @@ conf(FILE *output) conf_ctl(output, "", "ftp-proxy", 0); conf_ctl(output, "", "inet", 0); conf_ctl(output, "", "sshd", 0); + + offset = ftello(output); + conf_ctl(output, "", "crontab", 0); + if (offset != ftello(output)) /* we have custom crontab rules */ + fprintf(output, "crontab install\n"); conf_rtables(output); blob - b617dc27b60cd8856efb13d9b6dc0718eacfe45a blob + 1f41ae4718fad3b064aeebdc12d5f8fceec607d0 --- ctl.c +++ ctl.c @@ -32,7 +32,10 @@ static char table[16]; /* service routines */ +void edit_crontab(char *, char **, char *); +void install_crontab(char *, char **, char *); void call_editor(char *, char **, char *); +int edit_file(char *, mode_t, char *, char **); void ctl_symlink(char *, char *, char *); int rule_writeline(char *, mode_t, char *); int fill_tmpfile(char **, char *, char **); @@ -69,11 +72,22 @@ struct daemons ctl_daemons[] = { { "ldap", "LDAP", ctl_ldap, LDAPCONF_TEMP, 0600, 0, RT_TABLEID_MAX }, { "ifstate", "If state",ctl_ifstate, IFSTATECONF_TEMP,0600, 0, RT_TABLEID_MAX }, { "motd", "MOTD", ctl_motd, MOTD_TEMP,0644, 0, 0 }, +{ "crontab", "crontab", ctl_crontab, CRONTAB_TEMP, 0600, 0, 0 }, { 0, 0, 0, 0, 0, 0 } }; /* per-daemon commands, and their C or executable functions */ + +/* CRONTAB */ +struct ctl ctl_crontab[] = { + { "edit", "edit scheduled background jobs", + { "crontab", NULL, NULL }, edit_crontab, 0, T_HANDLER }, + { "install", "install scheduled background job config", + { "crontab", NULL, NULL }, install_crontab, 0, T_HANDLER }, + { 0, 0, { 0 }, 0, 0, 0 } +}; + /* MOTD */ struct ctl ctl_motd[] = { { "edit", "edit message-of-the-day", @@ -697,10 +711,62 @@ fill_tmpfile(char **fillargs, char *tmpfile, char **tm } void -call_editor(char *name, char **args, char *z) +edit_crontab(char *name, char **args, char *z) { + char *crontab_argv[] = { CRONTAB, "-u", "root", "-l", NULL }; + char tmpfile[PATH_MAX]; + int found = 0; + struct daemons *daemons; + int fd = -1; + + for (daemons = ctl_daemons; daemons->name != 0; daemons++) + if (strncmp(daemons->name, name, strlen(name)) == 0) { + found = 1; + break; + } + + if (!found) { + printf("%% edit_crontab internal error\n"); + return; + } + + snprintf(tmpfile, sizeof(tmpfile), "%s.%d", daemons->tmpfile, + cli_rtable); + + fd = open(tmpfile, O_RDWR | O_EXCL); + if (fd == -1) { + if (errno != ENOENT) { + printf("%% open %s: %s\n", tmpfile, strerror(errno)); + return; + } + fd = open(tmpfile, O_RDWR | O_CREAT | O_EXCL, daemons->mode); + if (fd == -1) { + printf("%% open %s: %s\n", tmpfile, strerror(errno)); + return; + } + + /* Populate temporary file with current crontab. */ + if (cmdargs_output(CRONTAB, crontab_argv, fd, -1) != 0) { + printf("%% crontab -l command failed\n"); + goto done; + } + } + + if (edit_file(tmpfile, daemons->mode, daemons->propername, args) == 0) { + crontab_argv[3] = tmpfile; + if (cmdargs(CRONTAB, crontab_argv) != 0) + printf("%% failed to install crontab\n"); + } +done: + close(fd); +} + +void +install_crontab(char *name, char **args, char *z) +{ + char *crontab_argv[] = { CRONTAB, "-u", "root", NULL, NULL }; + char tmpfile[PATH_MAX]; int fd, found = 0; - char *editor, tmpfile[64]; struct daemons *daemons; for (daemons = ctl_daemons; daemons->name != 0; daemons++) @@ -710,6 +776,35 @@ call_editor(char *name, char **args, char *z) } if (!found) { + printf("%% install_crontab internal error\n"); + return; + } + + snprintf(tmpfile, sizeof(tmpfile), "%s.%d", daemons->tmpfile, + cli_rtable); + + if ((fd = acq_lock(tmpfile)) > 0) { + crontab_argv[3] = tmpfile; + if (cmdargs(CRONTAB, crontab_argv) != 0) + printf("%% failed to install crontab\n"); + rls_lock(fd); + } +} + +void +call_editor(char *name, char **args, char *z) +{ + int found = 0; + char tmpfile[64]; + struct daemons *daemons; + + for (daemons = ctl_daemons; daemons->name != 0; daemons++) + if (strncmp(daemons->name, name, strlen(name)) == 0) { + found = 1; + break; + } + + if (!found) { printf("%% call_editor internal error\n"); return; } @@ -717,6 +812,16 @@ call_editor(char *name, char **args, char *z) snprintf(tmpfile, sizeof(tmpfile), "%s.%d", daemons->tmpfile, cli_rtable); + edit_file(tmpfile, daemons->mode, daemons->propername, args); +} + +int +edit_file(char *tmpfile, mode_t mode, char *propername, char **args) +{ + char *editor; + int fd; + int ret = 0; + /* acq lock, call editor, test config with cmd and args, release lock */ if ((editor = getenv("VISUAL")) == NULL) { if ((editor = getenv("EDITOR")) == NULL) @@ -724,14 +829,22 @@ call_editor(char *name, char **args, char *z) } if ((fd = acq_lock(tmpfile)) > 0) { char *argv[] = { editor, tmpfile, NULL }; - cmdargs(editor, argv); - chmod(tmpfile, daemons->mode); - if (args != NULL) - cmdargs(args[0], args); + ret = cmdargs(editor, argv); + if (ret == 0 && chmod(tmpfile, mode) == -1) { + printf("%% chmod %o %s: %s\n", + mode, tmpfile, strerror(errno)); + ret = 1; + } + if (ret == 0 && args != NULL) + ret = cmdargs(args[0], args); rls_lock(fd); - } else + } else { printf ("%% %s configuration is locked for editing\n", - daemons->propername); + propername); + return 1; + } + + return ret; } int blob - 2ae9f799551bdf7081e8be62c03b4b7a8f97f104 blob + 23cea48dd84cd5bce7b4c64b2ebd91bbf33f4f0a --- ctl.h +++ ctl.h @@ -121,6 +121,7 @@ struct daemons { #define LDAPCONF_TEMP "/var/run/ldapd.conf" #define IFSTATECONF_TEMP "/var/run/ifstated.conf" #define MOTD_TEMP "/var/run/motd" +#define CRONTAB_TEMP "/var/run/crontab" /* ctl tests*/ extern char *ctl_bgp_test[]; @@ -178,5 +179,6 @@ extern struct ctl ctl_dns[]; extern struct ctl ctl_inet[]; extern struct ctl ctl_ldap[]; extern struct ctl ctl_motd[]; +extern struct ctl ctl_crontab[]; extern struct ctl ctl_resolv[]; void flag_x(char *, char *, int, char *); blob - 610184e1adf628ff1d5a11f25b77b38f302ad923 blob + a1f2ecea9d8252e146f3e086eba407199cdc685d --- externs.h +++ externs.h @@ -167,6 +167,7 @@ extern pid_t child; #define SSH "/usr/bin/ssh" #define PKILL "/usr/bin/pkill" #define DIFF "/usr/bin/diff" +#define CRONTAB "/usr/bin/crontab" #define REBOOT "/sbin/reboot" #define HALT "/sbin/halt" #define SU "/usr/bin/su" blob - 83288f6f5bc17abddbcf6da9eed5f016be75e964 blob + ba0b8399145657180f7d392d196a61fa97ee1911 --- nsh.8 +++ nsh.8 @@ -1680,7 +1680,34 @@ The .Ar options are documented in .Xr telnet 1 . +.Pp +.Tg crontab +.Ic crontab Op Cm edit | install .Pp +Edit the configuration of scheduled background jobs which are +managed by +.Xr cron 8 . +Only the crontab file of the root user can be edited. +See the +.Xr crontab 5 +man page for information about configuration file syntax. +.Pp +.Nm +stores a private copy of the root user's crontab in +.Pa /var/run/crontab.0 . +The +.Cm crontab edit +command edits this file and then installs it to the system by running the +.Xr crontab 1 +command. +.Pp +The +.Cm crontab install +command skips editing but otherwise has the same effect. +This command can be used to overwrite the system crontab in case it has +become out of sync with the copy managed by +.Nm . +.Pp .Tg reboot .Ic reboot .Pp @@ -1772,7 +1799,7 @@ nsh/no verbose .Op hostname | interface | autoconf | ip | inet | inet6 | route | route6\ | sadb | arp | ndp | vlan | kernel | bgp | ospf | ospf6 | pf | eigrp | rip\ | ldp | ike | ipsec | dvmrp | relay | dhcp | smtp | ldap | monitor\ - | version | users | running-config | startup-config |\&? | help + | version | users | crontab | running-config | startup-config |\&? | help .Pp The main diagnostic and informational command is 'show'. show without arguments displays the available diagnostic show sub commands. @@ -1815,6 +1842,7 @@ nsh(p)/show monitor Monitor routing/arp table changes version Software information users System users + crontab Scheduled background jobs running-config Operating configuration startup-config Startup configuration ? Options @@ -2616,6 +2644,15 @@ kernel: OpenBSD 7.1 (GENERIC.MP) #459: Mon Apr 4 18:1 deraadt@amd64.openbsd.org:/usr/src/sys/arch/amd64/compile/GENERIC.MP .Ed .Pp +.Ic show crontab +.Pp +Display the scheduled background jobs of the root user which are +managed by +.Xr cron 8 . +See the +.Xr crontab 5 +manual page for information about scheduling rules syntax. +.Pp .Ic show running-config .Pp Display the current running configuration on the system, including