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
*.suo
.*.sw?
*.swiftdoc
*.swiftmodule
/VERSION.txt
/configure
/autom4te.cache
@ -37,6 +39,7 @@ test.*.log
/java-api/Makefile
/java-api/classes/
/java-api/testclasses/
/swift-daemon-api/build/
/swift-client-api/Makefile
/swift-client-api/build/
/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
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.swiftdoc**
**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/
[Notes for Developers]: ./doc/Development.md
[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
[OpenWRT]: ./doc/OpenWRT.md
[Serval Infrastructure]: ./doc/Serval-Infrastructure.md

View File

@ -1,4 +1,5 @@
# Makefile.in for Serval DNA daemon and libraries
# nnnn
# vim: noet ts=8 sts=0 sw=8
prefix=@prefix@
exec_prefix=@exec_prefix@
@ -7,16 +8,24 @@ sbindir=@sbindir@
sysconfdir=@sysconfdir@
localstatedir=@localstatedir@
srcdir=@srcdir@
abs_srcdir=@abs_srcdir@
abs_builddir=@abs_builddir@
CC = @CC@
AR = @AR@
RANLIB = @RANLIB@
JAVAC= @JAVAC@
SWIFTC= @SWIFTC@
SWIFTCFLAGS= @SWIFTCFLAGS@ -I. -I $(srcdir)
SWIFTC= @SWIFTC@
SWIFTCFLAGS= @SWIFTCFLAGS@ -I$(abs_builddir) -I$(abs_srcdir)
SWIFTLIBS= @LIBS@
SWIFT_BUILD= @SWIFT_BUILD@
SWIFT_BUILD_FLAGS= $(addprefix -Xswiftc , $(SWIFTCFLAGS))
SWIFT_MODULE_NAME= ServalDNA
SWIFT_PACKAGE_DIR= $(srcdir)/swift-daemon-api
SWIFT_BUILD_DIR= $(abs_builddir)/swift-daemon-api/build
INSTALL= install
INSTALL_PROGRAM= $(INSTALL)
@ -166,13 +175,26 @@ endif # $(JAVAC)
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
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:
@mkdir -p swift-client-api
@ -369,12 +391,15 @@ ifneq ($(SWIFTC),) # Only provide Swift targets if the Swift compiler is availab
# package.
servaldswift: $(srcdir)/servaldswift.swift \
$(OBJSDIR_SERVALD)/servald_features.o \
$(OBJSDIR_SERVALD)/log_output_console.o \
$(OBJSDIR_SERVALD)/log_output_file.o \
libservaldaemon.a \
$(srcdir)/module.modulemap
$(SWIFT_MODULE_NAME).swiftmodule \
libServalDNA.a
@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)
@ -413,7 +438,7 @@ libservalclient.a: _servalclient.a $(LIBSODIUM_A)
libservalclient.so: $(LIB_SERVAL_OBJS) $(OBJSDIR_TOOLS)/version.o $(LIBSODIUM_A)
@echo LINK $@
@$(CC) -Wall -shared -o $@ $(LDFLAGS) $(filter %.o, $^) $(filter %.a, $^)
@$(CC) -Wall -shared -o $@ $(LDFLAGS) $(filter %.o %.a, $^) $(filter %.a, $^)
.INTERMEDIATE: _monitorclient.a
_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 "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)
{
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
`./configure` script [generated from configure.ac](#autotools) detects whether
a [Swift 4][] compiler is present, or failing that, a [Swift 3][] compiler, and
if so, then produces a Makefile that will compile
[servaldswift.swift](../servaldswift.swift) into the *servaldswift* executable,
to prove that the Swift [module map](../module.modulemap) allows Swift source
code to invoke internal Serval DNA functions.
if so, then produces a Makefile that will compile [servaldswift.swift][] into
the *servaldswift* executable, to prove that the Swift [module
map](../module.modulemap) allows Swift source code to invoke internal Serval
DNA functions.
The `./configure` script can be passed the following variables, either as
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;
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
----------------
Serval DNA provides a *Swift Client API* as a [Swift module][] called
**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][]
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
[Serval DNA]: ../README.md
[build]: ../INSTALL.md
[CLI API]: ./CLI-API.md
[REST API]: ./REST-API.md
[Keyring REST API]: ./REST-API-Keyring.md
[test scripts]: ./Testing.md
[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 macro archive]: http://www.gnu.org/software/autoconf-archive/
[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;
}
/* 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)

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
This program is free software; you can redistribute it and/or
@ -21,7 +21,14 @@ module servald {
requires tls // thread-local storage
module main {
header "servald_main.h"
module cli {
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 @@
import Foundation
import servald.main
/*
Serval DNA daemon in Swift
Copyright 2017 Flinders University
public func serval_daemon_main(args: [String]) -> CInt {
// print "args = \(args)"
var argv = args.map { strdup($0) }
argv.append(nil)
defer {
argv.forEach { free($0) }
}
return servald_main(CInt(argv.count - 1), &argv)
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 ServalDNA
import servald.log
// Logging
//
// A simplistic console log outputter that writes to standard error and is not
// configurable. Note that log_output_console.o is not linked into
// servaldswift, in order to avoid duplicate log outputs on standard error.
private func logPrint(_ level: CInt, _ message: UnsafePointer<CChar>?, _ overrun: Int8) {
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 \
log_output_console.c \
log_output_file.c \
log_output_delegate.c \
lsif.c \
radio_link.c \
meshms.c \
@ -131,6 +132,12 @@ SERVAL_DAEMON_JNI_SOURCES = \
jni_instance.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.c

View File

@ -23,16 +23,18 @@ SWIFT_BUILD_FLAGS= $(addprefix -Xswiftc , $(SWIFTCFLAGS))
DEFS= @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
check: check_swiftc check_swift_build
.PHONY: check
check: check_swiftc check_swift_build
.PHONY: check_swiftc
check_swiftc:
@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
.PHONY: check_swift_build
check_swift_build:
@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
@ -40,17 +42,19 @@ check_swift_build:
# 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
# package-dir && swift build" instead.
swiftmodule:
.PHONY: package
package:
mkdir -p $(SWIFT_BUILD_DIR) && \
cd $(SWIFT_PACKAGE_DIR) && \
$(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
@echo SWIFT $@
@$(SWIFTC) -emit-executable $(SWIFTCFLAGS) $(SWIFTDEFS) -I $(SWIFT_BUILD_DIR)/debug -o $@ $^
.PHONY: clean
clean:
$(RM) -r $(SWIFT_BUILD_DIR)
$(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)
}