#! /usr/bin/python3
# -*- coding: utf-8 -*-
#
# ntpq - query an NTP server using mode 6 commands
#
# Freely translated from the old C ntpq code by ESR. The idea was to
# cleanly separate ntpq-that-was into a thin front-end layer handling
# mainly command interpretation and a back-end that presents the take
# from ntpd as objects that can be re-used by other front
# ends. Reusable pieces live in pylib.
#
# SPDX-License-Identifier: BSD-2-Clause
from __future__ import print_function, division
import cmd
import getopt
import os
import re
import resource
import socket
import sys
import time
try:
import ntp.control
import ntp.ntpc
import ntp.packet
import ntp.util
import ntp.poly
except ImportError as e:
sys.stderr.write(
"ntpq: can't find Python NTP library -- check PYTHONPATH.\n")
sys.stderr.write("%s\n" % e)
sys.exit(1)
# This used to force UTF-8 encoding, but that breaks the readline system.
# Unfortunately sometimes sys.stdout.encoding lies about the encoding,
# so expect random false positives.
ntp.util.check_unicode()
version = ntp.util.stdversion()
# Flags for forming descriptors.
OPT = 0x80 # this argument is optional, or'd with type */
NO = 0x0
NTP_STR = 0x1 # string argument
NTP_UINT = 0x2 # unsigned integer
NTP_INT = 0x3 # signed integer
NTP_ADD = 0x4 # IP network address
IP_VERSION = 0x5 # IP version
NTP_ADP = 0x6 # IP address and port
NTP_LFP = 0x7 # NTP timestamp
NTP_MODE = 0x8 # peer mode
NTP_2BIT = 0x9 # leap bits
NTP_FLOAT = 0xa # Float value
NTP_UPTIME = 0xb # uptime in daysD H:M:S (usually no frac)
NTP_PACKETS = 0xc # packet counts
class Ntpq(cmd.Cmd):
"ntpq command interpreter"
def __init__(self, session):
cmd.Cmd.__init__(self)
self.session = session
self.prompt = "ntpq> " if os.isatty(0) else ""
self.interactive = False # set to True when we should prompt
# self.auth_keyid = 0# Keyid used for authentication.
# self.auth_keytype = "NID_md5"# MD5 (FIXME: string value is a dummy)
# self.auth_hashlen = 16# MD5
# I do not know if the preceding are there for a specific reason
# so I am leaving them, and possibly duplicating them.
self.rawmode = False # Flag which indicates raw mode output.
self.directmode = False # Flag for direct MRU output.
self.showhostnames = 1 # If & 1, display names
self.showunits = False # If False, show old style float
self.auth_delay = 20 # delay time (default 20msec)
self.wideremote = False # show wide remote names?
self.ccmds = [] # Queued commands
self.chosts = [] # Command-line hosts
self.peers = [] # Data from NTP peers.
self.debug = 0
self.logfp = sys.stderr
self.pktversion = ntp.magic.NTP_OLDVERSION + 1
self.uservars = ntp.util.OrderedDict()
self.ai_family = socket.AF_UNSPEC
self.termwidth = ntp.util.termsize()[0]
def get_names(self):
# Overrides a method in Cmd
return [x.replace('hot_', ':').replace('config_from_file',
'config-from-file')
for x in dir(self.__class__)]
def emptyline(self):
"Called when an empty line is entered in response to the prompt."
pass
def precmd(self, line):
if line.startswith(":config"):
line = "hot_" + line[1:]
elif line.startswith("config-from-file"):
line = line.replace("config-from-file", "config_from_file")
return line
def default(self, line):
"Called on an input line when the command prefix is not recognized."
cmd, arg, line = self.parseline(line)
try:
dotext = 'do_'+cmd
cmdprefixlist = [a[3:] for a in self.get_names()
if a.startswith(dotext)]
if len(cmdprefixlist) == 1:
line = line.replace(cmd, cmdprefixlist[0])
cmd = cmdprefixlist[0]
elif len(cmdprefixlist) > 1:
self.warn("***Command `%s' ambiguous" % cmd)
return
elif not cmdprefixlist:
self.warn("***Command `%s' unknown" % cmd)
return
if cmd == "help" and arg:
helptext = 'help_'+arg
if helptext not in self.get_names():
argprefixlist = [a[5:] for a in self.get_names()
if a.startswith(helptext)]
if len(argprefixlist) == 1:
line = line.replace(arg, argprefixlist.pop())
elif len(argprefixlist) > 1:
self.warn("Command `%s' is ambiguous" % arg)
return
elif not argprefixlist:
self.warn("Command `%s' is unknown" % arg)
return
self.onecmd(line)
except TypeError:
self.warn("Command `%s' is unknown" % line)
def do_help(self, arg):
if arg:
helptext = 'help_'+arg
if helptext not in self.get_names():
argprefixlist = [a[5:] for a in self.get_names()
if a.startswith(helptext)]
if len(argprefixlist) == 1:
arg = argprefixlist.pop()
elif len(argprefixlist) > 1:
self.warn("Command `%s' is ambiguous." % arg)
return
cmd.Cmd.do_help(self, arg)
def say(self, msg):
try:
sys.stdout.write(ntp.poly.polyunicode(msg))
except UnicodeEncodeError as e:
self.warn("Unicode failure: %s" % str(e))
self.warn("msg:\n" + repr(msg))
raise e
sys.stdout.flush() # In case we're piping the output
def warn(self, msg):
sys.stderr.write(ntp.poly.polystr(msg) + "\n")
def help_help(self):
self.say("""\
function: tell the use and syntax of commands
usage: help [ command ]
""")
# Unexposed helper tables and functions begin here
def __dogetassoc(self):
try:
self.peers = self.session.readstat()
except ntp.packet.ControlException as e:
self.warn(e.message)
return False
except IOError as e:
self.warn(e.strerror)
return False
if not self.peers:
if self.chosts:
self.say("server=%s " % self.session.hostname)
self.say("No association IDs returned\n")
return False
if self.debug:
self.warn("\n%d associations total" % len(self.peers))
# sortassoc()
return True
def __printassoc(self, showall):
if not self.peers:
self.say("No association IDs in list\n")
return
self.say(
"\nind assid status conf reach auth condition last_event cnt\n")
self.say(
"===========================================================\n")
for (i, peer) in enumerate(self.peers):
statval = ntp.control.CTL_PEER_STATVAL(peer.status)
if (not showall and
(statval & (ntp.control.CTL_PST_CONFIG |
ntp.control.CTL_PST_REACH)) == 0):
continue
sw = ntp.util.PeerStatusWord(peer.status)
display = "%3d %5u %04x %3.3s %4s %4.4s %9.9s %11s %2lu" \
% (i + 1, peer.associd, peer.status, sw.conf, sw.reach,
sw.auth, sw.condition, sw.last_event, sw.event_count)
self.say(display + "\n")
def __dopeers(self, showall, mode):
if not self.__dogetassoc():
return
report = ntp.util.PeerSummary(mode,
self.pktversion,
self.showhostnames,
self.wideremote,
self.showunits,
termwidth=interpreter.termwidth,
debug=interpreter.debug,
logfp=self.logfp)
try:
maxhostlen = 0
if len(self.chosts) > 1:
maxhostlen = max([len(host) for (host, _af) in self.chosts])
self.say("%-*.*s "
% (maxhostlen, maxhostlen+1, "server"))
self.say(report.header() + "\n")
if len(self.chosts) > 1:
maxhostlen = max([len(host) for (host, _af) in self.chosts])
self.say("=" * (maxhostlen + 1))
self.say(("=" * report.width()) + "\n")
for peer in self.peers:
if (not showall and
not (ntp.control.CTL_PEER_STATVAL(peer.status) &
(ntp.control.CTL_PST_CONFIG |
ntp.control.CTL_PST_REACH))):
if self.debug:
self.warn("eliding [%d]\n" % peer.associd)
continue
try:
variables = self.session.readvar(peer.associd, raw=True)
except ntp.packet.ControlException as e:
self.warn(e.message)
return
except IOError as e:
self.warn(e.strerror)
return
if not variables:
if len(self.chosts) > 1:
self.warn("server=%s " % self.session.hostname)
self.warn("***No information returned for association %d"
% peer.associd)
continue
if len(self.chosts) > 1:
self.say(ntp.util.PeerSummary.high_truncate(
self.session.hostname, maxhostlen) + " " *
(maxhostlen + 1 - len(self.session.hostname)))
self.say(report.summary(self.session.rstatus,
variables, peer.associd))
except KeyboardInterrupt:
pass
def __assoc_valid(self, line, required=False):
"Process a numeric associd or index."
# FIXME: This does a useless call to __dogetassoc() when associd == 0
# No big deal most of the time. Just a useless packet exchange.
if not line:
if required:
self.warn("An associd argument is required.")
return -1
else:
return 0
if not self.peers:
self.__dogetassoc()
if line.startswith("&"):
try:
idx = int(line[1:].split()[0])
except ValueError:
self.warn("Invalid index literal.")
return -1
if idx < 0 or idx >= 2**16-1:
self.warn("%d is not a valid association number." % idx)
return -1
elif idx not in range(1, len(self.peers)+1):
self.warn("No such association as %d." % idx)
return -1
else:
return self.peers[idx - 1].associd
else:
try:
associd = int(line.split()[0])
except ValueError:
self.warn("Invalid associd literal.")
return -1
if (associd != 0 and
associd not in [peer.associd for peer in self.peers]):
self.warn("Unknown associd.")
return -1
else:
return associd
def __assoc_range_valid(self, line):
"Try to get a range of assoc IDs."
tokens = line.split()
if len(tokens) < 2:
return ()
lo = self.__assoc_valid(tokens[0])
hi = self.__assoc_valid(tokens[1])
if lo < 0 or hi < 0 or hi < lo:
return ()
if lo == hi:
return(lo,)
return range(lo, hi+1)
def printvars(self, variables, dtype, quiet):
"Dump variables in raw (actually, semi-cooked) mode."
if self.rawmode:
if not quiet:
self.say("status=0x%04x,\n" % self.session.rstatus)
# C ntpq not only suppressed \r but tried to visibilize
# high-half characters. We won't do that unless somebody
# files a bug, Mode 6 never seems to generate those in
# variable fetches.
text = ntp.poly.polystr(session.response.replace(
",\r\n", ",\n"))
else:
if not quiet:
self.say("status=%04x %s,\n"
% (self.session.rstatus,
ntp.ntpc.statustoa(dtype, self.session.rstatus)))
text = ntp.util.cook(variables, self.showunits)
text = text.replace("'", '"')
self.say(text)
def __dolist(self, varlist, associd, op, type, quiet=False):
"List variables associated with a specified peer."
try:
variables = self.session.readvar(associd, varlist, op, raw=True)
except ntp.packet.ControlException as e:
self.warn(e.message)
return False
except IOError as e:
self.warn(e.strerror)
return False
if len(self.chosts) > 1:
self.say("server=%s " % self.session.hostname)
if not variables:
if associd == 0:
self.say("No system%s variables returned\n"
% " clock" if (type == ntp.ntpc.TYPE_CLOCK) else "")
else:
self.say("No information returned for%s association %d\n"
% (" clock" if (type == ntp.ntpc.TYPE_CLOCK) else "",
associd))
return True
if not quiet:
self.say("associd=%d " % associd)
self.printvars(variables, type, not (not varlist))
return True
# Unexposed helper tables and functions end here
def do_units(self, _unused):
"toggle unit display"
self.showunits = not self.showunits
def help_units(self):
self.say("""\
function: toggle unit display
usage: units
""")
def do_EOF(self, _unused):
"exit ntpq"
self.say("\n")
return True
def do_timeout(self, line):
"set the primary receive time out"
if line:
try:
self.session.primary_timeout = int(line)
except ValueError:
self.warn("What?")
self.say("primary timeout %d ms\n" % self.session.primary_timeout)
def help_timeout(self):
self.say("""\
function: set the primary receive time out
usage: timeout [ msec ]
""")
def collect_display2(self, variables):
"Query and display a collection of variables from the system."
try:
queried = self.session.readvar(0,
[v[0] for v in variables] +
[v[0] + '_r' for v in variables],
raw=True)
except ntp.packet.ControlException as e:
if ntp.control.CERR_UNKNOWNVAR == e.errorcode:
self.warn("Unknown variable. Trying one at a time.")
varlist = [v[0] for v in variables] + \
[v[0] + '_r' for v in variables]
items = []
for var in varlist:
try:
queried = self.session.readvar(0, [var],
raw=True)
for (name, (value, rawvalue)) in queried.items():
items.append((name, (value, rawvalue)))
except ntp.packet.ControlException as e:
if ntp.control.CERR_UNKNOWNVAR == e.errorcode:
items.append((var, ("???", None)))
continue
raise e
queried = ntp.util.OrderedDict(items)
else:
self.warn(e.message)
return
except IOError as e:
self.warn(e.strerror)
return
if self.rawmode:
self.say(self.session.response)
return
try:
runs, runl = None, None
for (name, legend, fmt) in variables:
if name not in queried:
continue
value = queried[name][0]
rawvalue = queried[name][1]
value2 = queried[name + '_r'][0]
rawvalue2 = queried[name + '_r'][1]
if fmt in (NTP_UINT, NTP_INT, NTP_FLOAT):
if self.showunits and isinstance(rawvalue, (int, float)):
display = ntp.util.unitifyvar(rawvalue, name)
else:
display = value
if self.showunits and isinstance(rawvalue2, (int, float)):
display2 = ntp.util.unitifyvar(rawvalue2, name)
else:
display2 = value2
self.say(
"{0:<13} {1:>13} {2:>13}\n".format(
legend, display, display2)
)
elif fmt == NTP_PACKETS:
self.say(
"{0:<13} {1[0]:>13} {2[0]:>13} {1[1]:>10}{1[2]:<3} {2[1]:>10}{2[2]:<3}\n".format(
legend,
ntp.util.packetize(value, runs),
ntp.util.packetize(value2, runl),
)
)
elif fmt == NTP_UPTIME:
runs, display = ntp.util.periodize(value)
runl, display2 = ntp.util.periodize(value2)
self.say(
"{0:<13} {1:>13} {2:>13}\n".format(
legend, display, display2)
)
else:
self.warn("unexpected vc type %s for %s, value %s %s "
% (fmt, name, value, value2))
except KeyboardInterrupt:
self.warn("display interrupted")
def collect_display(self, associd, variables, decodestatus):
"Query and display a collection of variables from the system."
try:
queried = self.session.readvar(associd,
[v[0] for v in variables],
raw=True)
except ntp.packet.ControlException as e:
if ntp.control.CERR_UNKNOWNVAR == e.errorcode:
self.warn("Unknown variable. Trying one at a time.")
varlist = [v[0] for v in variables]
items = []
for var in varlist:
try:
queried = self.session.readvar(associd, [var],
raw=True)
for (name, (value, rawvalue)) in queried.items():
items.append((name, (value, rawvalue)))
except ntp.packet.ControlException as e:
if ntp.control.CERR_UNKNOWNVAR == e.errorcode:
items.append((var, "???"))
continue
raise e
queried = ntp.util.OrderedDict(items)
else:
self.warn(e.message)
return
except IOError as e:
self.warn(e.strerror)
return
if self.rawmode:
self.say(self.session.response)
return
if decodestatus:
if associd == 0:
statype = ntp.ntpc.TYPE_SYS
else:
statype = ntp.ntpc.TYPE_PEER
self.say("associd=%u status=%04x %s,\n"
% (associd, self.session.rstatus,
ntp.ntpc.statustoa(statype, self.session.rstatus)))
try:
run = 1
for (name, legend, fmt) in variables:
if name not in queried:
continue
value = queried[name][0]
rawvalue = queried[name][1]
if fmt in (NTP_ADD, NTP_ADP):
if self.showhostnames & 1: # if & 1, display names
if self.debug:
self.say("DNS lookup begins...")
value = ntp.util.canonicalize_dns(
value, family=self.ai_family)
if self.debug:
self.say("DNS lookup complete.")
self.say("%13s %13s\n" % (legend, value))
elif fmt == NTP_STR:
if value:
self.say("%13s %13s\n" % (legend, value))
elif fmt in (NTP_UINT, NTP_INT, NTP_FLOAT):
if self.showunits:
displayvalue = ntp.util.unitifyvar(rawvalue, name)
else:
displayvalue = value
self.say("%13s %13s\n" % (legend, displayvalue))
elif fmt == NTP_LFP:
self.say("%13s %13s\n" % (
legend, ntp.ntpc.prettydate(value)))
elif fmt == NTP_2BIT:
self.say("%13s %13s\n"
% (legend, ("00", "01", "10", "11")[value]))
elif fmt == NTP_MODE:
modes = (
"unspec", "sym_active", "sym_passive", "client",
"server",
"broadcast", "control", "private", "bclient"
)
try:
self.say("%s %s\n" % (legend, modes[value]))
except IndexError:
self.say("%s %s%d\n" % (legend, "mode#", value))
elif fmt == NTP_PACKETS:
self.say(
"{0:<13} {1[0]:>13} {1[1]:>10}{1[2]:<3}\n".format(
legend, ntp.util.packetize(value, run)
)
)
elif fmt == NTP_UPTIME:
run, display = ntp.util.periodize(value)
self.say("{0:<13} {1:>13}\n".format(legend, display))
else:
self.warn("unexpected vc type %s for %s, value %s"
% (fmt, name, value))
except KeyboardInterrupt:
self.warn("display interrupted")
def do_delay(self, line):
"set the delay added to encryption time stamps"
if not line:
self.say("delay %d ms\n" % self.auth_delay)
else:
try:
self.auth_delay = int(line)
if self.auth_delay < 0:
raise ValueError
except ValueError:
self.say("Huh?")
def help_delay(self):
self.say("""\
function: set the delay added to encryption time stamps
usage: delay [ msec ]
""")
def do_host(self, line):
"specify the host whose NTP server we talk to"
if not line:
if self.session.havehost():
self.say("current host is %s\n" % self.session.hostname)
else:
self.say("no current host\n")
else:
tokens = line.split()
if tokens[0] == '-4':
session.ai_family = socket.AF_INET
tokens.pop(0)
elif tokens[0] == '-6':
session.ai_family = socket.AF_INET6
tokens.pop(0)
try:
if (tokens and
self.session.openhost(tokens[0], session.ai_family)):
self.say("current host set to %s\n"
% self.session.hostname)
elif self.session.havehost():
self.say("current host remains %s\n"
% self.session.hostname)
else:
self.say("still no current host\n")
except KeyboardInterrupt:
self.warn("lookup interrupted")
def help_host(self):
self.say("""\
function: specify the host whose NTP server we talk to
usage: host [-4|-6] [hostname]
""")
def do_poll(self, line):
"poll an NTP server in client mode `n' times"
# And it's not in the C version, so we're off the hook here
self.warn("WARNING: poll not implemented yet")
def help_poll(self):
self.say("""\
function: poll an NTP server in client mode `n' times
usage: poll [n] [verbose]
""")
def do_passwd(self, line):
"specify a password to use for authenticated requests"
try:
self.session.password()
except ntp.packet.ControlException as e:
self.warn(e.message)
except IOError:
self.warn("***Can't read control key from /etc/ntpsec/ntp.conf")
def help_passwd(self):
self.say("""\
function: specify a password to use for authenticated requests
usage: passwd []
""")
def do_hostnames(self, line):
"specify whether hostnames or net numbers are printed"
if not line:
pass
elif line == "yes":
self.showhostnames = 1
elif line == "no":
self.showhostnames = 0
elif line == 'hostnum':
self.showhostnames = 2
elif line == 'hostname':
self.showhostnames = 3
else:
self.say("What?\n")
pass
if self.showhostnames & 1:
self.say('resolved hostnames being shown\n')
else:
self.say('resolved hostnames not being shown\n')
if self.showhostnames & 2:
self.say('supplied hostnames being shown\n')
else:
self.say('supplied hostnames not being shown\n')
def help_hostnames(self):
self.say("""\
function: specify whether hostnames or net numbers are printed
usage: hostnames [yes|no|hostname|hostnum]
""")
def do_debug(self, line):
"set/change debugging level"
if not line:
pass
elif line == "more":
self.debug += 1
elif line == "less":
if self.debug > 0:
self.debug -= 1
elif line == "no":
self.debug = 0
else:
try:
self.debug = int(line) # C version didn't implement this
except ValueError:
self.warn("What?")
self.session.debug = self.debug
self.say("debug level is %d\n" % self.debug)
def do_logfile(self, line):
"""view/change logfile. \"<stderr>\" will log to stderr
instead of a file"""
if not line:
self.say(repr(self.logfp.name) + "\n")
return
if self.logfp != sys.stderr:
self.logfp.close()
if line == "<stderr>":
self.logfp = self.session.logfp = sys.stderr
else:
try:
logfp = open(line, "a", 1) # 1 => line buffered
self.logfp = self.session.logfp = logfp
self.say("Logfile set to %s\n" % line)
except IOError:
self.warn("Could not open %s for logging." % line)
def help_debug(self):
self.say("""\
function: set/change debugging level
usage: debug [no|more|less|n]
""")
def do_exit(self, line):
"exit ntpq"
return True
def help_exit(self):
self.say("""\
function: exit ntpq
usage: exit
""")
do_quit = do_exit
def help_quit(self):
self.say("""\
function: exit ntpq
usage: quit
""")
def do_keyid(self, line):
"set keyid to use for authenticated requests"
if line:
try:
self.session.keyid = int(line)
except ValueError:
self.warn("What?")
if self.session.keyid is None:
self.say("no keyid defined\n")
else:
self.say("keyid is %d\n" % self.session.keyid)
def help_keyid(self):
self.say("""\
function: set keyid to use for authenticated requests
usage: keyid [key#]
""")
def do_version(self, line):
"print version number"
self.say(version + "\n")
def help_version(self):
self.say("""\
function: print version number
usage: version
""")
def do_direct(self, line):
"toggle direct mode output"
self.directmode = not self.directmode
if self.directmode:
self.say("Direct mode is on\n")
else:
self.say("Direct mode is off\n")
def help_direct(self):
self.say("""\
function: toggle direct-mode MRU output
usage: direct
""")
def do_raw(self, line):
"do raw mode variable output"
self.rawmode = True
self.say("Output set to raw\n")
def help_raw(self):
self.say("""\
function: do raw mode variable output
usage: raw
""")
def do_cooked(self, line):
"do cooked mode variable output"
self.rawmode = False
self.say("Output set to cooked\n")
def help_cooked(self):
self.say("""\
function: do cooked mode variable output
usage: cooked
""")
def do_authenticate(self, line):
"always authenticate requests to this server"
if not line:
pass
elif line == "yes":
self.session.always_auth = True
elif line == "no":
self.session.always_auth = False
else:
self.warn("What?")
if self.session.always_auth:
self.say("authenticated requests being sent\n")
else:
self.say("unauthenticated requests being sent\n")
def help_authenticate(self):
self.say("""\
function: always authenticate requests to this server
usage: authenticate [yes|no]
""")
def do_ntpversion(self, line):
"set the NTP version number to use for requests"
if not line:
pass
else:
try:
newversion = int(line)
if (newversion >= ntp.magic.NTP_OLDVERSION and
newversion <= ntp.magic.NTP_VERSION):
self.pktversion = newversion
else:
self.warn("versions %d to %d, please"
% (ntp.magic.NTP_OLDVERSION,
ntp.magic.NTP_VERSION))
except ValueError:
self.warn("What?")
self.say("NTP version being claimed is %d\n" % self.pktversion)
def help_ntpversion(self):
self.say("""\
function: set the NTP version number to use for requests
usage: ntpversion [version number]
""")
def do_keytype(self, line):
"set key type to use for authenticated requests"
if not line:
self.say("Keytype: %s\n" % self.session.keytype)
elif line.upper() in ['AES', 'AES128CMAC']:
self.session.keytype = 'AES-128'
elif not ntp.ntpc.checkname(line.upper()):
self.warn("Keytype %s is not supported by openSSL or ntpq.\n" % line)
else:
self.session.keytype = line.upper()
def help_keytype(self):
self.say("""\
function: set key type to use for authenticated requests, one of:
DSA, MD4, MD5, MDC2, RIPEMD160, SHA-1, AES-CMAC
usage: keytype [digest-name]
""")
def do_associations(self, line):
"print list of association IDs and statuses for the server's peers"
if self.__dogetassoc():
self.__printassoc(showall=True)
def help_associations(self):
self.say("""\
function: print list of association IDs and statuses for the server's peers
usage: associations
""")
def do_passociations(self, line):
"print list of associations returned by last associations command"
self.__printassoc(showall=True)
def help_passociations(self):
self.say("""\
function: print list of associations returned by last associations command
usage: passociations
""")
def do_lassociations(self, line):
"print list of associations including all client information"
if self.__dogetassoc():
self.__printassoc(showall=True)
def help_lassociations(self):
self.say("""\
function: print list of associations including all client information
usage: lassociations
""")
def do_lpassociations(self, line):
"""\
print last obtained list of associations, including client information
"""
self.__printassoc(showall=True)
def help_lpassociations(self):
self.say("""\
function: print last obtained list of associations, including
client information
usage: lpassociations
""")
def do_addvars(self, line):
"add variables to the variable list or change their values"
if not line:
self.warn("usage: addvars name[=value][,...]\n")
return
vars_to_add = line.split(',')
for add_var in vars_to_add:
try:
(name, val) = add_var.split("=")
except ValueError:
(name, val) = (add_var, "")
self.uservars[name.strip()] = val.strip()
def help_addvars(self):
self.say("""\
function: add variables to the variable list or change their values
usage: addvars name[=value][,...]
""")
def do_rmvars(self, line):
"remove variables from the variable list"
if not line:
self.warn("usage: rmvars name[,...]\n")
return
vars_to_rm = line.split(',')
for rm_var in vars_to_rm:
if rm_var not in self.uservars:
self.warn("%s is not in the variable list" % rm_var)
else:
del self.uservars[rm_var]
def help_rmvars(self):
self.say("""\
function: remove variables from the variable list
usage: rmvars name[,...]
""")
def do_clearvars(self, line):
"remove all variables from the variable list"
self.uservars.clear()
def help_clearvars(self):
self.say("""\
function: remove all variables from the variable list
usage: clearvars
""")
def do_showvars(self, line):
"print variables on the variable list"
if not self.uservars:
self.say("No variables on list.\n")
for (name, value) in self.uservars.items():
if value:
self.say("%s=%s\n" % (name, value))
else:
self.say(name + "\n")
def help_showvars(self):
self.say("""\
function: print variables on the variable list
usage: showvars
""")
def do_readlist(self, line):
"read the system or peer variables included in the variable list"
associd = self.__assoc_valid(line)
if associd >= 0:
qtype = ntp.ntpc.TYPE_SYS if associd == 0 else ntp.ntpc.TYPE_PEER
self.__dolist(self.uservars.keys(), associd,
ntp.control.CTL_OP_READVAR, qtype)
def help_readlist(self):
self.say("""\
function: read the system or peer variables included in the variable list
usage: readlist [assocID]
""")
def do_rl(self, line):
"read the system or peer variables included in the variable list"
self.do_readlist(line)
def help_rl(self):
self.say("""\
function: read the system or peer variables included in the variable list
usage: rl [assocID]
""")
def do_writelist(self, line):
"write the system or peer variables included in the variable list"
pass
def help_writelist(self):
self.say("""\
function: write the system or peer variables included in the variable list
usage: writelist [ assocID ]
""")
def do_readvar(self, line):
"read system or peer variables"
associd = self.__assoc_valid(line)
if associd >= 0:
qtype = ntp.ntpc.TYPE_SYS if associd == 0 else ntp.ntpc.TYPE_PEER
silence = bool(len(line.split()) >= 2)
# Some scripts written for C ntpq need associd printed here
self.__dolist(line.split()[1:], associd,
ntp.control.CTL_OP_READVAR, qtype, quiet=silence)
def help_readvar(self):
self.say("""\
function: read system or peer variables
usage: readvar [assocID] [varname1] [varname2] [varname3]
""")
def do_rv(self, line):
"read system or peer variables"
self.do_readvar(line)
def help_rv(self):
self.say("""\
function: read system or peer variables
usage: rv [assocID] [varname1] [varname2] [varname3]
""")
def do_writevar(self, line):
"write system or peer variables"
pass
def help_writevar(self):
self.say("""\
function: write system or peer variables
usage: writevar assocID name=value,[...]
""")
def do_mreadlist(self, line):
"read the peer variables in the variable list for multiple peers"
if not line:
self.warn("usage: mreadlist assocIDlow assocIDhigh\n")
return
idrange = self.__assoc_range_valid(line)
if not idrange:
return
for associd in idrange:
if associd != idrange[0]:
self.say("\n")
if not self.__dolist(self.uservars,
associd, ntp.control.CTL_OP_READVAR,
ntp.ntpc.TYPE_PEER):
return
def help_mreadlist(self):
self.say("""\
function: read the peer variables in the variable list for multiple peers
usage: mreadlist assocIDlow assocIDhigh
""")
def do_mrl(self, line):
"read the peer variables in the variable list for multiple peers"
if not line:
self.warn("usage: mrl assocIDlow assocIDhigh")
return
self.do_mreadlist(line)
def help_mrl(self):
self.say("""\
function: read the peer variables in the variable list for multiple peers
usage: mrl assocIDlow assocIDhigh
""")
def do_mreadvar(self, line):
"read peer variables from multiple peers"
if not line:
self.warn("usage: mreadvar assocIDlow assocIDhigh "
"[ name=value[,...] ]")
return
idrange = self.__assoc_range_valid(line)
if not idrange:
return
varlist = line.split()[2:]
for associd in idrange:
if associd != idrange[0]:
self.say("\n")
if not self.__dolist(varlist, associd,
ntp.control.CTL_OP_READVAR,
ntp.ntpc.TYPE_PEER):
return
def help_mreadvar(self):
self.say("""\
function: read peer variables from multiple peers
usage: mreadvar assocIDlow assocIDhigh [name=value[,...]]
""")
def do_mrv(self, line):
"read peer variables from multiple peers"
if not line:
self.warn(
"usage: mrv assocIDlow assocIDhigh [name=value[,...]]")
return
self.do_mreadvar(line)
def help_mrv(self):
self.say("""\
function: read peer variables from multiple peers
usage: mrv assocIDlow assocIDhigh [name=value[,...]]
""")
def do_clocklist(self, line):
"read the clock variables included in the variable list"
assoc = self.__assoc_valid(line)
if assoc >= 0:
self.__dolist(self.uservars.keys(),
assoc, ntp.control.CTL_OP_READCLOCK,
ntp.ntpc.TYPE_CLOCK)
def help_clocklist(self):
self.say("""\
function: read the clock variables included in the variable list
usage: clocklist [assocID]
""")
def do_cl(self, line):
"read the clock variables included in the variable list"
self.do_clocklist(line)
def help_cl(self):
self.say("""\
function: read the clock variables included in the variable list
usage: cl [assocID]
""")
def do_clockvar(self, line):
"read clock variables"
assoc = self.__assoc_valid(line)
if assoc == 0:
self.warn("This command requires the association ID of a clock.")
elif assoc > 0:
self.__dolist(line.split()[1:], assoc,
ntp.control.CTL_OP_READCLOCK, ntp.ntpc.TYPE_CLOCK)
def help_clockvar(self):
self.say("""\
function: read clock variables
usage: clockvar [assocID] [name=value[,...]]
""")
def do_cv(self, line):
"read clock variables"
self.do_clockvar(line)
def help_cv(self):
self.say("""\
function: read clock variables
usage: cv [ assocID ] [ name=value[,...] ]
""")
def do_pstats(self, line):
"show statistics for a peer"
pstats = (
("srcadr", "remote host: ", NTP_ADD),
("dstadr", "local address: ", NTP_ADD),
("timerec", "time last received: ", NTP_UPTIME),
("timer", "time until next send: ", NTP_UPTIME),
("timereach", "reachability change: ", NTP_INT),
("sent", "packets sent: ", NTP_INT),
("received", "packets received: ", NTP_INT),
("badauth", "bad authentication: ", NTP_INT),
("bogusorg", "bogus origin: ", NTP_INT),
("oldpkt", "duplicate: ", NTP_INT),
("seldisp", "bad dispersion: ", NTP_INT),
("selbroken", "bad reference time: ", NTP_INT),
("candidate", "candidate order: ", NTP_INT),
("ntscookies", "count of nts cookies: ", NTP_INT),
)
if not line:
self.warn("usage: pstats assocID")
return
associd = self.__assoc_valid(line)
if associd >= 0:
self.collect_display(associd=associd,
variables=pstats, decodestatus=True)
def help_pstats(self):
self.say("""\
function: show statistics for a peer
usage: pstats assocID
""")
def do_peers(self, line):
"obtain and print a list of the server's peers [IP version]"
self.__dopeers(showall=True, mode="peers")
def help_peers(self):
self.say("""\
function: obtain and print a list of the server's peers [IP version]
usage: peers
""")
def do_rpeers(self, line):
"obtain and print a list of the server's peers (dextral)"
self.__dopeers(showall=True, mode="rpeers")
def help_rpeers(self):
self.say("""\
function: obtain and print a list of the server's peers (dextral)
usage: rpeers
""")
def do_apeers(self, line):
"""
obtain and print a list of the server's peers and their assocIDs [IP version]
"""
self.__dopeers(showall=True, mode="apeers")
def help_apeers(self):
self.say("""\
function: obtain and print a list of the server's peers and their
assocIDs [IP version]
usage: apeers
""")
def do_lpeers(self, line):
"obtain and print a list of all peers and clients [IP version]"
self.__dopeers(showall=True, mode="peers")
def help_lpeers(self):
self.say("""\
function: obtain and print a list of all peers and clients [IP version]
usage: lpeers
""")
def do_opeers(self, line):
"""
print peer list the old way, with dstadr shown rather than refid [IP version]
"""
self.__dopeers(showall=True, mode="opeers")
def help_opeers(self):
self.say("""\
function: print peer list the old way, with dstadr shown rather than
refid [IP version]
usage: opeers
""")
def do_lopeers(self, line):
"""obtain and print a list of all peers and clients showing
dstadr [IP version]"""
self.__dopeers(showall=True, mode="opeers")
def help_lopeers(self):
self.say("""\
function: obtain and print a list of all peers and clients showing
dstadr [IP version]
usage: lopeers
""")
def do_hot_config(self, line):
"send a remote configuration command to ntpd"
try:
self.session.password()
except ntp.packet.ControlException as e:
self.warn(e.message)
return
except IOError:
self.warn("***Can't read control key from /etc/ntpsec/ntp.conf")
return
if self.debug > 2:
self.warn("In Config\nKeyword = :config\nCommand = %s" % line)
try:
self.session.config(line)
self.session.response = ntp.poly.polystr(self.session.response)
m = re.match("column ([0-9]+) syntax error", self.session.response)
if m:
col = int(m.group(1))
if col >= 0 and col <= len(line):
if self.interactive:
self.say("_" * (len(self.prompt) + 2 + col))
else:
self.say(line + "\n")
self.say("_" * (col - 1))
self.say("^\n")
self.say(self.session.response + "\n")
except ntp.packet.ControlException as e:
self.warn(e.message)
def help_hot_config(self):
self.say("""\
function: send a remote configuration command to ntpd
usage: config <configuration command line>
""")
def do_config_from_file(self, line):
"configure ntpd using the configuration filename"
try:
with open(line) as rfp:
self.say("%s\n" % self.session.config(rfp.read()))
except IOError:
self.warn("Could not read %s" % line)
def help_config_from_file(self):
self.say("""\
function: configure ntpd using the configuration filename
usage: config_from_file <configuration filename>
""")
def printdirect(self, entries):
for entry in entries:
self.say(self.formatter.summary(entry) + "\n")
def do_noflake(self):
"""Disables the dropping of control packets by ntpq for testing."""
self.session.flakey = False
def help_noflake(self):
"""Print help for noflake."""
self.say("""\
function: Disables the dropping of received packets by ntpq for testing.
usage: noflake
""")
def do_doflake(self, line):
"""Drop some received packets for testing.
Probabilities 0 and 1 should be certainly accepted
and discarded respectively. No default, but 0.1
should be a one in ten loss rate.
"""
try:
_ = float(line)
if not 0 < _ < 1:
raise ValueError
self.session.flakey = _
except ValueError:
self.say('Flakiness must be a (positive) float less than 1.')
def help_doflake(self):
"""Print help for doflake."""
self.say("""\
function: Enables the dropping of control packets by
ntpq for testing. Probabilities 0 and 1 should be
certainly accepted and discarded respectively. No
default, but 0.1 should be a one in ten loss rate.
usage: doflake <probability>
""")
def do_mrulist(self, line):
"""display the list of most recently seen source addresses,
tags mincount=... resall=0x... resany=0x..."""
cmdvars = {}
for item in line.split(" "):
if not item:
continue
if '=' not in item:
cmdvars[item] = True
else:
eq = item.index("=")
var = item[:eq].strip()
val = item[eq+1:].strip()
try:
val = int(val, 0)
except ValueError:
try:
val = float(val)
except ValueError:
if val[0] == '"' and val[-1] == '"':
val = val[1:-1]
cmdvars[var] = val
if not self.directmode:
self.say("Ctrl-C will stop MRU retrieval and display "
"partial results.\n")
if self.rawmode:
mruhook = lambda v: self.printvars(variables=v,
dtype=ntp.ntpc.TYPE_SYS,
quiet=True)
else:
mruhook = None
try:
formatter = ntp.util.MRUSummary(interpreter.showhostnames,
wideremote=True)
if self.directmode:
formatter.now = None
self.formatter = formatter
if session.debug:
formatter.logfp = session.logfp
formatter.debug = session.debug
self.session.slots = 0
self.session.start = time.time()
direct = self.printdirect if self.directmode else None
span = self.session.mrulist(variables=cmdvars,
rawhook=mruhook, direct=direct)
if not self.directmode and not self.rawmode:
if not span.is_complete():
self.say("mrulist retrieval interrupted by operator.\n"
"Displaying partial client list.\n")
span.now = time.time()
try:
delta1 = time.time() - self.session.start
self.say(ntp.util.MRUSummary.header + "\n")
self.say(("=" * len(ntp.util.MRUSummary.header)) + "\n")
# reversed puts most recent entries at the top if no sort=
# see sort comments in pylib/packet.py
formatter.now = span.now
for entry in reversed(span.entries):
self.say(formatter.summary(entry) + "\n")
self.say("# Collected %d slots in %.3f seconds\n"
% (self.session.slots, delta1))
except KeyboardInterrupt:
pass
delta2 = time.time() - self.session.start
self.say("# Processed %d slots in %.3f seconds\n"
% (self.session.slots, delta2))
usage = resource.getrusage(resource.RUSAGE_SELF)
rusage_denom = 1024.
if sys.platform == 'darwin':
# OSX uses bytes, while every other platform uses kilobytes
rusage_denom = rusage_denom * rusage_denom
self.say("# Used %d megabytes of memory\n"
% (usage.ru_maxrss/rusage_denom))
except ntp.packet.ControlException as e:
# Giving up after 8 restarts from the beginning.
# With high-traffic NTP servers, this can occur if the
# MRU list is limited to less than about 16 seconds' of
# entries. See the 'mru' ntp.conf entry.
self.warn(e.message)
def help_mrulist(self):
self.say("""\
function: display the list of most recently seen source addresses,
tags mincount=... resall=0x... resany=0x...
usage: mrulist [tag=value] [tag=value] [tag=value] [tag=value]
""")
def do_ifstats(self, line):
"show statistics for each local address ntpd is using"
try:
self.session.password()
entries = self.session.ifstats()
if self.rawmode:
self.say(self.session.response + "\n")
else:
formatter = ntp.util.IfstatsSummary()
self.say(ntp.util.IfstatsSummary.header)
self.say(("=" * ntp.util.IfstatsSummary.width) + "\n")
for (i, entry) in enumerate(entries):
self.say(formatter.summary(i, entry))
except ntp.packet.ControlException as e:
self.warn(e.message)
return
except IOError:
self.warn("***Can't read control key from /etc/ntpsec/ntp.conf")
def help_ifstats(self):
self.say("""\
function: show statistics for each local address ntpd is using
usage: ifstats
""")
def do_reslist(self, line):
"show ntpd access control list"
try:
self.session.password()
entries = self.session.reslist()
if self.rawmode:
self.say(self.session.response + "\n")
else:
formatter = ntp.util.ReslistSummary()
self.say(ntp.util.ReslistSummary.header)
self.say(("=" * ntp.util.ReslistSummary.width) + "\n")
for entry in entries:
self.say(formatter.summary(entry))
except ntp.packet.ControlException as e:
self.warn(e.message)
return
except IOError:
self.warn("***Can't read control key from /etc/ntpsec/ntp.conf")
def help_reslist(self):
self.say("""\
function: show ntpd access control list
usage: reslist
""")
# FIXME: This table should move to ntpd
# so the answers track when ntpd is updated
def do_sysinfo(self, _line):
"display system summary"
sysinfo = (
("peeradr", "system peer: ", NTP_ADP),
("peermode", "system peer mode: ", NTP_MODE),
("leap", "leap indicator: ", NTP_2BIT),
("stratum", "stratum: ", NTP_INT),
("precision", "log2 precision: ", NTP_INT),
("rootdelay", "root delay: ", NTP_FLOAT),
("rootdisp", "root dispersion: ", NTP_FLOAT),
("rootdist", "root distance ", NTP_FLOAT),
("refid", "reference ID: ", NTP_STR),
("reftime", "reference time: ", NTP_LFP),
("sys_jitter", "system jitter: ", NTP_FLOAT),
("clk_jitter", "clock jitter: ", NTP_FLOAT),
("clk_wander", "clock wander: ", NTP_FLOAT),
("authdelay", "symm. auth. delay:", NTP_FLOAT),
)
self.collect_display(associd=0, variables=sysinfo, decodestatus=True)
def help_sysinfo(self):
self.say("""\
function: display system summary
usage: sysinfo
""")
# FIXME: This table should move to ntpd
# so the answers track when ntpd is updated
def do_kerninfo(self, _line):
"display kernel loop and PPS statistics"
kerninfo = (
("koffset", "pll offset: ", NTP_FLOAT),
("kfreq", "pll frequency: ", NTP_FLOAT),
("kmaxerr", "maximum error: ", NTP_FLOAT),
("kesterr", "estimated error: ", NTP_FLOAT),
("kstflags", "kernel status: ", NTP_STR),
("ktimeconst", "pll time constant: ", NTP_INT),
("kprecis", "precision: ", NTP_FLOAT),
("kfreqtol", "frequency tolerance: ", NTP_INT),
("kppsfreq", "pps frequency: ", NTP_INT),
("kppsstab", "pps stability: ", NTP_INT),
("kppsjitter", "pps jitter: ", NTP_INT),
("kppscalibdur", "calibration interval ", NTP_INT),
("kppscalibs", "calibration cycles: ", NTP_INT),
("kppsjitexc", "jitter exceeded: ", NTP_INT),
("kppsstbexc", "stability exceeded: ", NTP_INT),
("kppscaliberrs", "calibration errors: ", NTP_INT),
)
self.collect_display(associd=0, variables=kerninfo, decodestatus=True)
def help_kerninfo(self):
self.say("""\
function: display kernel loop and PPS statistics
usage: kerninfo
""")
# FIXME: This table should move to ntpd
# so the answers track when ntpd is updated
def do_sysstats(self, _line):
"display system uptime and packet counts"
sysstats = (
("ss_uptime", "uptime: ", NTP_UPTIME),
("ss_numctlreq", "control requests: ", NTP_INT),
)
sysstats2 = (
("ss_reset", "sysstats reset: ", NTP_UPTIME),
("ss_received", "packets received: ", NTP_PACKETS),
("ss_thisver", "current version: ", NTP_PACKETS),
("ss_oldver", "older version: ", NTP_PACKETS),
("ss_ver1", "NTPv1 total: ", NTP_PACKETS),
("ss_ver1client","NTPv1 clients: ", NTP_PACKETS),
("ss_ver1zero", "NTPv1 mode0: ", NTP_PACKETS),
("ss_ver1symm", "NTPv1 symm act: ", NTP_PACKETS),
("ss_badformat", "bad length or format: ", NTP_PACKETS),
("ss_badauth", "authentication failed:", NTP_PACKETS),
("ss_declined", "declined: ", NTP_PACKETS),
("ss_restricted","restricted: ", NTP_PACKETS),
("ss_limited", "rate limited: ", NTP_PACKETS),
("ss_kodsent", "KoD responses: ", NTP_PACKETS),
("ss_processed", "processed for time: ", NTP_PACKETS),
)
self.collect_display(associd=0, variables=sysstats, decodestatus=False)
self.collect_display2(variables=sysstats2)
def help_sysstats(self):
self.say("""\
function: display system uptime and packet counts
usage: sysstats
""")
# FIXME: This table should move to ntpd
# so the answers track when ntpd is updated
def do_monstats(self, _line):
"display monitor (mrulist) counters and limits"
monstats = (
("mru_enabled", "enabled: ", NTP_INT),
("mru_hashslots", "hash slots in use: ", NTP_INT),
("mru_depth", "addresses in use: ", NTP_INT),
("mru_deepest", "peak addresses: ", NTP_INT),
("mru_maxdepth", "maximum addresses: ", NTP_INT),
("mru_mindepth", "reclaim above count: ", NTP_INT),
("mru_maxage", "reclaim maxage: ", NTP_UPTIME),
("mru_minage", "reclaim minage: ", NTP_UPTIME),
("mru_mem", "kilobytes: ", NTP_INT),
("mru_maxmem", "maximum kilobytes: ", NTP_INT),
("mru_exists", "alloc: exists: ", NTP_INT),
("mru_new", "alloc: new: ", NTP_INT),
("mru_recycleold", "alloc: recycle old: ", NTP_INT),
("mru_recyclefull", "alloc: recycle full: ", NTP_INT),
("mru_none", "alloc: none: ", NTP_INT),
("mru_oldest_age", "age of oldest slot: ", NTP_UPTIME),
)
self.collect_display(associd=0, variables=monstats, decodestatus=False)
def help_monstats(self):
self.say("""\
function: display monitor (mrulist) counters and limits
usage: monstats
""")
# FIXME: This table should move to ntpd
# so the answers track when ntpd is updated
def do_authinfo(self, _line):
"display symmetric authentication counters"
authinfo = (
("authreset", "time since reset: ", NTP_UPTIME),
("authkeys", "stored keys: ", NTP_INT),
("authfreek", "free keys: ", NTP_INT),
("authklookups", "key lookups: ", NTP_INT),
("authknotfound", "keys not found: ", NTP_INT),
("authencrypts", "encryptions: ", NTP_PACKETS),
("authdigestencrypts", "digest encryptions: ", NTP_PACKETS),
("authcmacencrypts", "CMAC encryptions: ", NTP_PACKETS),
("authdecrypts", "decryptions: ", NTP_PACKETS),
("authdigestdecrypts", "digest decryptions: ", NTP_PACKETS),
("authdigestfails", "digest failures: ", NTP_PACKETS),
("authcmacdecrypts", "CMAC decryptions: ", NTP_PACKETS),
("authcmacfails", "CMAC failures: ", NTP_PACKETS),
# Old variables no longer supported.
# Interesting if looking at an old system.
("authkuncached", "uncached keys: ", NTP_INT),
("authkexpired", "expired keys: ", NTP_INT),
)
self.collect_display(associd=0, variables=authinfo, decodestatus=False)
def help_authinfo(self):
self.say("""\
function: display symmetric authentication counters
usage: authinfo
""")
# FIXME: This table should move to ntpd
# so the answers track when ntpd is updated
def do_ntsinfo(self, _line):
"display NTS authentication counters"
ntsinfo = (
("nts_client_send", "NTS client sends: ", NTP_UINT),
("nts_client_recv_good", "NTS client recvs good: ", NTP_UINT),
("nts_client_recv_bad", "NTS client recvs w error: ", NTP_UINT),
("nts_server_recv_good", "NTS server recvs good: ", NTP_UINT),
("nts_server_recv_bad", "NTS server recvs w error: ", NTP_UINT),
("nts_server_send", "NTS server sends: ", NTP_UINT),
("nts_cookie_make", "NTS make cookies: ", NTP_UINT),
("nts_cookie_decode", "NTS decode cookies: ", NTP_UINT),
("nts_cookie_decode_old", "NTS decode cookies old: ", NTP_UINT),
("nts_cookie_decode_old2", "NTS decode cookies old2: ", NTP_UINT),
("nts_cookie_decode_older", "NTS decode cookies older: ", NTP_UINT),
("nts_cookie_decode_too_old", "NTS decode cookies too old:", NTP_UINT),
("nts_cookie_decode_error", "NTS decode cookies error: ", NTP_UINT),
("nts_ke_probes_good", "NTS KE client probes good: ", NTP_UINT),
("nts_ke_probes_bad", "NTS KE client probes bad: ", NTP_UINT),
("nts_ke_serves_good", "NTS KE serves good: ", NTP_UINT),
("nts_ke_serves_bad", "NTS KE serves bad: ", NTP_UINT),
)
self.collect_display(associd=0, variables=ntsinfo, decodestatus=False)
def help_ntsinfo(self):
self.say("""\
function: display NTS authentication counters
usage: ntsinfo
""")
# FIXME: This table should move to ntpd
# so the answers track when ntpd is updated
def do_iostats(self, _line):
"display network input and output counters"
iostats = (
("iostats_reset", "time since reset: ", NTP_UPTIME),
("total_rbuf", "receive buffers: ", NTP_INT),
("free_rbuf", "free receive buffers: ", NTP_INT),
("used_rbuf", "used receive buffers: ", NTP_INT),
("rbuf_lowater", "low water refills: ", NTP_INT),
("io_dropped", "dropped packets: ", NTP_PACKETS),
("io_ignored", "ignored packets: ", NTP_PACKETS),
("io_received", "received packets: ", NTP_PACKETS),
("io_sent", "packets sent: ", NTP_PACKETS),
("io_sendfailed", "packet send failures: ", NTP_PACKETS),
("io_wakeups", "input wakeups: ", NTP_INT),
("io_goodwakeups", "useful input wakeups: ", NTP_INT),
)
self.collect_display(associd=0, variables=iostats, decodestatus=False)
def help_iostats(self):
self.say("""\
function: display network input and output counters
usage: iostats
""")
# FIXME: This table should move to ntpd
# so the answers track when ntpd is updated
def do_timerstats(self, line):
"display interval timer counters"
timerstats = (
("timerstats_reset", "time since reset: ", NTP_UPTIME),
("timer_overruns", "timer overruns: ", NTP_INT),
("timer_xmts", "calls to transmit: ", NTP_INT),
)
self.collect_display(associd=0, variables=timerstats,
decodestatus=False)
def help_timerstats(self):
self.say("""\
function: display interval timer counters
usage: timerstats
""")
# Default values we use.
DEFHOST = "localhost" # default host name
#
# main - parse arguments and handle options
#
usage = '''
USAGE: ntpq [-46dphinOV] [-c str] [-D lvl] [host ...]
Flg Arg Option-Name Description
-4 no ipv4 Force IPv4 DNS name resolution
- prohibits the option 'ipv6'
-6 no ipv6 Force IPv6 DNS name resolution
- prohibits the option 'ipv4'
-a Num authentication Enable authentication with the numbered key
-c Str command Run a command and exit
- may appear multiple times
-d no debug-level Increase output debug message level
- may appear multiple times
-l Str logfile Logs debug messages to the provided filename
-D Int set-debug-level Set the output debug message level
- may appear multiple times
-h no help Print a usage message.
-p no peers Print a list of the peers
-n no numeric Numeric host addresses
-k Str keyfile Specify a keyfile. ntpq will look in this file
for the key specified with -a
-V opt version Output version information and exit
-w no wide Enable wide display of addresses / hosts
on a separate line
-W Num width Force output width to this value instead of
querying the terminal size
-u no units Display time with units.
'''
if __name__ == '__main__':
bin_ver = "ntpsec-1.2.2"
if ntp.util.stdversion() != bin_ver:
sys.stderr.write("Module/Binary version mismatch\n")
sys.stderr.write("Binary: %s\n" % bin_ver)
sys.stderr.write("Module: %s\n" % ntp.util.stdversion())
try:
(options, arguments) = getopt.getopt(
sys.argv[1:],
"46a:c:dD:hk:npsSVwW:ul:",
["ipv4", "ipv6", "authentication=",
"command=", "debug", "set-debug-level=",
"help", "keyfile", "numeric", "peers",
"version", "wide", "width=", "units",
"logfile=", "srcname", "srcnumber"])
except getopt.GetoptError as e:
sys.stderr.write("%s\n" % e)
sys.stderr.write(usage)
raise SystemExit(1)
progname = os.path.basename(sys.argv[0])
ntp.ntpc.setprogname(progname)
session = ntp.packet.ControlSession()
interpreter = Ntpq(session)
keyid = keyfile = None
logfp = sys.stderr
for (switch, val) in options:
if switch in ("-4", "--ipv4"):
interpreter.ai_family = socket.AF_INET
elif switch in ("-6", "--ipv6"):
interpreter.ai_family = socket.AF_INET6
elif switch in ("-a", "--authentication"):
errmsg = "Error: -a parameter '%s' not a number\n"
keyid = ntp.util.safeargcast(val, int, errmsg, usage)
elif switch in ("-c", "--command"):
interpreter.ccmds.append(val)
elif switch in ("-d", "--debug"):
interpreter.debug += 1
session.debug += 1
elif switch in ("-D", "--set-debug-level"):
errmsg = "Error: -D parameter '%s' not a number\n"
cast = ntp.util.safeargcast(val, int, errmsg, usage)
session.debug = interpreter.debug = cast
elif switch in ("-h", "--help"):
sys.stderr.write(usage)
raise SystemExit(0)
elif switch in ("-n", "--numeric"):
interpreter.showhostnames = 0
elif switch in ("-p", "--peers"):
interpreter.ccmds.append("peers")
elif switch in ("-k", "--keyfile"):
keyfile = val
elif switch in ("-s", "--srcname"):
interpreter.showhostnames = 3
elif switch in ("-S", "--srcnumber"):
interpreter.showhostnames = 2
elif switch in ("-V", "--version"):
sys.stdout.write("ntpq %s\n" % version)
raise SystemExit(0)
elif switch in ("-w", "--wide"):
interpreter.wideremote = True
elif switch in ("-W", "--width"):
errmsg = "Error: -W parameter '%s' not a number\n"
interpreter.termwidth = ntp.util.safeargcast(val, int,
errmsg, usage)
elif switch in ("-u", "--units"):
interpreter.showunits = True
elif switch in ("-l", "--logfile"):
if logfp != sys.stderr:
logfp.close()
logfp = open(val, "a", 1) # 1 => line buffered
session.logfp = interpreter.logfp = logfp
if ntp.poly.forced_utf8 and interpreter.debug:
interpreter.warn("\nforced UTF-8 output\n")
if keyfile is not None: # Have a -k, setup the auth
credentials = None
try:
credentials = ntp.packet.Authenticator(keyfile)
except (OSError, IOError):
sys.stderr.write("ntpq: %s nonexistent or unreadable" % keyfile)
raise SystemExit(1)
if credentials:
session.auth = credentials
if keyid is not None: # Have an -a
session.keyid = keyid
for token in arguments:
if token.startswith("-"):
if '4' == token[-1]:
session.ai_family = socket.AF_INET
elif '6' == token[-1]:
session.ai_family = socket.AF_INET6
else:
interpreter.warn("%s: unexpected option-like thing."
% progname)
raise SystemExit(1)
arguments.pop(0)
else:
interpreter.chosts.append((token, session.ai_family))
if not arguments:
interpreter.chosts.append((DEFHOST, session.ai_family))
if (not interpreter.ccmds and
not interpreter.interactive and
os.isatty(0) and
os.isatty(1)):
interpreter.interactive = True
try:
if not interpreter.ccmds:
if len(interpreter.chosts) > 1:
interpreter.warn(
"ntpq can only work interactively on one host.")
interpreter.chosts = interpreter.chosts[:1]
session.openhost(*interpreter.chosts[0])
interpreter.cmdloop()
else:
for ihost in interpreter.chosts:
if session.openhost(*ihost):
for command in interpreter.ccmds:
interpreter.onecmd(interpreter.precmd(command))
session.close()
raise SystemExit(0)
except (KeyboardInterrupt, EOFError):
if os.isatty(0):
interpreter.say("\n")
except ntp.packet.ControlException as e:
interpreter.warn(e.message)
except IOError:
sys.stderr.write("Bailing out...\n")
# end
|