[Git][NTPsec/ntpsec][master] Add and by default build/use ctypes wrapper for FFI ntp library...
James Browning
gitlab at mg.gitlab.com
Sun Sep 27 00:58:08 UTC 2020
James Browning pushed to branch master at NTPsec / ntpsec
Commits:
366569b1 by James Browning at 2020-09-27T00:58:00+00:00
Add and by default build/use ctypes wrapper for FFI ntp library...
- - - - -
6 changed files:
- + libntp/ntp_c.c
- libntp/wscript
- + pylib/ntpc.py
- pylib/wscript
- wafhelpers/options.py
- wscript
Changes:
=====================================
libntp/ntp_c.c
=====================================
@@ -0,0 +1,119 @@
+/*
+ * Copyright the NTPsec project contributors
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Python binding for selected libntp library functions
+ */
+
+/* This include has to come early or we get warnings from redefining
+ * _POSIX_C_SOURCE and _XOPEN_SOURCE on some systems.
+ */
+#include "config.h"
+
+#include "ntp_machine.h"
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_fp.h"
+#include "ntp_stdlib.h"
+#include "ntp_syslog.h"
+#include "timespecops.h"
+
+#include "ntp_config.h"
+#include "ntp_assert.h"
+
+#include "ntp_control.h"
+
+#include "pymodule-mac.h"
+
+void ntpc_setprogname(char*);
+char *ntpc_prettydate(char*);
+double ntpc_lfptofloat(char*);
+int ntpc_set_tod(int, int);
+bool ntpc_adj_systime(double);
+bool ntpc_step_systime(double);
+
+/* Don't include anything from OpenSSL */
+
+const char *progname = "libntpc";
+int SYS_TYPE = TYPE_SYS;
+int PEER_TYPE = TYPE_PEER;
+int CLOCK_TYPE = TYPE_CLOCK;
+
+/*
+ * Client utility functions
+ */
+
+void
+ntpc_setprogname(char *s)
+{
+ /*
+ * This function is only called from clients. Therefore
+ * log to stderr rather than syslog, and suppress logfile
+ * impediments. If we ever want finer-grained control, that
+ * will be easily implemented with additional arguments.
+ */
+ syslogit = false; /* don't log messages to syslog */
+ termlogit = true; /* duplicate to stdout/err */
+ termlogit_pid = false;
+ msyslog_include_timestamp = false;
+ progname = strdup(s);
+}
+
+char *
+ntpc_prettydate(char *s)
+{
+ l_fp ts;
+
+ if (false == hextolfp(s+2, &ts)) {
+ errno = EINVAL;
+ return strdup("ERROR");
+ }
+ errno = 0;
+ return prettydate(ts);
+}
+
+double
+ntpc_lfptofloat(char *s)
+{
+ l_fp ts;
+ struct timespec tt;
+
+ if (false == hextolfp(s+2, &ts)) {
+ errno = EINVAL;
+ return -0;
+ }
+ errno = 0;
+ tt = lfp_stamp_to_tspec(ts, time(NULL));
+ return tt.tv_sec + (tt.tv_nsec * S_PER_NS);
+}
+
+int
+ntpc_set_tod(int seconds, int fractional)
+{
+ struct timespec ts;
+ ts.tv_sec = seconds;
+ ts.tv_nsec = fractional;
+
+ return ntp_set_tod(&ts);
+}
+
+bool
+ntpc_adj_systime(double adjustment)
+{
+ return adj_systime(adjustment, adjtime) ? 1 : 0;
+}
+
+bool
+ntpc_step_systime(double adjustment)
+{
+ doubletime_t full_adjustment;
+
+ /*
+ * What we really want is for Python to parse a long double.
+ * As this is, it's a potential source of problems in the Python
+ * utilities if and when the time difference between the Unix epoch
+ * and now exceeds the range of a double.
+ */
+ full_adjustment = adjustment;
+ return step_systime(full_adjustment, ntp_set_tod);
+}
=====================================
libntp/wscript
=====================================
@@ -1,3 +1,5 @@
+import os
+
def build(ctx):
libntp_source = [
@@ -47,12 +49,24 @@ def build(ctx):
use="CRYPTO SSL",
)
- # Loadable Python extension
- ctx(
- features="c cshlib pyext",
- install_path='${PYTHONARCHDIR}/ntp',
- includes=[ctx.bldnode.parent.abspath(), "../include"],
- source=["pymodule.c", "pymodule-mac.c"] + libntp_source_sharable,
- target="../pylib/ntpc", # Put the output in the pylib directory
- use="M RT CRYPTO",
- )
+ if ctx.env['ntpc'] == 'ffi':
+ # Loadable FFI stub
+ ctx(
+ features="c cshlib",
+ install_path='${LIBDIR}/ntp',
+ includes=[ctx.bldnode.parent.abspath(), "../include"],
+ source=["ntp_c.c", "pymodule-mac.c"] + libntp_source_sharable,
+ target="../pylib/ntpc", # Put the output in the pylib directory
+ use="M RT CRYPTO",
+ vnum=ctx.env['ntpcver'],
+ )
+ elif ctx.env['ntpc'] == 'ext':
+ # Loadable Python extension
+ ctx(
+ features="c cshlib pyext",
+ install_path='${PYTHONARCHDIR}/ntp',
+ includes=[ctx.bldnode.parent.abspath(), "../include"],
+ source=["pymodule.c", "pymodule-mac.c"] + libntp_source_sharable,
+ target="../pylib/ntpc", # Put the output in the pylib directory
+ use="M RT CRYPTO",
+ )
=====================================
pylib/ntpc.py
=====================================
@@ -0,0 +1,159 @@
+# -*- coding: utf-8 -*-
+# SPDX-License-Identifier: BSD-2-Clause
+"""Access libntp funtions from Python."""
+from __future__ import absolute_import
+import ctypes
+import ctypes.util
+import errno
+import os
+import os.path
+import sys
+import ntp.poly
+
+LIB = 'ntpc'
+
+
+def _fmt():
+ """Produce library naming scheme."""
+ if sys.platform.startswith('darwin'):
+ return 'lib%s.dylib'
+ if sys.platform.startswith('win32'):
+ return '%s.dll'
+ if sys.platform.startswith('cygwin'):
+ return 'lib%s.dll'
+ return 'lib%s.so'
+
+
+def _importado():
+ """Load the ntpc library or throw an OSError trying."""
+ ntpc_paths = [] # places to look
+
+ j = __file__.split(os.sep)[:-1]
+ ntpc_paths.append(os.sep.join(j + [_fmt() % LIB]))
+
+ ntpc_path = ctypes.util.find_library(LIB)
+ if ntpc_path:
+ ntpc_paths.append(ntpc_path)
+
+ return _dlo(ntpc_paths)
+
+
+def _dlo(paths):
+ """Try opening library from a list."""
+ for ntpc_path in paths:
+ try:
+ lib = ctypes.CDLL(ntpc_path, use_errno=True)
+ return lib
+ except OSError:
+ pass
+ raise OSError("Can't find %s library" % LIB)
+
+
+_ntpc = _importado()
+progname = ctypes.c_char_p.in_dll(_ntpc, 'progname')
+# log_sys = ctypes.c_bool.in_dll(_ntpc, 'syslogit')
+# log_term = ctypes.c_bool.in_dll(_ntpc, 'termlogit')
+# log_pid = ctypes.c_bool.in_dll(_ntpc, 'termlogit_pid')
+# log_time = ctypes.c_bool.in_dll(_ntpc, 'msyslog_include_timestamp')
+
+TYPE_SYS = ctypes.c_int.in_dll(_ntpc, 'SYS_TYPE').value
+TYPE_PEER = ctypes.c_int.in_dll(_ntpc, 'PEER_TYPE').value
+TYPE_CLOCK = ctypes.c_int.in_dll(_ntpc, 'CLOCK_TYPE').value
+
+
+def checkname(name):
+ """Check if name is a valid algorithm name."""
+ _ntpc.do_checkname.restype = ctypes.c_int
+ mid_bytes = ntp.poly.polybytes(name)
+ _ntpc.do_checkname.argtypes = [ctypes.c_char_p]
+ return _ntpc.do_checkname(mid_bytes)
+
+
+def mac(data, key, name):
+ """Compute HMAC or CMAC from data, key, and algorithm name."""
+ resultlen = ctypes.c_size_t()
+ result = (ctypes.c_char * 64)()
+ result.value = b'\0' * 64
+ _ntpc.do_mac.restype = None
+ _ntpc.do_mac(ntp.poly.polybytes(name),
+ ntp.poly.polybytes(data), len(data),
+ ntp.poly.polybytes(key), len(key),
+ ctypes.byref(result), ctypes.byref(resultlen))
+ return result.value
+
+
+def setprogname(in_string):
+ """Set program name for logging purposes."""
+ mid_bytes = ntp.poly.polybytes(in_string)
+ _setprogname(mid_bytes)
+
+
+def _lfp_wrap(callback, in_string):
+ """NTP l_fp to other Python-style format."""
+ mid_bytes = ntp.poly.polybytes(in_string)
+ out_value = callback(mid_bytes)
+ err = ctypes.get_errno()
+ if err == errno.EINVAL:
+ raise ValueError('ill-formed hex date')
+ return out_value
+
+
+def statustoa(i_type, i_st):
+ """Convert a time stamp to something readable."""
+ mid_str = _statustoa(i_type, i_st)
+ return ntp.poly.polystr(mid_str)
+
+
+def prettydate(in_string):
+ """Convert a time stamp to something readable."""
+ mid_str = _lfp_wrap(_prettydate, in_string)
+ return ntp.poly.polystr(mid_str)
+
+
+def lfptofloat(in_string):
+ """NTP l_fp to Python-style float time."""
+ return _lfp_wrap(_lfptofloat, in_string)
+
+
+def msyslog(level, in_string):
+ """Log send a message to terminal or output."""
+ mid_bytes = ntp.poly.polybytes(in_string)
+ _msyslog(level, mid_bytes)
+
+
+# Set return type and argument types of hidden ffi handlers
+_msyslog = _ntpc.msyslog
+_msyslog.restype = None
+_msyslog.argtypes = [ctypes.c_int, ctypes.c_char_p]
+
+_setprogname = _ntpc.ntpc_setprogname
+_setprogname.restype = None
+_setprogname.argtypes = [ctypes.c_char_p]
+
+_prettydate = _ntpc.ntpc_prettydate
+_prettydate.restype = ctypes.c_char_p
+_prettydate.argtypes = [ctypes.c_char_p]
+
+_lfptofloat = _ntpc.ntpc_lfptofloat
+_lfptofloat.restype = ctypes.c_double
+_lfptofloat.argtypes = [ctypes.c_char_p]
+
+# Status string display from peer status word.
+_statustoa = _ntpc.statustoa
+_statustoa.restype = ctypes.c_char_p
+_statustoa.argtypes = [ctypes.c_int, ctypes.c_int]
+
+# Set time to nanosecond precision.
+set_tod = _ntpc.ntpc_set_tod
+set_tod.restype = ctypes.c_int
+set_tod.argtypes = [ctypes.c_int, ctypes.c_int]
+
+# Adjust system time by slewing.
+adj_systime = _ntpc.ntpc_adj_systime
+adj_systime.restype = ctypes.c_bool
+adj_systime.argtypes = [ctypes.c_double]
+
+# Adjust system time by stepping.
+step_systime = _ntpc.ntpc_step_systime
+step_systime.restype = ctypes.c_bool
+step_systime.argtypes = [ctypes.c_double]
=====================================
pylib/wscript
=====================================
@@ -36,7 +36,11 @@ def build(ctx):
target1 = bldnode.make_node('control.py')
target2 = bldnode.make_node('magic.py')
- sources = srcnode.ant_glob('*.py')
+ sources = []
+ if ctx.env['ntpc'] == 'ext':
+ sources = srcnode.ant_glob("*.py", excl='ntpc.py')
+ elif ctx.env['ntpc'] == 'ffi':
+ sources = srcnode.ant_glob('*.py')
builds = [x.get_bld() for x in sources]
# The rm's here were added to fix a reported (but underdocumented) problem
@@ -52,6 +56,9 @@ def build(ctx):
#ctx.exec_command("rm -f %s" % target1.abspath())
#ctx.exec_command("rm -f %s" % target2.abspath())
+ if ctx.env['ntpc'] is None:
+ return
+
# Make sure Python sees .py as well as .pyc/.pyo
ctx(
features="subst",
=====================================
wafhelpers/options.py
=====================================
@@ -31,6 +31,10 @@ def options_cmd(ctx, config):
grp.add_option('--enable-debug-timing', action='store_true',
default=False,
help="Collect timing statistics for debugging.")
+ grp.add_option('--enable-pylib', action='store',
+ default='ffi', choices=['ext', 'ffi', 'none'],
+ help="""Choose which Python library to build.\n
+ext, ffi, or none. defaults to ffi.""", nargs=1)
grp = ctx.add_option_group("NTP cross compile options")
grp.add_option('--cross-compiler', type='string',
=====================================
wscript
=====================================
@@ -120,6 +120,9 @@ def configure(ctx):
opt = flag.replace("--", "").upper()
opt_map[opt] = ctx.env.OPT_STORE[flag]
+ ctx.env['ntpc'] = ctx.options.enable_pylib
+ ctx.env['ntpcver'] = '1.1.0'
+
msg("--- Configuring host ---")
ctx.setenv('host', ctx.env.derive())
@@ -1013,15 +1016,24 @@ def build(ctx):
ctx.load('asciidoc', tooldir='wafhelpers/')
ctx.load('rtems_trace', tooldir='wafhelpers/')
+ if ctx.variant == "host":
+ ctx.recurse("ntpd")
+ return
+
if ctx.cmd == "build":
# It's a waf gotcha that if there are object files (including
# .pyc and .pyo files) in a source directory, compilation to
# the build directory never happens. This is how we foil that.
ctx.add_pre_fun(lambda ctx: ctx.exec_command("rm -f pylib/*.py[co]"))
-
- if ctx.variant == "host":
- ctx.recurse("ntpd")
- return
+ # Start purging ntp.ntpc files from build dir
+ # so old extension won't clobber FFI or reverse
+ bldnode = ctx.bldnode.make_node('pylib')
+ bldnode.mkdir()
+ target3 = bldnode.ant_glob('*ntpc*')
+ for _ in target3:
+ ctx.exec_command("rm -f %s" % _.abspath())
+ # Finish purging ntp.ntpc
+ ctx.add_group()
if ctx.env.REFCLOCK_GENERIC or ctx.env.REFCLOCK_TRIMBLE:
# required by the generic and Trimble refclocks
View it on GitLab: https://gitlab.com/NTPsec/ntpsec/-/commit/366569b1ea50015b8556799c4d0cccbbfce666b7
--
View it on GitLab: https://gitlab.com/NTPsec/ntpsec/-/commit/366569b1ea50015b8556799c4d0cccbbfce666b7
You're receiving this email because of your account on gitlab.com.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.ntpsec.org/pipermail/vc/attachments/20200927/4fb1e77a/attachment-0001.htm>
More information about the vc
mailing list