[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