rename ntpq dumpcfg command to "saveconfig".
require authentication for saveconfig.
"restrict ... nomodify" prevents saveconfig and :config.
"saveconfig ." shorthand to save to startup configuration file.
support strftime() substitution in saveconfig arg to timestamp
the output filename, for example "saveconfig %Y%m%d-%H%M%S.conf".
display saveconfig response message from ntpd in ntpq.
save output filename in "savedconfig" variable, fetched with ntpq -c
"rv 0 savedconfig".
document saveconfig in html/ntpq.html.
add ./configure --disable-saveconfig to build a smaller ntpd.
log saveconfig failures and successes to syslog.
bk: 4a999c25yGmWAgeJeMYlLfjAR9MezQ
+* [Bug 1293] make configuration dumper ready for release, specifically:
+* rename ntpq dumpcfg command to "saveconfig".
+* require authentication for saveconfig.
+* "restrict ... nomodify" prevents saveconfig and :config.
+* "saveconfig ." shorthand to save to startup configuration file.
+* support strftime() substitution in saveconfig arg to timestamp
+ the output filename, for example "saveconfig %Y%m%d-%H%M%S.conf".
+* display saveconfig response message from ntpd in ntpq.
+* save output filename in "savedconfig" variable, fetched with ntpq -c
+ "rv 0 savedconfig".
+* document saveconfig in html/ntpq.html.
+* add ./configure --disable-saveconfig to build a smaller ntpd.
+* log saveconfig failures and successes to syslog.
(4.2.5p206) 2009/08/26 Released by Harlan Stenn <stenn@ntp.org>
* accopt.html typo fixes from Dave Mills.
* [Bug 1283] default to remembering KoD in sntp.
<p>Interactive format commands consist of a keyword followed by zero to four arguments. Only enough characters of the full keyword to uniquely identify the command need be typed. The output of a command is normally sent to the standard output, but optionally the output of individual commands may be sent to a file by appending a <tt>></tt>, followed by a file name, to the command line. A number of interactive format commands are executed entirely within the <tt>ntpq</tt> program itself and do not result in NTP mode-6 requests being sent to a server. These are described following.</p>
<dl>
<dt><tt>? [<i>command_keyword</i>]</tt><br>
- <tt>helpl [<i>command_keyword</i>]</tt>
+ <tt>help [<i>command_keyword</i>]</tt>
<dd>A <tt>?</tt> by itself will print a list of all the command keywords known to <tt>ntpq</tt>. A <tt>?</tt> followed by a command keyword will print function and usage information about the command.
<dt><tt>addvars <i>name</i> [ = <i>value</i>] [...]</tt><br>
<tt>rmvars <i>name</i> [...]</tt><br>
<td>event count (see <a href="decode.html#peer">peer status word</a>)</td>
</tr>
</table>
- <dt><tt>clockvar [<i>assocID</i>] [<i>name</i> [ = <i>value</i> [...]] [...]</tt>
- <dt><tt>cv [<i>assocID</i>] [<i>name</i> [ = <i>value</i> [...] ][...]</tt>
+ <dt><tt>clockvar [<i>assocID</i>] [<i>name</i> [ = <i>value</i> [...]] [...]</tt><br>
+ <tt>cv [<i>assocID</i>] [<i>name</i> [ = <i>value</i> [...] ][...]</tt>
<dd>Display a list of <a href="#clock">clock variables</a> for those assocations supporting a reference clock.
+ <dt><tt>:config [...]</tt>
+ <dd>Send the remainder of the command line, including whitespace, to the server as a run-time configuration command in the same format as the configuration file. This command is experimental until further notice and clarification. Authentication is of course required.
+ <dt><tt>config-from-file <i>filename</i></tt>
+ <dd>Send the each line of <i>filename</i> to the server as run-time configuration commands in the same format as the configuration file. This command is experimental until further notice and clarification. Authentication is of course required.
<dt><tt>keyid</tt>
<dd>Specify the key ID to use for write requests.<dt><tt>lassociations</tt>
<dd>Perform the same function as the associations command, execept display mobilized and unmobilized associations.<dt><tt>mreadvar <i>assocID</i> <i>assocID</i> [ <i>variable_name</i> [ = <i>value</i>[ ... ]</tt><br>
</table>
<dt id="rv"><tt>readvar <i>assocID</i> <i>name</i> [ = <i>value</i> ] [,...]</tt><br>
<tt>rv <i>assocID</i> [ <i>name</i> ] [,...]</tt>
- <dd>Display the specified variables. If the association ID is omitted or is given as zero, the variables are from the <a href="#system">system variables</a> name space, otherwise they are from the <a href="#peer">peer variables</a> name space. If no name is included, all operative variables in the name space are displayed. Multiple names are specified with comma separators and without whitespace. Note that time values are represented in milliseconds and frequency values in parts-per-million (PPM). Some NTP timestamps are represented in the format YYYYMMDDTTTT, where YYYY is the year, MM the month of year, DD the day of month and TTTT the time of day.<dt><tt>writevar <i>assocID</i> <i>name</i> = <i>value</i> [,...]</tt>
- <dd>Write the specified variables. If the association ID is omitted or is given as zero, the variables are from the <a href="#system">system variables</a> name space, otherwise they are from the <a href="#peer">peer variables</a> name space.
- <dt><tt>:config [...]</tt>
- <dd>Send the remainder of the command line, including whitespace, to the server as a run-time configuration command in the same format as the configuration file. This command is experimental until further notice and clarification. Authentication is of course required.</dl>
+ <dd>Display the specified variables. If the association ID is omitted or is given as zero, the variables are from the <a href="#system">system variables</a> name space, otherwise they are from the <a href="#peer">peer variables</a> name space. If no name is included, all operative variables in the name space are displayed. Multiple names are specified with comma separators and without whitespace. Note that time values are represented in milliseconds and frequency values in parts-per-million (PPM). Some NTP timestamps are represented in the format YYYYMMDDTTTT, where YYYY is the year, MM the month of year, DD the day of month and TTTT the time of day.
+ <dt><tt>saveconfig <i>filename</i></tt>
+ <dd>Write the current configuration, including any runtime modifications given with <tt>:config</tt> or <tt>config-from-file</tt>, to the ntpd host's file <i>filename</i>. A single period given for <i>filename</i> is shorthand for the startup configuration file. <i>filename</i> can use strftime() format specifiers to substitute the current date and time, for example, <tt>saveconfig ntp-%Y%m%d-%H%M%S.conf</tt>. The filename used is stored in system variable <tt>savedconfig</tt>. Authentication is required.
+ <dt><tt>writevar <i>assocID</i> <i>name</i> = <i>value</i> [,...]</tt>
+ <dd>Write the specified variables. If the association ID is omitted or is given as zero, the variables are from the <a href="#system">system variables</a> name space, otherwise they are from the <a href="#peer">peer variables</a> name space.</dl>
<h4 id="status">Status Words and Kiss Codes</h4>
<p>The current state of the operating program is shown in a set of status words maintained by the system and each association separately. These words are displayed in the <tt>rv</tt> and <tt>as</tt> commands both in hexadecimal and decoded short tip strings. The codes, tips and short explanations are on the <a href="decode.html">Event Messages and Status Words</a> page. The page also includes a list of system and peer messages, the code for the latest of which is included in the status word.</p>
<p>Information resulting from protocol machine state transitions is displayed using an informal set of ASCII strings called <a href="decode.html#kiss">kiss codes</a>. The original purpose was for kiss-o'-death (KoD) packets sent by the server to advise the client of an unusual condition. They are now displayed, when appropriate, in the reference identifier field in various billboards.</p>
<h4 id="system">System Variables</h4>
- <p>The following system variables apear in the <tt>rv</tt> billboard. Not all variables are displayed in some configurations.</p>
+ <p>The following system variables appear in the <tt>rv</tt> billboard. Not all variables are displayed in some configurations.</p>
<table width="100%" border="1" cellspacing="2" cellpadding="2">
<tr>
<td>Variable</td>
extern struct REMOTE_CONFIG_INFO remote_config;
void config_remotely(sockaddr_u *);
+#ifdef SAVECONFIG
int dump_config_tree(struct config_tree *ptree, FILE *df);
int dump_all_config_trees(FILE *df);
-
+#endif
#endif /* !defined(NTP_CONFIG_H) */
#define CTL_OP_WRITECLOCK 5 /* write clock variables */
#define CTL_OP_SETTRAP 6 /* set trap address */
#define CTL_OP_ASYNCMSG 7 /* asynchronous message */
-#define CTL_OP_CONFIGURE 8 /* configuration message */
+#define CTL_OP_CONFIGURE 8 /* runtime configuration */
+#define CTL_OP_SAVECONFIG 9 /* save config to file */
#define CTL_OP_UNSETTRAP 31 /* unset trap */
-#define CTL_OP_DUMPCONFIG 9
/*
* {En,De}coding of the system status word
extern int config_priority_override;
extern int config_priority;
#endif
-
extern char *ntp_signd_socket;
+extern struct config_tree *cfg_tree_history;
/* ntp_control.c */
extern int num_ctl_traps;
}
#endif /* DEBUG */
+
+#ifdef SAVECONFIG
/* Dump all trees */
int
dump_all_config_trees (
return 0;
}
-
+#endif /* SAVECONFIG */
/* FUNCTIONS FOR CREATING NODES ON THE SYNTAX TREE
static void
do_resolve_internal(void)
{
+#ifndef SYS_WINNT
int i;
+#endif
if (res_fp == NULL) {
/* belch */
static void set_trap (struct recvbuf *, int);
static void unset_trap (struct recvbuf *, int);
static void configure (struct recvbuf *, int);
-static void dump_config (struct recvbuf *, int);
+static void save_config (struct recvbuf *, int);
static struct ctl_trap *ctlfindtrap (sockaddr_u *,
struct interface *);
{ CTL_OP_WRITECLOCK, NOAUTH, write_clock_status },
{ CTL_OP_SETTRAP, NOAUTH, set_trap },
{ CTL_OP_UNSETTRAP, NOAUTH, unset_trap },
- { CTL_OP_DUMPCONFIG, NOAUTH, dump_config },
+ { CTL_OP_SAVECONFIG, AUTH, save_config },
{ CTL_OP_CONFIGURE, AUTH, configure },
{ NO_REQUEST, 0 }
};
}
/*
- * Call the config dumper
+ * save_config - Implements ntpq -c "saveconfig <filename>"
+ * Writes current configuration including any runtime
+ * changes by ntpq's :config or config-from-file
*/
void
-dump_config(
- struct recvbuf *rbufp,
- int restrict_mask
- )
+save_config(
+ struct recvbuf *rbufp,
+ int restrict_mask
+ )
{
- /* Dump config to file (for now) to ntp_dumpXXXXXXXXXX.conf */
- char fullpath[256];
- char filename[80];
- char reply[80];
+ char reply[128];
+#ifdef SAVECONFIG
+ char filespec[256];
+ char filename[256];
+ time_t now;
int fd;
FILE *fptr;
+ const char savedconfig_eq[] = "savedconfig=";
+ size_t octets;
+ char *savedconfig;
+#endif
- if (reqend - reqpt) {
- strncpy(filename, reqpt, sizeof(filename));
- filename[sizeof(filename) - 1] = 0;
- if (NULL != strchr(filename, '/')
- || NULL != strchr(filename, '\\'))
- snprintf(filename, sizeof(filename),
- "ntp_dump%i.conf", time(NULL));
- } else
- snprintf(filename, sizeof(filename), "ntp_dump%i.conf",
- time(NULL));
+ if (restrict_mask & RES_NOMODIFY) {
+ snprintf(reply, sizeof(reply),
+ "saveconfig prohibited by restrict ... nomodify");
+ ctl_putdata(reply, strlen(reply), 0);
+ ctl_flushpkt(0);
+ msyslog(LOG_NOTICE,
+ "saveconfig from %s rejected due to nomodify restriction",
+ stoa(&rbufp->recv_srcadr));
+ return;
+ }
-#ifndef SYS_WINNT
- snprintf(fullpath, sizeof(fullpath), "/var/tmp/%s", filename);
-#else
- snprintf(fullpath, sizeof(fullpath), "%s\\%s", getenv("TEMP"),
- filename);
-#endif
+#ifdef SAVECONFIG
+ if (0 == reqend - reqpt)
+ return;
+
+ strncpy(filespec, reqpt, sizeof(filespec));
+ filespec[sizeof(filespec) - 1] = '\0';
- fd = open(fullpath, O_CREAT | O_EXCL | O_WRONLY, S_IRUSR | S_IWUSR);
+ time(&now);
+
+ /*
+ * "saveconfig ." is shorthand for replacing the startup
+ * configuration file.
+ */
+ if ('.' == filespec[0] && '\0' == filespec[1]
+ && NULL != cfg_tree_history)
+ strncpy(filename, cfg_tree_history->source.value.s,
+ sizeof(filename));
+ /*
+ * allow timestamping of the saved config filename with
+ * strftime() format such as:
+ * ntpq -c "saveconfig ntp-%Y%m%d-%H%M%S.conf"
+ */
+ else if (0 == strftime(filename, sizeof(filename), filespec,
+ localtime(&now)))
+ strncpy(filename, filespec, sizeof(filename));
+
+ filename[sizeof(filename) - 1] = '\0';
+
+
+ fd = open(filename, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR);
if (-1 == fd)
fptr = NULL;
else
fptr = fdopen(fd, "w");
- if (NULL == fptr || -1 == dump_all_config_trees(fptr))
- snprintf(reply, sizeof(reply), "Couldn't dump to file %s",
- fullpath);
- else
- snprintf(reply, sizeof(reply), "Dumped to config file %s",
- fullpath);
+ if (NULL == fptr || -1 == dump_all_config_trees(fptr)) {
+ snprintf(reply, sizeof(reply),
+ "Unable to save configuration to file %s",
+ filename);
+ msyslog(LOG_ERR,
+ "saveconfig %s from %s failed", filename,
+ stoa(&rbufp->recv_srcadr));
+ } else {
+ snprintf(reply, sizeof(reply),
+ "Configuration saved to %s",
+ filename);
+ msyslog(LOG_NOTICE,
+ "Configuration saved to %s (requested by %s)",
+ filename, stoa(&rbufp->recv_srcadr));
+ /*
+ * save the output filename in system variable
+ * savedconfig, retrieved with:
+ * ntpq -c "rv 0 savedconfig"
+ */
+ octets = sizeof(savedconfig_eq) + strlen(filename) + 1;
+ savedconfig = emalloc(sizeof(savedconfig_eq)
+ + strlen(filename) + 1);
+ snprintf(savedconfig, octets, "%s%s",
+ savedconfig_eq, filename);
+ set_sys_var(savedconfig, octets, RO);
+ }
if (NULL != fptr)
fclose(fptr);
+#else /* !SAFECONFIG follows */
+ snprintf(reply, sizeof(reply),
+ "saveconfig unavailable, configured with --disable-saveconfig");
+#endif
ctl_putdata(reply, strlen(reply), 0);
ctl_flushpkt(0);
return;
}
+ if (restrict_mask & RES_NOMODIFY) {
+ snprintf(remote_config.err_msg,
+ sizeof(remote_config.err_msg),
+ "runtime configuration prohibited by restrict ... nomodify");
+ ctl_putdata(remote_config.err_msg,
+ strlen(remote_config.err_msg), 0);
+ ctl_flushpkt(0);
+ msyslog(LOG_NOTICE,
+ "runtime config from %s rejected due to nomodify restriction",
+ stoa(&rbufp->recv_srcadr));
+ return;
+ }
+
/* Initialize the remote config buffer */
data_count = reqend - reqpt;
memcpy(remote_config.buffer, reqpt, data_count);
ntp_intres(void)
{
FILE *in;
- struct timeval tv;
- fd_set fdset;
#ifdef SYS_WINNT
DWORD rc;
#else
int rc;
+ struct timeval tv;
+ fd_set fdset;
#endif
#ifdef DEBUG
int flag
)
{
- struct interface *interf;
-
#ifndef SO_EXCLUSIVEADDRUSE
+ struct interface *interf;
for (interf = inter_list;
interf != NULL;
static void doopeers (int, FILE *, int);
static void opeers (struct parse *, FILE *);
static void lopeers (struct parse *, FILE *);
-static void config (struct parse *, FILE *);
-static void dumpcfg (struct parse *, FILE *);
+static void config (struct parse *, FILE *);
+static void saveconfig (struct parse *, FILE *);
static void config_from_file (struct parse *, FILE *);
* Commands we understand. Ntpdc imports this.
*/
struct xcmd opcmds[] = {
- { "dumpcfg", dumpcfg, { NTP_STR, NO, NO, NO },
- { "dumpfile", "", "", ""},
- "dump ntp server configuration"},
+ { "saveconfig", saveconfig, { NTP_STR, NO, NO, NO },
+ { "filename", "", "", ""},
+ "save ntpd configuration to file, . for current config file"},
{ "associations", associations, { NO, NO, NO, NO },
{ "", "", "", "" },
"print list of association ID's and statuses for the server's peers" },
* * dumpcfg - dump ntp server configuration
*/
static void
-dumpcfg(
+saveconfig(
struct parse *pcmd,
FILE *fp
)
int dsize;
u_short rstatus;
- /* Is there a way to make an argument optional? */
- if(pcmd->nargs > 0)
- res = doquery(CTL_OP_DUMPCONFIG, 0, 0, strlen(pcmd->argval[0].string),
- pcmd->argval[0].string, &rstatus, &dsize, &datap);
+ if (0 == pcmd->nargs)
+ return;
+
+ res = doquery(CTL_OP_SAVECONFIG, 0, 1,
+ strlen(pcmd->argval[0].string),
+ pcmd->argval[0].string, &rstatus, &dsize,
+ &datap);
+
+ if (res != 0)
+ return;
+
+ if (0 == dsize)
+ fprintf(fp, "(no response message, curiously)");
else {
- res = doquery(CTL_OP_DUMPCONFIG, 0, 0, 0, (char *) 0,
- &rstatus, &dsize, &datap);
- printf("No filename supplied\n");
+ datap[dsize] = '\0';
+ fprintf(fp, "%s", datap);
}
}
*/
#pragma warning(disable: 4100) /* unreferenced formal parameter */
-#pragma warning(disable: 4101) /* unreferenced local variable */
#pragma warning(disable: 4127) /* conditional expression is constant */
#pragma warning(disable: 4996) /* more secure replacement available */
#define HAVE_RANDOM
#define MAXHOSTNAMELEN 64
#define AUTOKEY
+#define SAVECONFIG 1
/*
* Multimedia timer enable
DWORD comm_mask;
DWORD modem_status;
static const l_fp zero_time = { 0 };
- DWORD dwBytesReturned;
BOOL rc;
get_systime(&arrival_time);
{
struct recvbuf *buff = NULL;
recvbuf_t *newbuff;
- isc_boolean_t ignore_this;
l_fp arrival_time;
struct interface * inter = (struct interface *) i;