mirror of
https://github.com/servalproject/serval-dna.git
synced 2024-12-18 20:57:56 +00:00
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:
parent
3ffa4b10af
commit
a79156c4d0
3
.gitignore
vendored
3
.gitignore
vendored
@ -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
|
||||
|
@ -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
|
||||
|
43
Makefile.in
43
Makefile.in
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
134
log_output_delegate.c
Normal 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
36
log_output_delegate.h
Normal 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
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
9
swift-daemon-api/Package.swift
Normal file
9
swift-daemon-api/Package.swift
Normal file
@ -0,0 +1,9 @@
|
||||
import PackageDescription
|
||||
|
||||
let package = Package(
|
||||
name: "ServalDNA"
|
||||
)
|
||||
|
||||
products.append(
|
||||
Product(name: "ServalDNA", type: .Library(.Static), modules: "ServalDNA")
|
||||
)
|
158
swift-daemon-api/Sources/CliContext.swift
Normal file
158
swift-daemon-api/Sources/CliContext.swift
Normal 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
|
||||
)
|
54
swift-daemon-api/Sources/CliContextFile.swift
Normal file
54
swift-daemon-api/Sources/CliContextFile.swift
Normal 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()
|
||||
}
|
||||
}
|
||||
}
|
39
swift-daemon-api/Sources/commandline.swift
Normal file
39
swift-daemon-api/Sources/commandline.swift
Normal 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!)
|
||||
}
|
||||
}
|
39
swift-daemon-api/Sources/log.swift
Normal file
39
swift-daemon-api/Sources/log.swift
Normal 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)
|
||||
}
|
Loading…
Reference in New Issue
Block a user