Add Swift daemon API, use in 'servaldswift'

The daemon API is built as a Swift module called ServalDNA.

The new CliContext class allows easy implementation of CLI output using
Swift code.  The new CliContextFile subclass is the obvious first
implementation, equivalent to cli_stdio.c.  The 'servaldswift'
executable now uses CliContextFile to print its CLI output to standard
output.

The new delegated log output support constructs each log line in a
buffer and prints it by calling the delegate's 'print' function at
end-of-line.  The 'servaldswift' executable now provides a log output
delegate in Swift that simply prints to standard error, replacing
log_output_console.o, which is omitted from its link.
This commit is contained in:
Andrew Bettison 2017-12-15 17:10:56 +10:30
parent 3ffa4b10af
commit a79156c4d0
17 changed files with 640 additions and 35 deletions

3
.gitignore vendored
View File

@ -11,6 +11,8 @@
*.a *.a
*.suo *.suo
.*.sw? .*.sw?
*.swiftdoc
*.swiftmodule
/VERSION.txt /VERSION.txt
/configure /configure
/autom4te.cache /autom4te.cache
@ -37,6 +39,7 @@ test.*.log
/java-api/Makefile /java-api/Makefile
/java-api/classes/ /java-api/classes/
/java-api/testclasses/ /java-api/testclasses/
/swift-daemon-api/build/
/swift-client-api/Makefile /swift-client-api/Makefile
/swift-client-api/build/ /swift-client-api/build/
/swift-client-api/swift-client-util /swift-client-api/swift-client-util

View File

@ -259,6 +259,11 @@ present][Swift development]:
Swift language's run-time support and resultant library dependencies, so is Swift language's run-time support and resultant library dependencies, so is
not suitable for deployment. not suitable for deployment.
* **ServalDNA.swiftmodule**
**ServalDNA.swiftdoc**
**libServalDNA.a**
are the [Swift Daemon API module][].
* **swift-client-api/build/debug/ServalClient.swiftmodule** * **swift-client-api/build/debug/ServalClient.swiftmodule**
**swift-client-api/build/debug/ServalClient.swiftdoc** **swift-client-api/build/debug/ServalClient.swiftdoc**
**swift-client-api/build/debug/libServalClient.a** **swift-client-api/build/debug/libServalClient.a**
@ -330,6 +335,7 @@ This document is available under the [Creative Commons Attribution 4.0 Internati
[gcc 6]: http://gcc.gnu.org/gcc-6/ [gcc 6]: http://gcc.gnu.org/gcc-6/
[Notes for Developers]: ./doc/Development.md [Notes for Developers]: ./doc/Development.md
[Swift development]: ./doc/Development.md#swift [Swift development]: ./doc/Development.md#swift
[Swift Daemon API module]: ./doc/Development.md#swift-daemon-api
[Swift Client API module]: ./doc/Development.md#swift-client-api [Swift Client API module]: ./doc/Development.md#swift-client-api
[OpenWRT]: ./doc/OpenWRT.md [OpenWRT]: ./doc/OpenWRT.md
[Serval Infrastructure]: ./doc/Serval-Infrastructure.md [Serval Infrastructure]: ./doc/Serval-Infrastructure.md

View File

@ -1,4 +1,5 @@
# Makefile.in for Serval DNA daemon and libraries # Makefile.in for Serval DNA daemon and libraries
# nnnn
# vim: noet ts=8 sts=0 sw=8 # vim: noet ts=8 sts=0 sw=8
prefix=@prefix@ prefix=@prefix@
exec_prefix=@exec_prefix@ exec_prefix=@exec_prefix@
@ -7,16 +8,24 @@ sbindir=@sbindir@
sysconfdir=@sysconfdir@ sysconfdir=@sysconfdir@
localstatedir=@localstatedir@ localstatedir=@localstatedir@
srcdir=@srcdir@ srcdir=@srcdir@
abs_srcdir=@abs_srcdir@
abs_builddir=@abs_builddir@
CC = @CC@ CC = @CC@
AR = @AR@ AR = @AR@
RANLIB = @RANLIB@ RANLIB = @RANLIB@
JAVAC= @JAVAC@ JAVAC= @JAVAC@
SWIFTC= @SWIFTC@
SWIFTCFLAGS= @SWIFTCFLAGS@ -I. -I $(srcdir) SWIFTC= @SWIFTC@
SWIFTCFLAGS= @SWIFTCFLAGS@ -I$(abs_builddir) -I$(abs_srcdir)
SWIFTLIBS= @LIBS@ SWIFTLIBS= @LIBS@
SWIFT_BUILD= @SWIFT_BUILD@
SWIFT_BUILD_FLAGS= $(addprefix -Xswiftc , $(SWIFTCFLAGS))
SWIFT_MODULE_NAME= ServalDNA SWIFT_MODULE_NAME= ServalDNA
SWIFT_PACKAGE_DIR= $(srcdir)/swift-daemon-api
SWIFT_BUILD_DIR= $(abs_builddir)/swift-daemon-api/build
INSTALL= install INSTALL= install
INSTALL_PROGRAM= $(INSTALL) INSTALL_PROGRAM= $(INSTALL)
@ -166,13 +175,26 @@ endif # $(JAVAC)
ifneq ($(SWIFTC),) # Only provide Swift targets if the Swift compiler is available. ifneq ($(SWIFTC),) # Only provide Swift targets if the Swift compiler is available.
all: swift-client-api all: swift-daemon-api swift-client-api
test: servaldswift test: servaldswift
clean: swift-client-api-clean clean: swift-daemon-api-clean swift-client-api-clean
.PHONY: swift-client-api swift-client-api-clean .PHONY: swift-daemon-api swift-daemon-api-clean swift-client-api swift-client-api-clean
libServalDNA.a $(SWIFT_MODULE_NAME).swiftmodule $(SWIFT_MODULE_NAME).swiftdoc: swift-daemon-api
cp $(SWIFT_BUILD_DIR)/debug/$(notdir $@) $@
swift-daemon-api:
mkdir -p $(SWIFT_BUILD_DIR) && \
cd $(SWIFT_PACKAGE_DIR) && \
$(SWIFT_BUILD) --build-path $(SWIFT_BUILD_DIR) $(SWIFT_BUILD_FLAGS) $(SWIFTDEFS)
swift-daemon-api-clean:
$(RM) -r $(SWIFT_BUILD_DIR) \
$(SWIFT_MODULE_NAME).swiftmodule \
$(SWIFT_MODULE_NAME).swiftdoc
swift-client-api: swift-client-api:
@mkdir -p swift-client-api @mkdir -p swift-client-api
@ -369,12 +391,15 @@ ifneq ($(SWIFTC),) # Only provide Swift targets if the Swift compiler is availab
# package. # package.
servaldswift: $(srcdir)/servaldswift.swift \ servaldswift: $(srcdir)/servaldswift.swift \
$(OBJSDIR_SERVALD)/servald_features.o \ $(OBJSDIR_SERVALD)/servald_features.o \
$(OBJSDIR_SERVALD)/log_output_console.o \
$(OBJSDIR_SERVALD)/log_output_file.o \ $(OBJSDIR_SERVALD)/log_output_file.o \
libservaldaemon.a \ libservaldaemon.a \
$(srcdir)/module.modulemap $(SWIFT_MODULE_NAME).swiftmodule \
libServalDNA.a
@echo SWIFT $@ @echo SWIFT $@
@$(SWIFTC) -emit-executable $(SWIFTCFLAGS) $(SWIFTDEFS) -o $@ $(filter %.swift, $^) $(filter %.o, $^) $(filter %.a, $^) $(SWIFTLIBS) @$(SWIFTC) -emit-executable -o $@ \
$(SWIFTCFLAGS) $(SWIFTDEFS) \
$(filter %.swift, $^) $(filter %.o, $^) $(filter %.a, $^) \
$(SWIFTLIBS)
endif # $(SWIFTC) endif # $(SWIFTC)
@ -413,7 +438,7 @@ libservalclient.a: _servalclient.a $(LIBSODIUM_A)
libservalclient.so: $(LIB_SERVAL_OBJS) $(OBJSDIR_TOOLS)/version.o $(LIBSODIUM_A) libservalclient.so: $(LIB_SERVAL_OBJS) $(OBJSDIR_TOOLS)/version.o $(LIBSODIUM_A)
@echo LINK $@ @echo LINK $@
@$(CC) -Wall -shared -o $@ $(LDFLAGS) $(filter %.o, $^) $(filter %.a, $^) @$(CC) -Wall -shared -o $@ $(LDFLAGS) $(filter %.o %.a, $^) $(filter %.a, $^)
.INTERMEDIATE: _monitorclient.a .INTERMEDIATE: _monitorclient.a
_monitorclient.a: $(MONITOR_CLIENT_OBJS) $(OBJSDIR_TOOLS)/version.o _monitorclient.a: $(MONITOR_CLIENT_OBJS) $(OBJSDIR_TOOLS)/version.o

View File

@ -28,6 +28,10 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "log.h" #include "log.h"
#include "str.h" #include "str.h"
/* An instance of struct cli_vtable that prints all fields to a stdio stream. This is used by the
* 'servald' daemon to print its output on stdout.
*/
static FILE *stdio_fp(struct cli_context *context) static FILE *stdio_fp(struct cli_context *context)
{ {
return ((struct cli_context_stdio *)(context->context))->fp; return ((struct cli_context_stdio *)(context->context))->fp;

View File

@ -241,10 +241,10 @@ Serval DNA supports [Swift][], the language that Apple recommend for developing
iOS apps for their mobile devices such as phones and tablets. The iOS apps for their mobile devices such as phones and tablets. The
`./configure` script [generated from configure.ac](#autotools) detects whether `./configure` script [generated from configure.ac](#autotools) detects whether
a [Swift 4][] compiler is present, or failing that, a [Swift 3][] compiler, and a [Swift 4][] compiler is present, or failing that, a [Swift 3][] compiler, and
if so, then produces a Makefile that will compile if so, then produces a Makefile that will compile [servaldswift.swift][] into
[servaldswift.swift](../servaldswift.swift) into the *servaldswift* executable, the *servaldswift* executable, to prove that the Swift [module
to prove that the Swift [module map](../module.modulemap) allows Swift source map](../module.modulemap) allows Swift source code to invoke internal Serval
code to invoke internal Serval DNA functions. DNA functions.
The `./configure` script can be passed the following variables, either as The `./configure` script can be passed the following variables, either as
environment variables or using the `VARNAME=value` syntax on its command line: environment variables or using the `VARNAME=value` syntax on its command line:
@ -255,12 +255,45 @@ environment variables or using the `VARNAME=value` syntax on its command line:
* `SWIFTCFLAGS` extra command-line arguments to pass to the Swift compiler; * `SWIFTCFLAGS` extra command-line arguments to pass to the Swift compiler;
analogous to `CFLAGS` for the C compiler analogous to `CFLAGS` for the C compiler
Swift Daemon API
----------------
Serval DNA provides a *Swift Daemon API* as a [Swift module][] called
**ServalDNA**, which provides access to some of the internal APIs of the Serval
DNA daemon by wrapping direct function invocations in Swift classes.
The Swift daemon API is written as a set of [Swift][] "wrappers" around
internal functions and constants that are declared in C header files. Those
internals are exposed to Swift by the Swift [module map](../module.modulemap),
which defines a [Swift module][] called **servald** that has the following
submodules:
* **servald.log** the Serval DNA logging API, including the log output API
* **servald.cli** the Serval DNA [CLI API][] and the daemon's command-line
entry point
The [CliContext][] Swift class provides an object-oriented interface to the
[CLI API][]. To capture the output from any CLI command, a Swift program can
simply subclass [CliContext][] and override its `write()` method and any other
methods as needed, then pass an instance of that subclass to the
[serval\_commandline\_main][] function. An example of how to do this is in
[servaldswift.swift][], which uses an instance of the [CliContextFile][]
subclass to print its output on standard output via a buffer.
[servaldswift.swift][] shows how to capture Serval DNA log output in Swift
code, by providing an implementation of the delegate log output `print()`
function. This works because [Makefile.in][] includes `log_output_delegate.o`
in the link for the *servaldswift* executable, and omits the other log outputs.
Swift Client API Swift Client API
---------------- ----------------
Serval DNA provides a *Swift Client API* as a [Swift module][] called Serval DNA provides a *Swift Client API* as a [Swift module][] called
**ServalClient**, which provides access to the services of the Serval DNA **ServalClient**, which provides access to the services of the Serval DNA
daemon through its [REST API][]. daemon through its [REST API][]. Once an iOS app has started a thread that is
running the daemon (invoked via the [Swift Daemon API](#swift-daemon-api)), the
client API can be used to communicate with the running daemon thread.
The Swift client API is written entirely in [Swift][] using the [URLSession][] The Swift client API is written entirely in [Swift][] using the [URLSession][]
Foundation class and related classes as the HTTP client. The API is covered Foundation class and related classes as the HTTP client. The API is covered
@ -307,10 +340,16 @@ Available under the [Creative Commons Attribution 4.0 International licence][CC
[CC BY 4.0]: ../LICENSE-DOCUMENTATION.md [CC BY 4.0]: ../LICENSE-DOCUMENTATION.md
[Serval DNA]: ../README.md [Serval DNA]: ../README.md
[build]: ../INSTALL.md [build]: ../INSTALL.md
[CLI API]: ./CLI-API.md
[REST API]: ./REST-API.md [REST API]: ./REST-API.md
[Keyring REST API]: ./REST-API-Keyring.md [Keyring REST API]: ./REST-API-Keyring.md
[test scripts]: ./Testing.md [test scripts]: ./Testing.md
[configure.ac]: ../configure.ac [configure.ac]: ../configure.ac
[Makefile.in]: ../Makefile.in
[servaldswift.swift]: ../servaldswift.swift
[CliContext]: ../swift-daemon-api/Sources/CliContext.swift
[CliContextFile]: ../swift-daemon-api/Sources/CliContextFile.swift
[serval\_commandline\_main]: ../swift-daemon-api/Sources/commandline.swift
[autoconf]: http://www.gnu.org/software/autoconf/autoconf.html [autoconf]: http://www.gnu.org/software/autoconf/autoconf.html
[autoconf macro archive]: http://www.gnu.org/software/autoconf-archive/ [autoconf macro archive]: http://www.gnu.org/software/autoconf-archive/
[GNU M4]: http://www.gnu.org/software/m4/m4.html [GNU M4]: http://www.gnu.org/software/m4/m4.html

View File

@ -268,7 +268,8 @@ JNIEXPORT jint JNICALL Java_org_servalproject_servaldna_ServalDCommand_rawComman
return (jint) status; return (jint) status;
} }
/* Output primitives for JNI. /* An instance of struct cli_vtable that passes all output fields to an IJniResults
* interface callback.
*/ */
static struct jni_context *jni_context(struct cli_context *cli_context) static struct jni_context *jni_context(struct cli_context *cli_context)

134
log_output_delegate.c Normal file
View File

@ -0,0 +1,134 @@
/*
Serval DNA logging output to a delegate
Copyright 2017 Flinders University
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "log.h"
#include "log_output.h"
#include "log_output_delegate.h"
#include "strbuf.h"
/* An implementation of the Serval logging API that constructs log messages in
* a buffer then passes the buffer to a delegate.
*/
/* Private state for delegate log output.
*/
struct log_output_delegate_state {
bool_t opened;
// Buffer to hold log messages before the log file is open and ready for writing:
struct strbuf strbuf;
char buf[8192];
};
static struct log_output_delegate_state static_state = {
.opened = 0,
.strbuf = STRUCT_STRBUF_EMPTY,
.buf = ""
};
/* Functions for querying configuration.
*/
static int log_delegate_minimum_level(const struct log_output *UNUSED(out))
{
return serval_log_delegate.minimum_level;
}
static bool_t log_delegate_show_pid(const struct log_output *UNUSED(out))
{
return serval_log_delegate.show_pid;
}
static bool_t log_delegate_show_time(const struct log_output *UNUSED(out))
{
return serval_log_delegate.show_time;
}
/* Log output operations.
*/
static inline struct log_output_delegate_state *_state(struct log_output *out)
{
assert(out->state);
return (struct log_output_delegate_state *)(out->state);
}
static bool_t is_log_delegate_available(const struct log_output_iterator *UNUSED(it))
{
return serval_log_delegate.print != NULL;
}
static void log_delegate_open(struct log_output_iterator *it)
{
struct log_output_delegate_state *state = _state(*it->output);
if (!state->opened) {
state->opened = 1;
if (serval_log_delegate.show_prolog)
serval_log_print_prolog(it);
}
}
static void log_delegate_start_line(struct log_output_iterator *it, int UNUSED(level))
{
struct log_output_delegate_state *state = _state(*it->output);
strbuf sb = &state->strbuf;
assert(strbuf_len(sb) == 0);
strbuf_init(sb, state->buf, sizeof state->buf);
it->xpf = XPRINTF_STRBUF(sb);
}
static void log_delegate_end_line(struct log_output_iterator *it, int level)
{
struct log_output_delegate_state *state = _state(*it->output);
strbuf sb = &state->strbuf;
serval_log_delegate.print(level, strbuf_str(sb), strbuf_overrun(sb));
strbuf_reset(sb);
}
static void flush_log_delegate(struct log_output_iterator *UNUSED(it))
{
if (serval_log_delegate.flush)
serval_log_delegate.flush();
}
static struct log_output static_log_output = {
.minimum_level = log_delegate_minimum_level,
.show_pid = log_delegate_show_pid,
.show_time = log_delegate_show_time,
.state = &static_state,
.open = log_delegate_open,
.capture_fd = NULL,
.is_available = is_log_delegate_available,
.start_line = log_delegate_start_line,
.end_line = log_delegate_end_line,
.flush = flush_log_delegate,
.close = NULL
};
DEFINE_LOG_OUTPUT(&static_log_output);
// These are defaults only; the delegate fills this in as it wishes.
struct log_delegate serval_log_delegate = {
.minimum_level = LOG_LEVEL_INFO,
.show_prolog = 1,
.show_pid = 0,
.show_time = 1,
.print = NULL,
.flush = NULL
};

36
log_output_delegate.h Normal file
View File

@ -0,0 +1,36 @@
/*
Serval DNA logging output to a delegate
Copyright (C) 2017 Flinders University
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef __SERVAL_DNA__LOG_OUTPUT_DELEGATE_H
#define __SERVAL_DNA__LOG_OUTPUT_DELEGATE_H
#include "lang.h" // for bool_t
struct log_delegate {
bool_t show_prolog;
int minimum_level;
bool_t show_pid;
bool_t show_time;
void (*print)(int level, const char *message, bool_t overrun);
void (*flush)();
};
extern struct log_delegate serval_log_delegate;
#endif // __SERVAL_DNA__LOG_OUTPUT_DELEGATE_H

View File

@ -1,5 +1,5 @@
/* /*
Serval DNA module map Serval DNA Swift module map
Copyright (C) 2016-2017 Flinders University Copyright (C) 2016-2017 Flinders University
This program is free software; you can redistribute it and/or This program is free software; you can redistribute it and/or
@ -21,7 +21,14 @@ module servald {
requires tls // thread-local storage requires tls // thread-local storage
module main { module cli {
header "servald_main.h" header "commandline.h"
header "cli.h"
}
module log {
header "log.h"
header "log_output.h"
header "log_output_delegate.h"
} }
} }

View File

@ -1,14 +1,54 @@
/*
Serval DNA daemon in Swift
Copyright 2017 Flinders University
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import Foundation import Foundation
import servald.main import ServalDNA
import servald.log
public func serval_daemon_main(args: [String]) -> CInt { // Logging
// print "args = \(args)" //
var argv = args.map { strdup($0) } // A simplistic console log outputter that writes to standard error and is not
argv.append(nil) // configurable. Note that log_output_console.o is not linked into
defer { // servaldswift, in order to avoid duplicate log outputs on standard error.
argv.forEach { free($0) }
} private func logPrint(_ level: CInt, _ message: UnsafePointer<CChar>?, _ overrun: Int8) {
return servald_main(CInt(argv.count - 1), &argv) let level_text = String(cString: serval_log_level_prefix_string(level)!)
let message_text = String(cString: message!)
FileHandle.standardError.write("\(level_text) \(message_text)\n".data(using:.utf8)!)
} }
exit(serval_daemon_main(args: CommandLine.arguments)) serval_log_delegate.print = logPrint
serval_log_delegate.minimum_level = LOG_LEVEL_WARN
serval_log_delegate.show_prolog = 1
serval_log_delegate.show_pid = 1
serval_log_delegate.show_time = 1
// Output
var contextFile = CliContextFile(FileHandle.standardOutput)
// Invocation
let status = serval_commandline_main(context: contextFile, args: CommandLine.arguments)
// Cleanup
contextFile.flush()
exit(status)

View File

@ -62,6 +62,7 @@ SERVAL_DAEMON_SOURCES = \
keyring_restful.c \ keyring_restful.c \
log_output_console.c \ log_output_console.c \
log_output_file.c \ log_output_file.c \
log_output_delegate.c \
lsif.c \ lsif.c \
radio_link.c \ radio_link.c \
meshms.c \ meshms.c \
@ -131,6 +132,12 @@ SERVAL_DAEMON_JNI_SOURCES = \
jni_instance.c \ jni_instance.c \
jni_server.c jni_server.c
SERVAL_DAEMON_SWIFT_SOURCES = \
swift_main.swift \
swift_cli.swift \
swift_cli_stdio.swift \
swift_log.swift
MDP_CLIENT_SOURCES = \ MDP_CLIENT_SOURCES = \
mdp_client.c mdp_client.c

View File

@ -23,16 +23,18 @@ SWIFT_BUILD_FLAGS= $(addprefix -Xswiftc , $(SWIFTCFLAGS))
DEFS= @DEFS@ DEFS= @DEFS@
SWIFTDEFS= $(addprefix -Xcc , $(DEFS)) SWIFTDEFS= $(addprefix -Xcc , $(DEFS))
.PHONY: all check check_swiftc check_swift_build swiftmodule clean .PHONY: all
all: check package swift-client-util
all: check swiftmodule swift-client-util
.PHONY: check
check: check_swiftc check_swift_build check: check_swiftc check_swift_build
.PHONY: check_swiftc
check_swiftc: check_swiftc:
@if [ -z "$(SWIFTC)" ]; then echo "No swift compiler configured" >&2; exit 1; fi @if [ -z "$(SWIFTC)" ]; then echo "No swift compiler configured" >&2; exit 1; fi
@if ! $(SWIFTC) -version >/dev/null; then echo "Swift compiler not executable" >&2; exit 1; fi @if ! $(SWIFTC) -version >/dev/null; then echo "Swift compiler not executable" >&2; exit 1; fi
.PHONY: check_swift_build
check_swift_build: check_swift_build:
@if [ -z "$(SWIFT_BUILD)" ]; then echo "No swift package manager configured" >&2; exit 1; fi @if [ -z "$(SWIFT_BUILD)" ]; then echo "No swift package manager configured" >&2; exit 1; fi
@if ! $(SWIFT_BUILD) -h >/dev/null; then echo "Swift package manager not executable" >&2; exit 1; fi @if ! $(SWIFT_BUILD) -h >/dev/null; then echo "Swift package manager not executable" >&2; exit 1; fi
@ -40,17 +42,19 @@ check_swift_build:
# The Swift 3 package manager --chdir option was replaced with --package-path # The Swift 3 package manager --chdir option was replaced with --package-path
# in Swift 4, so to support both, avoid the options altogether and use "cd # in Swift 4, so to support both, avoid the options altogether and use "cd
# package-dir && swift build" instead. # package-dir && swift build" instead.
swiftmodule: .PHONY: package
package:
mkdir -p $(SWIFT_BUILD_DIR) && \ mkdir -p $(SWIFT_BUILD_DIR) && \
cd $(SWIFT_PACKAGE_DIR) && \ cd $(SWIFT_PACKAGE_DIR) && \
$(SWIFT_BUILD) --build-path $(SWIFT_BUILD_DIR) $(SWIFT_BUILD_FLAGS) $(SWIFTDEFS) $(SWIFT_BUILD) --build-path $(SWIFT_BUILD_DIR) $(SWIFT_BUILD_FLAGS) $(SWIFTDEFS)
$(SWIFT_BUILD_DIR)/debug/libServalClient.a: swiftmodule $(SWIFT_BUILD_DIR)/debug/libServalClient.a: package
swift-client-util: $(srcdir)/client_util.swift $(SWIFT_BUILD_DIR)/debug/libServalClient.a swift-client-util: $(srcdir)/client_util.swift $(SWIFT_BUILD_DIR)/debug/libServalClient.a
@echo SWIFT $@ @echo SWIFT $@
@$(SWIFTC) -emit-executable $(SWIFTCFLAGS) $(SWIFTDEFS) -I $(SWIFT_BUILD_DIR)/debug -o $@ $^ @$(SWIFTC) -emit-executable $(SWIFTCFLAGS) $(SWIFTDEFS) -I $(SWIFT_BUILD_DIR)/debug -o $@ $^
.PHONY: clean
clean: clean:
$(RM) -r $(SWIFT_BUILD_DIR) $(RM) -r $(SWIFT_BUILD_DIR)
$(RM) swift-client-util $(RM) swift-client-util

View File

@ -0,0 +1,9 @@
import PackageDescription
let package = Package(
name: "ServalDNA"
)
products.append(
Product(name: "ServalDNA", type: .Library(.Static), modules: "ServalDNA")
)

View File

@ -0,0 +1,158 @@
/*
Serval DNA Swift CLI output
Copyright 2017 Flinders University
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import Foundation
import servald.cli
private extension Data {
var hexUpper: String {
return map { String(format: "%02hhX", $0) }.joined()
}
}
open class CliContext {
public var cContext: cli_context = cli_context(context: nil, vtable: nil)
public init() {
cContext.context = UnsafeMutableRawPointer(Unmanaged.passUnretained(self).toOpaque())
cContext.vtable = UnsafeMutablePointer<cli_vtable>(&cliVtable)
}
// Subclasses override these as needed:
func delim(_ opt: String? = nil) {}
func write(_ data: Data) {}
func puts(_ str: String) {
self.write(str.data(using:.utf8)!)
}
func flush() {}
func putString(_ str: String, _ delim: String? = nil) {
self.puts(str)
self.delim(delim)
}
func putLong(_ value: Int64, _ delim: String? = nil) {
self.puts("\(value)")
self.delim(delim)
}
func putHexData(_ data: Data, _ delim: String? = nil) {
self.puts(data.hexUpper)
self.delim(delim)
}
func putBlob(_ data: Data, _ delim: String? = nil) {
self.write(data)
self.delim(delim)
}
func startTable(column_names: [String]) {
self.putLong(Int64(column_names.count))
for i in 0 ..< column_names.count {
self.putString(column_names[i], i == column_names.count - 1 ? nil : ":")
}
}
func endTable(row_count: Int) {}
func fieldName(_ name: String, _ delim: String? = nil) {
self.putString(name)
self.delim(delim)
}
}
private func _self(_ context: UnsafeMutablePointer<cli_context>?) -> CliContext {
return Unmanaged<CliContext>.fromOpaque(context!.pointee.context).takeUnretainedValue()
}
private func cliDelim(_ context: UnsafeMutablePointer<cli_context>?, _ opt: UnsafePointer<CChar>?) -> Void {
_self(context).delim(opt != nil ? String(cString: opt!) : nil)
}
private func cliWrite(_ context: UnsafeMutablePointer<cli_context>?, _ buf: UnsafePointer<CChar>?, _ len: Int) -> Void {
if buf != nil {
_self(context).write(Data(bytes: buf!, count: len))
}
}
private func cliPuts(_ context: UnsafeMutablePointer<cli_context>?, _ str: UnsafePointer<CChar>?) -> Void {
if str != nil {
_self(context).putString(String(cString: str!))
}
}
private func cliVprintf(_ context: UnsafeMutablePointer<cli_context>?, _ fmt: UnsafePointer<CChar>?, _ ap: CVaListPointer) -> Void {
let str = NSString(format: String(cString: fmt!), arguments: ap)
_self(context).putString(String(data: str.data(using: String.Encoding.utf16.rawValue)!, encoding:.utf16)!)
}
private func cliPutLong(_ context: UnsafeMutablePointer<cli_context>?, _ value: Int64, _ delim: UnsafePointer<CChar>?) -> Void {
_self(context).putLong(value, delim != nil ? String(cString: delim!) : nil)
}
private func cliPutString(_ context: UnsafeMutablePointer<cli_context>?, _ value: UnsafePointer<CChar>?, _ delim: UnsafePointer<CChar>?) -> Void {
_self(context).putString(value != nil ? String(cString: value!) : "",
delim != nil ? String(cString: delim!) : nil)
}
private func cliPutHexvalue(_ context: UnsafeMutablePointer<cli_context>?, _ buf: UnsafePointer<CUnsignedChar>?, _ len: Int, _ delim: UnsafePointer<CChar>?) -> Void {
_self(context).putHexData(buf != nil ? Data(bytes: buf!, count: len) : Data(),
delim != nil ? String(cString: delim!) : nil)
}
private func cliPutBlob(_ context: UnsafeMutablePointer<cli_context>?, _ blob: UnsafePointer<CUnsignedChar>?, _ len: Int, _ delim: UnsafePointer<CChar>?) -> Void {
_self(context).putBlob(blob != nil ? Data(bytes: UnsafeRawPointer(blob!).bindMemory(to: CChar.self, capacity: len), count: len) : Data(),
delim != nil ? String(cString: delim!) : nil)
}
private func cliStartTable(_ context: UnsafeMutablePointer<cli_context>?, _ column_count: Int, _ column_names: UnsafeMutablePointer<UnsafePointer<CChar>?>?) -> Void {
let names = (0..<column_count).map { String(cString: column_names![$0]!) }
_self(context).startTable(column_names: names)
}
private func cliEndTable(_ context: UnsafeMutablePointer<cli_context>?, _ row_count: Int) -> Void {
_self(context).endTable(row_count: row_count)
}
private func cliFieldName(_ context: UnsafeMutablePointer<cli_context>?, _ name: UnsafePointer<CChar>?, _ delim: UnsafePointer<CChar>?) -> Void {
_self(context).fieldName(name != nil ? String(cString: name!) : "", delim != nil ? String(cString: delim!) : nil)
}
private func cliFlush(_ context: UnsafeMutablePointer<cli_context>?) -> Void {
_self(context).flush()
}
private var cliVtable = cli_vtable(
delim: cliDelim,
write: cliWrite,
puts: cliPuts,
vprintf: cliVprintf,
put_long: cliPutLong,
put_string: cliPutString,
put_hexvalue: cliPutHexvalue,
put_blob: cliPutBlob,
start_table: cliStartTable,
end_table: cliEndTable,
field_name: cliFieldName,
flush: cliFlush
)

View File

@ -0,0 +1,54 @@
/*
Serval DNA Swift CLI output to stdio stream
Copyright 2017 Flinders University
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import Foundation
/* An instance of struct cli_vtable that prints all fields to a stdio stream
* via a buffer. This is the Swift equivalent of cli_stdio.c. It is used by
* the 'servaldswift' daemon to produce its output.
*/
open class CliContextFile: CliContext {
private var fileHandle: FileHandle
private var buffer: Data
private let bufsize: Int? = nil
public init(_ file: FileHandle) {
self.fileHandle = file
self.buffer = Data()
}
public override func delim(_ opt: String?) {
self.puts(ProcessInfo.processInfo.environment["SERVALD_OUTPUT_DELIMITER"] ?? opt ?? "\n")
}
public override func write(_ data: Data) {
self.buffer.append(data)
if self.bufsize != nil && self.buffer.count >= self.bufsize! {
self.flush()
}
}
public override func flush() {
if !self.buffer.isEmpty {
self.fileHandle.write(self.buffer)
self.buffer.removeAll()
}
}
}

View File

@ -0,0 +1,39 @@
/*
Serval DNA Swift command-line entry point
Copyright 2017 Flinders University
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import Foundation
import servald.cli
/* A Swift entry point to the Serval DNA daemon command-line entry point, which takes a
* [String] parameter, converts it into an argv array of C strings, and invokes
* the C main entry point with argv0, and argc/argv arguments.
*/
public func serval_commandline_main(context: CliContext, args: [String]) -> CInt {
var margv = args.map { strdup($0) }
margv.append(nil)
defer {
margv.forEach { free($0) }
}
let argv0 = margv[0]
margv.remove(at: 0)
let argv = margv.map { $0 != nil ? UnsafePointer<CChar>?($0!) : nil }
return argv.withUnsafeBufferPointer {
return commandline_main(&context.cContext, argv0, CInt(argv.count - 1), $0.baseAddress!)
}
}

View File

@ -0,0 +1,39 @@
import servald.log
private func serval_log(level: CInt, format: String, va_list: CVaListPointer) {
format.withCString { CString in
serval_vlogf(level, __whence, CString, va_list)
}
}
public func serval_log(level: CInt, text: String) {
text.withCString { CString in
withVaList([CString]) { va_list in
serval_log(level: level, format: "%s", va_list: va_list)
}
}
}
public func serval_log_fatal(_ text: String) {
serval_log(level: LOG_LEVEL_FATAL, text: text)
}
public func serval_log_error(_ text: String) {
serval_log(level: LOG_LEVEL_ERROR, text: text)
}
public func serval_log_warning(_ text: String) {
serval_log(level: LOG_LEVEL_WARN, text: text)
}
public func serval_log_hint(_ text: String) {
serval_log(level: LOG_LEVEL_HINT, text: text)
}
public func serval_log_info(_ text: String) {
serval_log(level: LOG_LEVEL_INFO, text: text)
}
public func serval_log_debug(_ text: String) {
serval_log(level: LOG_LEVEL_DEBUG, text: text)
}