Migrate and refactor more interfaces from batphone

This commit is contained in:
Jeremy Lakeman 2014-02-19 15:35:08 +10:30
parent 42200c2bcb
commit 46ef40cf23
20 changed files with 960 additions and 447 deletions

View File

@ -128,6 +128,8 @@ static int outv_growbuf(struct cli_context *context, size_t needed)
static int put_blob(struct cli_context *context, jbyte *value, jsize length){
jbyteArray arr = NULL;
if (context->jni_exception)
return -1;
if (value && length>0){
arr = (*context->jni_env)->NewByteArray(context->jni_env, length);
if (arr == NULL || (*context->jni_env)->ExceptionOccurred(context->jni_env)) {
@ -143,7 +145,7 @@ static int put_blob(struct cli_context *context, jbyte *value, jsize length){
(*context->jni_env)->CallVoidMethod(context->jni_env, context->jniResults, putBlob, arr);
if ((*context->jni_env)->ExceptionOccurred(context->jni_env)) {
context->jni_exception = 1;
return WHY("Exception thrown from CallVoidMethod()");
return WHY("Exception thrown from CallVoidMethod(putBlob)");
}
if (arr)
(*context->jni_env)->DeleteLocalRef(context->jni_env, arr);
@ -287,6 +289,7 @@ int parseCommandLine(struct cli_context *context, const char *argv0, int argc, c
// Load configuration so that log messages can get out.
cf_reload_permissive();
NOWHENCE(HINTF("Run \"%s help\" for more information.", argv0 ? argv0 : "servald"));
result =-1;
break;
default:
// Load configuration so that log error messages can get out.
@ -389,10 +392,13 @@ void cli_columns(struct cli_context *context, int columns, const char *names[])
{
#ifdef HAVE_JNI_H
if (context && context->jni_env) {
if (context->jni_exception)
return;
(*context->jni_env)->CallVoidMethod(context->jni_env, context->jniResults, startResultSet, columns);
if ((*context->jni_env)->ExceptionOccurred(context->jni_env)) {
context->jni_exception = 1;
WHY("Exception thrown from CallVoidMethod()");
WHY("Exception thrown from CallVoidMethod(startResultSet)");
return;
}
int i;
@ -405,6 +411,11 @@ void cli_columns(struct cli_context *context, int columns, const char *names[])
}
(*context->jni_env)->CallVoidMethod(context->jni_env, context->jniResults, setColumnName, i, str);
(*context->jni_env)->DeleteLocalRef(context->jni_env, str);
if ((*context->jni_env)->ExceptionOccurred(context->jni_env)) {
context->jni_exception = 1;
WHY("Exception thrown from CallVoidMethod(setColumnName)");
return;
}
}
return;
}
@ -425,6 +436,8 @@ void cli_field_name(struct cli_context *context, const char *name, const char *d
{
#ifdef HAVE_JNI_H
if (context && context->jni_env) {
if (context->jni_exception)
return;
jstring str = (jstring)(*context->jni_env)->NewStringUTF(context->jni_env, name);
if (str == NULL) {
context->jni_exception = 1;
@ -433,6 +446,11 @@ void cli_field_name(struct cli_context *context, const char *name, const char *d
}
(*context->jni_env)->CallVoidMethod(context->jni_env, context->jniResults, setColumnName, -1, str);
(*context->jni_env)->DeleteLocalRef(context->jni_env, str);
if ((*context->jni_env)->ExceptionOccurred(context->jni_env)) {
context->jni_exception = 1;
WHY("Exception thrown from CallVoidMethod(setColumnName)");
return;
}
return;
}
#endif
@ -443,7 +461,13 @@ void cli_field_name(struct cli_context *context, const char *name, const char *d
void cli_put_long(struct cli_context *context, int64_t value, const char *delim){
#ifdef HAVE_JNI_H
if (context && context->jni_env) {
if (context->jni_exception)
return;
(*context->jni_env)->CallVoidMethod(context->jni_env, context->jniResults, putLong, value);
if ((*context->jni_env)->ExceptionOccurred(context->jni_env)) {
context->jni_exception = 1;
WHY("Exception thrown from CallVoidMethod(putLong)");
}
return;
}
#endif
@ -454,6 +478,8 @@ void cli_put_long(struct cli_context *context, int64_t value, const char *delim)
void cli_put_string(struct cli_context *context, const char *value, const char *delim){
#ifdef HAVE_JNI_H
if (context && context->jni_env) {
if (context->jni_exception)
return;
jstring str = NULL;
if (value){
str = (jstring)(*context->jni_env)->NewStringUTF(context->jni_env, value);
@ -465,6 +491,10 @@ void cli_put_string(struct cli_context *context, const char *value, const char *
}
(*context->jni_env)->CallVoidMethod(context->jni_env, context->jniResults, putString, str);
(*context->jni_env)->DeleteLocalRef(context->jni_env, str);
if ((*context->jni_env)->ExceptionOccurred(context->jni_env)) {
context->jni_exception = 1;
WHY("Exception thrown from CallVoidMethod(putLong)");
}
return;
}
#endif
@ -488,12 +518,14 @@ void cli_put_hexvalue(struct cli_context *context, const unsigned char *value, i
void cli_row_count(struct cli_context *context, int rows){
#ifdef HAVE_JNI_H
if (context && context->jni_env) {
if (context->jni_exception)
return;
(*context->jni_env)->CallVoidMethod(context->jni_env, context->jniResults, totalRowCount, rows);
if ((*context->jni_env)->ExceptionOccurred(context->jni_env)) {
context->jni_exception = 1;
WHY("Exception thrown from CallVoidMethod()");
return;
}
return;
}
#endif
}
@ -713,6 +745,14 @@ int app_dna_lookup(const struct cli_parsed *parsed, struct cli_context *context)
time_ms_t now;
int interval=125;
const char *names[]={
"uri",
"did",
"name"
};
cli_columns(context, 3, names);
size_t rowcount = 0;
while (timeout > (now = gettime_ms())){
if ((last_tx+interval)<now){
lookup_send_request(mdp_sockfd, &srcsid, port, NULL, did);
@ -750,6 +790,7 @@ int app_dna_lookup(const struct cli_parsed *parsed, struct cli_context *context)
cli_put_string(context, uri, ":");
cli_put_string(context, did, ":");
cli_put_string(context, name, "\n");
rowcount++;
if (one_reply){
timeout=now;
@ -775,6 +816,7 @@ int app_dna_lookup(const struct cli_parsed *parsed, struct cli_context *context)
}
overlay_mdp_client_close(mdp_sockfd);
cli_row_count(context, rowcount);
return 0;
}
@ -2102,6 +2144,15 @@ int app_keyring_list(const struct cli_parsed *parsed, struct cli_context *contex
keyring_file *k = keyring_open_instance_cli(parsed);
if (!k)
return -1;
const char *names[]={
"sid",
"did",
"name"
};
cli_columns(context, 3, names);
size_t rowcount = 0;
unsigned cn, in;
for (cn = 0; cn < k->context_count; ++cn)
for (in = 0; in < k->contexts[cn]->identity_count; ++in) {
@ -2113,9 +2164,11 @@ int app_keyring_list(const struct cli_parsed *parsed, struct cli_context *contex
cli_put_string(context, alloca_tohex_sid_t(*sidp), ":");
cli_put_string(context, did, ":");
cli_put_string(context, name, "\n");
rowcount++;
}
}
keyring_free(k);
cli_row_count(context, rowcount);
return 0;
}
@ -2397,6 +2450,12 @@ int app_id_list(const struct cli_parsed *parsed, struct cli_context *context)
goto end;
}
const char *names[]={
"sid"
};
cli_columns(context, 1, names);
size_t rowcount=0;
time_ms_t timeout=gettime_ms()+500;
while(1){
struct mdp_header rev_header;
@ -2410,8 +2469,8 @@ int app_id_list(const struct cli_parsed *parsed, struct cli_context *context)
}
if (len>=SID_SIZE){
rowcount++;
sid_t *id = (sid_t*)response_payload;
cli_field_name(context, "sid", ":");
cli_put_hexvalue(context, id->binary, sizeof(sid_t), "\n");
// TODO receive and decode other details about this identity
}
@ -2421,7 +2480,7 @@ int app_id_list(const struct cli_parsed *parsed, struct cli_context *context)
break;
}
}
cli_row_count(context, rowcount);
end:
mdp_close(mdp_sock);
return ret;
@ -2436,7 +2495,6 @@ int app_id_self(const struct cli_parsed *parsed, struct cli_context *context)
overlay_mdp_frame a;
bzero(&a, sizeof(overlay_mdp_frame));
int result;
int count=0;
a.packetTypeAndFlags=MDP_GETADDRS;
const char *arg = parsed->labelc ? parsed->labelv[0].text : "";
@ -2453,6 +2511,12 @@ int app_id_self(const struct cli_parsed *parsed, struct cli_context *context)
if ((mdp_sockfd = overlay_mdp_client_socket()) < 0)
return WHY("Cannot create MDP socket");
const char *names[]={
"sid"
};
cli_columns(context, 1, names);
size_t rowcount=0;
do{
result=overlay_mdp_send(mdp_sockfd, &a, MDP_AWAITREPLY, 5000);
if (result) {
@ -2470,15 +2534,14 @@ int app_id_self(const struct cli_parsed *parsed, struct cli_context *context)
}
unsigned i;
for(i=0;i<a.addrlist.frame_sid_count;i++) {
count++;
cli_printf(context, "%s", alloca_tohex_sid_t(a.addrlist.sids[i]));
cli_delim(context, "\n");
rowcount++;
cli_put_string(context, alloca_tohex_sid_t(a.addrlist.sids[i]), "\n");
}
/* get ready to ask for next block of SIDs */
a.packetTypeAndFlags=MDP_GETADDRS;
a.addrlist.first_sid=a.addrlist.last_sid+1;
}while(a.addrlist.frame_sid_count==MDP_MAX_SID_REQUEST);
cli_row_count(context, rowcount);
overlay_mdp_client_close(mdp_sockfd);
return 0;
}
@ -2705,6 +2768,7 @@ int app_route_print(const struct cli_parsed *parsed, struct cli_context *context
"Next hop"
};
cli_columns(context, 4, names);
size_t rowcount=0;
while(overlay_mdp_client_poll(mdp_sockfd, 200)){
overlay_mdp_frame rx;
@ -2743,9 +2807,11 @@ int app_route_print(const struct cli_parsed *parsed, struct cli_context *context
cli_put_string(context, strbuf_str(b), ":");
cli_put_string(context, p->interface_name, ":");
cli_put_string(context, alloca_tohex_sid_t(p->neighbour), "\n");
rowcount++;
}
}
overlay_mdp_client_close(mdp_sockfd);
cli_row_count(context, rowcount);
return 0;
}

View File

@ -96,6 +96,10 @@ public abstract class AbstractId {
return hashCode;
}
public void toByteBuffer(ByteBuffer buff){
buff.put(this.binary);
}
public String toHex(int offset, int len) {
StringBuilder sb = new StringBuilder();
for (int i = offset; i < offset + len && i < binary.length; i++) {

View File

@ -20,29 +20,7 @@
package org.servalproject.servaldna;
/**
* Indicates an internal (coding) error in the JNI interface to servald. Typically encountered when
* unpacking the outv strings returned by a servald operation, and indicates that the C code in
* servald that constructs the outv array is not consistent with the Java code that unpacks the outv
* strings.
*/
public class ServalDInterfaceError extends Error
{
private static final long serialVersionUID = 1L;
public ServalDInterfaceError(String message, ServalDResult result) {
super(message + ": " + result);
}
public ServalDInterfaceError(String message, ServalDResult result, Throwable cause) {
super(message + ": " + result, cause);
}
public ServalDInterfaceError(ServalDResult result, Throwable cause) {
super("" + result, cause);
}
public ServalDInterfaceError(String message, Throwable cause) {
super(message, cause);
}
public interface AsyncResult<T> {
public void result(T nextResult);
}

View File

@ -0,0 +1,56 @@
package org.servalproject.servaldna;
/**
* Created by jeremy on 18/02/14.
*/
public class JniResult implements IJniResults{
protected String columnName=null;
protected String command[];
protected int result;
void setCommand(String command[]){
this.command = command;
}
void setResult(int result) throws ServalDFailureException {
this.result = result;
if (result == ServalDCommand.STATUS_ERROR)
throw new ServalDFailureException("Command \"" + ServalDCommand.toString(command)+"\" returned an error");
}
public int getResult(){
return result;
}
@Override
public void startResultSet(int columns) {
}
@Override
public void setColumnName(int column, String name) {
columnName=name;
}
@Override
public void putString(String value) {
}
@Override
public void putBlob(byte[] value) {
}
@Override
public void putLong(long value) {
}
@Override
public void putDouble(double value) {
}
@Override
public void totalRowCount(int rows) {
}
}

View File

@ -0,0 +1,75 @@
package org.servalproject.servaldna;
/**
* Created by jeremy on 18/02/14.
*/
public abstract class JniResultList<T extends JniResult> implements IJniResults {
private String names[];
private int column =-1;
private int columns = -1;
private T currentRow;
private AsyncResult<T> results;
public JniResultList(AsyncResult<T> results){
this.results = results;
}
public abstract T create();
@Override
public void startResultSet(int columns) {
names = new String[columns];
this.columns = columns;
}
@Override
public void setColumnName(int column, String name) {
names[column]=name;
}
private void prepareCol(){
column++;
if (column==0)
currentRow = create();
currentRow.columnName = names[column];
}
private void endCol(){
if (column+1>=columns){
if (currentRow!=null)
results.result(currentRow);
currentRow=null;
column=-1;
}
}
@Override
public void putString(String value) {
prepareCol();
currentRow.putString(value);
endCol();
}
@Override
public void putBlob(byte[] value) {
prepareCol();
currentRow.putBlob(value);
endCol();
}
@Override
public void putLong(long value) {
prepareCol();
currentRow.putLong(value);
endCol();
}
@Override
public void putDouble(double value) {
prepareCol();
currentRow.putDouble(value);
endCol();
}
@Override
public void totalRowCount(int rows) {
}
}

View File

@ -1,17 +0,0 @@
package org.servalproject.servaldna;
import java.util.List;
public class JniResultsList extends AbstractJniResults implements IJniResults {
final List<byte[]> list;
private byte[] empty = new byte[0];
public JniResultsList(List<byte[]> list) {
this.list = list;
}
@Override
public void putBlob(byte[] value) {
list.add(value == null ? empty : value);
}
}

View File

@ -20,7 +20,11 @@
package org.servalproject.servaldna;
import java.io.File;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
public class ServalDCommand
{
@ -32,6 +36,28 @@ public class ServalDCommand
System.loadLibrary("serval");
}
public static final int STATUS_ERROR = 255;
public static String toString(String[] values) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < values.length; i++) {
if (i > 0)
sb.append(' ');
sb.append(values[i]);
}
return sb.toString();
}
// copies the semantics of serval-dna's confParseBoolean
private static boolean parseBoolean(String value, boolean defaultValue) {
if (value == null || "".equals(value))
return defaultValue;
return "off".compareToIgnoreCase(value) != 0
&& "no".compareToIgnoreCase(value) != 0
&& "false".compareToIgnoreCase(value) != 0
&& "0".compareToIgnoreCase(value) != 0;
}
/**
* Low-level JNI entry point into servald command line.
*
@ -39,8 +65,7 @@ public class ServalDCommand
* @param args The words to pass on the command line (ie, argv[1]...argv[n])
* @return The servald exit status code (normally 0 indicates success)
*/
private static native int rawCommand(IJniResults results, String[] args)
throws ServalDInterfaceError;
private static native int rawCommand(IJniResults results, String[] args);
/**
* Common entry point into servald command line.
@ -54,39 +79,577 @@ public class ServalDCommand
* @return The servald exit status code (normally0 indicates success)
*/
public static synchronized int command(final IJniResults callback, String... args)
throws ServalDInterfaceError
throws ServalDFailureException {
int ret = ServalDCommand.rawCommand(callback, args);
if (ret == STATUS_ERROR)
throw new ServalDFailureException("Command \"" + toString(args)+"\" returned an error");
return ret;
}
public static synchronized JniResult command(String... args)
throws ServalDFailureException {
JniResult result = new JniResult();
result.setCommand(args);
result.setResult(ServalDCommand.rawCommand(result, args));
return result;
}
public static class Status extends JniResult{
public int pid;
public int tries;
public String instancePath;
public String status;
@Override
public void putString(String value) {
if (columnName.equals("instancepath"))
instancePath=value;
if (columnName.equals("status"))
status=value;
}
@Override
public void putLong(long value) {
if (columnName.equals("pid"))
pid = (int)value;
if (columnName.equals("tries"))
tries = (int)value;
}
}
/** Start the servald server process if it is not already running.
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
public static Status serverStart(String execPath)
throws ServalDFailureException {
Status result = new Status();
result.setResult(command(result, "start", "exec", execPath));
return result;
}
public static Status serverStop()
throws ServalDFailureException {
Status result = new Status();
result.setResult(command(result, "stop"));
return result;
}
public static Status serverStatus()
throws ServalDFailureException {
Status result = new Status();
result.setResult(command(result, "status"));
return result;
}
public static class IdentityResult extends JniResult {
public String did;
public String name;
public SubscriberId subscriberId;
@Override
public void putString(String value) {
if (this.columnName.equals("did"))
this.did = value;
if (this.columnName.equals("name"))
this.name = value;
if (this.columnName.equals("sid"))
try {
this.subscriberId = new SubscriberId(value);
} catch (AbstractId.InvalidHexException e) {
e.printStackTrace();
}
}
@Override
public void putBlob(byte[] value) {
if (this.columnName.equals("sid"))
try {
this.subscriberId = new SubscriberId(value);
} catch (AbstractId.InvalidBinaryException e) {
e.printStackTrace();
}
}
}
public static IdentityResult keyringAdd()
throws ServalDFailureException {
IdentityResult result = new IdentityResult();
command(result, "keyring", "add");
return result;
}
public static IdentityResult keyringSetDidName(SubscriberId sid, String did, String name) throws ServalDFailureException
{
return ServalDCommand.rawCommand(callback, args);
IdentityResult result = new IdentityResult();
command(result, "keyring","set","did", sid.toHex(), did==null?"":did, name==null?"":name);
return result;
}
public static int keyringList(final AsyncResult<IdentityResult> results) throws ServalDFailureException
{
return keyringList(new JniResultList<IdentityResult>(results) {
@Override
public IdentityResult create() {
return new IdentityResult();
}
});
}
public static int keyringList(IJniResults results) throws ServalDFailureException{
return command(results, "keyring", "list");
}
public static IdentityResult reverseLookup(final SubscriberId sid) throws ServalDFailureException {
IdentityResult result = new IdentityResult();
command(result, "reverse", "lookup", sid.toHex());
return result;
}
public static class LookupResult extends JniResult {
public String did;
public String name;
public String uri;
@Override
public void putString(String value) {
if (this.columnName.equals("did"))
this.did = value;
if (this.columnName.equals("name"))
this.name = value;
if (this.columnName.equals("uri"))
this.uri = value;
}
}
public static int dnaLookup(AsyncResult<LookupResult> results, String did, int timeout) throws ServalDFailureException {
return dnaLookup(new JniResultList<LookupResult>(results) {
@Override
public LookupResult create() {
return new LookupResult();
}
}, did, timeout);
}
public static int dnaLookup(IJniResults results, String did, int timeout) throws ServalDFailureException {
return command(results, "dna", "lookup", did, Integer.toString(timeout));
}
public static class ManifestResult extends JniResult{
public BundleId manifestId;
public long version;
public long fileSize;
public FileHash fileHash;
public BundleKey bundleKey;
public long date;
public int crypt;
public String service;
public String name;
public boolean readonly=true;
public byte[] manifest;
public String secret;
public SubscriberId author;
public long rowId;
public long insertTime;
@Override
public void putString(String value) {
try {
if (value!="" && (columnName.equals("manifestid")||columnName.equals("id")))
manifestId = new BundleId(value);
if (value!="" && columnName.equals("filehash"))
fileHash = new FileHash(value);
if (value!="" && columnName.equals("BK"))
bundleKey = new BundleKey(value);
if (value!="" && columnName.equals(".author"))
author = new SubscriberId(value);
} catch (AbstractId.InvalidHexException e) {
e.printStackTrace();
}
if (columnName.equals("service"))
service = value;
if (columnName.equals("name"))
name = value;
if (columnName.equals("secret"))
secret = value;
}
@Override
public void putBlob(byte[] value) {
if (columnName.equals("manifest"))
this.manifest = value;
}
@Override
public void putLong(long value) {
if (columnName.equals("version"))
version = value;
if (columnName.equals("filesize"))
fileSize = value;
if (columnName.equals("date"))
date = value;
if (columnName.equals("crypt"))
crypt = (int)value;
if (columnName.equals(".readonly"))
readonly = value>0;
if (columnName.equals(".fromhere"))
readonly = value==0;
if (columnName.equals(".rowid") || columnName.equals("_id"))
rowId = value;
if (columnName.equals(".inserttime"))
insertTime = value;
}
}
public static ManifestResult rhizomeAddFile(File payloadPath, File manifestPath, SubscriberId author, String pin)
throws ServalDFailureException
{
List<String> args = new LinkedList<String>();
args.add("rhizome");
args.add("add");
args.add("file");
if (pin != null) {
args.add("--entry-pin");
args.add(pin);
}
args.add(author == null ? "" : author.toHex());
if (payloadPath != null)
args.add(payloadPath.getAbsolutePath());
else if (manifestPath != null)
args.add("");
if (manifestPath != null)
args.add(manifestPath.getAbsolutePath());
ManifestResult result = new ManifestResult();
result.setResult(command(result, args.toArray(new String[args.size()])));
return result;
}
public static int rhizomeList(AsyncResult<ManifestResult> result, String service, String name, SubscriberId sender, SubscriberId recipient, int offset, int numRows) throws ServalDFailureException {
return rhizomeList(new JniResultList<ManifestResult>(result) {
@Override
public ManifestResult create() {
return new ManifestResult();
}
}, service, name, sender, recipient, offset, numRows);
}
public static int rhizomeList(IJniResults result, String service, String name, SubscriberId sender, SubscriberId recipient, int offset, int numRows) throws ServalDFailureException {
List<String> args = new LinkedList<String>();
args.add("rhizome");
args.add("list");
args.add(service == null ? "" : service);
args.add(name == null ? "" : name);
args.add(sender == null ? "" : sender.toHex());
args.add(recipient == null ? "" : recipient.toHex());
if (offset > 0)
args.add("" + offset);
else if (numRows > 0)
args.add("0");
if (numRows > 0)
args.add("" + numRows);
return command(result, args.toArray(new String[args.size()]));
}
public static ManifestResult rhizomeImportBundle(File payloadFile,
File manifestFile) throws ServalDFailureException {
ManifestResult result = new ManifestResult();
result.setResult(command(result, "rhizome", "import", "bundle",
payloadFile.getAbsolutePath(), manifestFile.getAbsolutePath()));
return result;
}
public static ManifestResult rhizomeExtractBundle(BundleId manifestId, File manifestFile, File payloadFile) throws ServalDFailureException{
ManifestResult result = new ManifestResult();
result.setResult(command(result, "rhizome", "extract", "bundle",
manifestId.toHex(),
manifestFile == null ? "-" : manifestFile.getAbsolutePath(),
payloadFile.getAbsolutePath()));
return result;
}
public static ManifestResult rhizomeExportManifest(BundleId manifestId, File manifestFile) throws ServalDFailureException{
ManifestResult result = new ManifestResult();
result.setResult(command(result, "rhizome", "export", "manifest",
manifestId.toHex(),
manifestFile == null ? "-" : manifestFile.getAbsolutePath()));
return result;
}
public static ManifestResult rhizomeExtractFile(BundleId manifestId, File payloadFile) throws ServalDFailureException{
ManifestResult result = new ManifestResult();
result.setResult(command(result, "rhizome", "extract", "file",
manifestId.toHex(),
payloadFile.getAbsolutePath()));
return result;
}
/**
* Common entry point into servald command line.
* Push Rhizome bundles to all configured direct hosts.
*
* @param args
* The parameters as passed on the command line, eg: res =
* servald.command("config", "set", "debug", "peers");
* @return An object containing the servald exit status code (normally0
* indicates success) and zero or more output fields that it would
* have sent to standard output if invoked via a shell command line.
* @author Andrew Bettison <andrew@servalproject.com>
*/
public static synchronized ServalDResult command(String... args)
throws ServalDInterfaceError
public static void rhizomeDirectPush() throws ServalDFailureException
{
LinkedList<byte[]> results = new LinkedList<byte[]>();
int status = rawCommand(new JniResultsList(results), args);
return new ServalDResult(args, status, results.toArray(new byte[results.size()][]));
command("rhizome", "direct", "push");
}
public static void main(String[] args)
/**
* Pull Rhizome bundles from all configured direct hosts.
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
public static void rhizomeDirectPull() throws ServalDFailureException
{
LinkedList<byte[]> outv = new LinkedList<byte[]>();
IJniResults results = new JniResultsList(outv);
int status = rawCommand(results, args);
for (byte[] a: outv) {
System.out.println(new String(a));
command("rhizome", "direct", "pull");
}
System.exit(status);
/**
* Sync (push and pull) Rhizome bundles from all configured direct hosts.
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
public static void rhizomeDirectSync() throws ServalDFailureException
{
command("rhizome", "direct", "sync");
}
public static class ConfigItems extends JniResult{
public Map<String, String> values = new HashMap<String, String>();
@Override
public void putString(String value) {
values.put(this.columnName, value);
}
}
public static ConfigItems getConfig(String pattern) throws ServalDFailureException {
ConfigItems results = new ConfigItems();
results.setResult(command(results, "config", "get", pattern));
return results;
}
public static String getConfigItem(String name) throws ServalDFailureException{
Object result = getConfig(name).values.get(name);
if (result == null)
return null;
if (result instanceof byte[]){
return new String((byte[])result);
}
return result.toString();
}
public static void deleteConfig(String name) throws ServalDFailureException {
ServalDCommand.command("config", "del", name);
}
public static void setConfigItem(String name, String value) throws ServalDFailureException {
ServalDCommand.command("config", "set", name, value);
}
public static boolean getConfigItemBoolean(String name, boolean defaultValue) {
try {
String value = getConfigItem(name);
return parseBoolean(value, defaultValue);
} catch (ServalDFailureException e) {
e.printStackTrace();
return defaultValue;
}
}
public static int getConfigItemInt(String name, int defaultValue) throws ServalDFailureException{
try {
return Integer.parseInt(getConfig(name).values.get(name));
} catch (ServalDFailureException e) {
e.printStackTrace();
return defaultValue;
}
}
private static class PeerCount extends JniResult{
long count;
@Override
public void putLong(long value) {
count = value;
}
}
public static int peerCount() throws ServalDFailureException {
PeerCount result = new PeerCount();
result.setResult(ServalDCommand.command(result, "peer", "count"));
return (int)result.count;
}
public static int idPeers(AsyncResult<IdentityResult> results) throws ServalDFailureException {
return idPeers(new JniResultList<IdentityResult>(results) {
@Override
public IdentityResult create() {
return new IdentityResult();
}
});
}
public static int idPeers(IJniResults results) throws ServalDFailureException {
return command(results, "id", "peers");
}
public static class Conversation extends JniResult{
public long id;
public SubscriberId recipient;
public String read;
public long last_message;
public long read_offset;
@Override
public void putString(String value) {
if (columnName.equals("read"))
this.read = value;
}
@Override
public void putBlob(byte[] value) {
if (columnName.equals("recipient"))
try {
this.recipient = new SubscriberId(value);
} catch (AbstractId.InvalidBinaryException e) {
e.printStackTrace();
}
}
@Override
public void putLong(long value) {
if (columnName.equals("_id"))
this.id = value;
if (columnName.equals("last_message"))
this.last_message = value;
if (columnName.equals("read_offset"))
this.read_offset = value;
}
}
public static int listConversations(AsyncResult<Conversation> result, final SubscriberId sender, int offset, int numRows) throws ServalDFailureException {
return listConversations(new JniResultList<Conversation>(result) {
@Override
public Conversation create() {
return new Conversation();
}
}, sender, offset, numRows);
}
public static int listConversations(IJniResults callback, final SubscriberId sender, int offset, int numRows) throws ServalDFailureException {
return command(callback, "meshms", "list", "conversations",
sender.toHex(), ""+offset, ""+numRows);
}
public static class Message extends JniResult{
public long id;
public long offset;
public String type;
public String message;
@Override
public void putString(String value) {
if (columnName.equals("type"))
this.type = value;
if (columnName.equals("message"))
this.message = value;
}
@Override
public void putLong(long value) {
if (columnName.equals("_id"))
this.id = value;
if (columnName.equals("offset"))
this.offset = value;
}
}
public static int listMessages(AsyncResult<Message> result, final SubscriberId sender, final SubscriberId recipient) throws ServalDFailureException {
return listMessages(new JniResultList<Message>(result) {
@Override
public Message create() {
return new Message();
}
}, sender, recipient);
}
public static int listMessages(IJniResults callback, final SubscriberId sender, final SubscriberId recipient) throws ServalDFailureException {
return ServalDCommand.command(callback, "meshms", "list", "messages",
sender.toHex(), recipient.toHex());
}
public static void sendMessage(final SubscriberId sender, final SubscriberId recipient, String message) throws ServalDFailureException {
command("meshms", "send", "message",
sender.toHex(), recipient.toHex(),
message);
}
public static void readMessage(final SubscriberId sender, final SubscriberId recipient) throws ServalDFailureException {
command("meshms", "read", "messages",
sender.toHex(), recipient.toHex());
}
public static void readMessage(final SubscriberId sender, final SubscriberId recipient, long offset) throws ServalDFailureException {
command("meshms", "read", "messages",
sender.toHex(), recipient.toHex(),
"" + offset);
}
public static int printCommand(final String fieldDelim, final String rowDelim, String... args){
return rawCommand(new IJniResults() {
int columns =-1;
int column =-1;
@Override
public void startResultSet(int columns) {
this.columns=columns;
}
@Override
public void setColumnName(int column, String name) {
System.out.print(name + fieldDelim);
if (column>=0 && column+1==columns)
System.out.println();
}
private void eol(){
if (columns==-1 || ++column==columns){
System.out.print(rowDelim);
column=-1;
}
}
@Override
public void putString(String value) {
System.out.print(value);
eol();
}
@Override
public void putBlob(byte[] value) {
System.out.print(new String(value));
eol();
}
@Override
public void putLong(long value) {
System.out.print(value);
eol();
}
@Override
public void putDouble(double value) {
System.out.print(value);
eol();
}
@Override
public void totalRowCount(int rows) {
}
}, args);
}
public static void main(String... args)
{
System.exit(printCommand(":","\n",args));
}
}

View File

@ -20,8 +20,6 @@
package org.servalproject.servaldna;
import org.servalproject.servaldna.ServalDResult;
/**
* Thrown when a request to a servald JNI method fails. This typically means that the returned
* status is non-zero, or some other result was returned that indicated the operation failed.
@ -32,10 +30,6 @@ public class ServalDFailureException extends Exception
{
private static final long serialVersionUID = 1L;
public ServalDFailureException(String message, ServalDResult result) {
super(message + " for command: " + result.getCommandString());
}
public ServalDFailureException(String message) {
super(message);
}

View File

@ -1,217 +0,0 @@
/**
* Copyright (C) 2011 The Serval Project
*
* This file is part of Serval Software (http://www.servalproject.org)
*
* Serval Software 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 3 of the License, or
* (at your option) any later version.
*
* This source code 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 source code; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.servalproject.servaldna;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
/** Represents the result of invoking servald via the JNI command-line interface. The 'args'
* attribute contains a copy of the arguments that were passed to the call that produced this
* result, to facilitate diagnosis of failures and errors. The results of a call are an integer
* 'status' value (normally the process exit status) and a list of strings in 'outv', called "output
* fields". These strings must be interpreted depending on the operation that produced them.
*
* Many operations return information about their outcome as a sequence of key-value pairs of
* fields. The getField() method and variants offer an order-independent means to query these
* fields by key and optionally enforce a type on the value. If the operation produces output
* that is not in the key-value structure, then the caller simply avoids using these methods,
* and accesses the 'outv' array directly.
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
public class ServalDResult
{
public static final int STATUS_ERROR = 255;
public final String[] args;
public final int status;
public final byte[][] outv;
private HashMap<String,byte[]> keyValue;
public ServalDResult(String[] args, int status, byte[][] outv) {
this.args = args;
this.status = status;
this.outv = outv;
this.keyValue = null;
}
public ServalDResult(ServalDResult orig) {
this.args = orig.args;
this.status = orig.status;
this.outv = orig.outv;
this.keyValue = orig.keyValue;
}
public String getCommandString() {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < this.args.length; i++) {
if (i > 0)
sb.append(' ');
sb.append(args[i]);
}
return sb.toString();
}
@Override
public String toString() {
String[] outvstr = new String[this.outv.length];
for (int i = 0; i != this.outv.length; ++i)
outvstr[i] = new String(this.outv[i]);
return this.getClass().getName() + "(args=" + Arrays.deepToString(this.args) + ", status=" + this.status + ", outv=" + Arrays.deepToString(outvstr) + ")";
}
public void failIfStatusError() throws ServalDFailureException {
if (this.status == STATUS_ERROR)
throw new ServalDFailureException("error exit status", this);
}
public void failIfStatusNonzero() throws ServalDFailureException {
if (this.status != 0)
throw new ServalDFailureException("non-zero exit status", this);
}
protected void makeKeyValueMap() {
if (this.keyValue == null) {
if (this.outv.length % 2 != 0)
throw new ServalDInterfaceError("odd number of fields", this);
this.keyValue = new HashMap<String,byte[]>();
for (int i = 0; i != this.outv.length; i += 2)
this.keyValue.put(new String(this.outv[i]), this.outv[i + 1]);
}
}
public Map<String,byte[]> getKeyValueMap() {
makeKeyValueMap();
return new HashMap<String,byte[]>(this.keyValue);
}
protected byte[] getFieldOrNull(String fieldName) {
makeKeyValueMap();
if (!this.keyValue.containsKey(fieldName))
return null;
return this.keyValue.get(fieldName);
}
protected byte[] getField(String fieldName) throws ServalDInterfaceError {
byte[] value = getFieldOrNull(fieldName);
if (value == null)
throw new ServalDInterfaceError("missing '" + fieldName + "' field", this);
return value;
}
public byte[] getFieldByteArray(String fieldName, byte[] defaultValue) {
byte[] value = getFieldOrNull(fieldName);
if (value == null)
return defaultValue;
return value;
}
public String getFieldString(String fieldName, String defaultValue) {
byte[] value = getFieldOrNull(fieldName);
if (value == null)
return defaultValue;
return new String(value);
}
public String getFieldString(String fieldName) throws ServalDInterfaceError {
return new String(getField(fieldName));
}
public String getFieldStringNonEmptyOrNull(String fieldName) throws ServalDInterfaceError {
String value = getFieldString(fieldName, "");
return value.length() == 0 ? null : value;
}
public long getFieldLong(String fieldName) throws ServalDInterfaceError {
String value = getFieldString(fieldName);
try {
return Long.valueOf(value);
}
catch (NumberFormatException e) {
throw new ServalDInterfaceError("field " + fieldName + "='" + value + "' is not of type long", this, e);
}
}
public int getFieldInt(String fieldName) throws ServalDInterfaceError {
String value = getFieldString(fieldName);
try {
return Integer.valueOf(value);
}
catch (NumberFormatException e) {
throw new ServalDInterfaceError("field " + fieldName + "='" + value + "' is not of type int", this, e);
}
}
public boolean getFieldBoolean(String fieldName) throws ServalDInterfaceError {
String value = getFieldString(fieldName);
try {
if (value.equalsIgnoreCase("true") || value.equalsIgnoreCase("yes") || value.equalsIgnoreCase("on"))
return true;
if (value.equalsIgnoreCase("false") || value.equalsIgnoreCase("no") || value.equalsIgnoreCase("off"))
return false;
return Integer.parseInt(value) != 0;
}
catch (NumberFormatException e) {
throw new ServalDInterfaceError("field " + fieldName + "='" + value + "' is not of type boolean", this, e);
}
}
public SubscriberId getFieldSubscriberId(String fieldName, SubscriberId defaultValue) throws ServalDInterfaceError {
byte[] value = getFieldOrNull(fieldName);
if (value == null)
return defaultValue;
String str = new String(value);
try {
return new SubscriberId(str);
}
catch (AbstractId.InvalidHexException e) {
throw new ServalDInterfaceError("field " + fieldName + "='" + str + "' is not a Bundle ID: " + e.getMessage(), this);
}
}
public SubscriberId getFieldSubscriberId(String fieldName) throws ServalDInterfaceError {
SubscriberId value = getFieldSubscriberId(fieldName, null);
if (value == null)
throw new ServalDInterfaceError("missing '" + fieldName + "' field", this);
return value;
}
public BundleId getFieldBundleId(String fieldName) throws ServalDInterfaceError {
String value = getFieldString(fieldName);
try {
return new BundleId(value);
}
catch (BundleId.InvalidHexException e) {
throw new ServalDInterfaceError("field " + fieldName + "='" + value + "' is not a Bundle ID: " + e.getMessage(), this, e);
}
}
public FileHash getFieldFileHash(String fieldName) throws ServalDInterfaceError {
String value = getFieldString(fieldName);
try {
return new FileHash(value);
}
catch (BundleId.InvalidHexException e) {
throw new ServalDInterfaceError("field " + fieldName + "='" + value + "' is not a file hash: " + e.getMessage(), this);
}
}
}

View File

@ -59,6 +59,7 @@ public class SubscriberId extends AbstractId {
}
public static SubscriberId broadcastSid;
public static SubscriberId ANY;
static {
byte buff[] = new byte[BINARY_SIZE];
for (int i = 0; i < BINARY_SIZE; i++)
@ -68,5 +69,14 @@ public class SubscriberId extends AbstractId {
} catch (InvalidBinaryException e) {
// TODO log error?
}
buff = new byte[BINARY_SIZE];
for (int i = 0; i < BINARY_SIZE; i++)
buff[i] = (byte) 0x00;
try {
ANY = new SubscriberId(buff);
} catch (InvalidBinaryException e) {
// TODO log error?
}
}
}

View File

@ -1,13 +1,12 @@
package org.servalproject.test;
import org.servalproject.servaldna.ServalDCommand;
import org.servalproject.servaldna.ServalDResult;
import java.util.Arrays;
class ServalDTests
{
public static void main(String[] args)
public static void main(String... args)
{
try {
for (int i = 0; i != args.length; ++i)
@ -22,14 +21,8 @@ class ServalDTests
}
while(repeatCount>0){
ServalDResult result = ServalDCommand.command(args);
System.out.print(result.status);
for (byte[] a: result.outv) {
System.out.print(":");
System.out.print(new String(a));
}
System.out.println("");
ServalDCommand.printCommand(""," ",args);
System.out.println();
repeatCount--;
}
}

View File

@ -1,4 +1,5 @@
SERVAL_SOURCES = \
$(SERVAL_BASE)sqlite-amalgamation-3070900/sqlite3.c \
$(SERVAL_BASE)cli.c \
$(SERVAL_BASE)commandline.c \
$(SERVAL_BASE)conf.c \
@ -70,7 +71,6 @@ SERVAL_SOURCES = \
$(SERVAL_BASE)sha2.c \
$(SERVAL_BASE)sighandlers.c \
$(SERVAL_BASE)slip.c \
$(SERVAL_BASE)sqlite-amalgamation-3070900/sqlite3.c \
$(SERVAL_BASE)srandomdev.c \
$(SERVAL_BASE)str.c \
$(SERVAL_BASE)strbuf.c \

View File

@ -78,6 +78,57 @@ extract_stdout_keyvalue() {
assert --message="stdout of ($executed) contains valid '$_label:' line" --stdout extract_stdout_keyvalue_optional "$@"
}
# Parse the standard result set output produced by the immediately preceding command
# command into the following shell variables:
# NCOLS the number of columns
# NROWS the number of data rows (not counting headers)
# HEADER[c] the C-th header label, 0 <= C <= NCOLS-1
# <label>[R] where <label> is a header label with all non-alphanumerics
# replaced by underscore '_' and all alphas converted to upper case, eg,
# .author -> _AUTHOR, is the value of that column in the R-th row, 0 <=
# R < NROWS
#
# Warning: overwrites existing shell variables. Names of overwritten shell
# variables are derived directly from the output of the command, so cannot be
# controlled. If a prefix is supplied, all variables are prefixed with that.
unpack_stdout_list() {
local prefix="$1"
{
local n
read n
eval ${prefix}NCOLS=\"\$n\"
declare -a ${prefix}HEADER
local -a header
local oIFS="$IFS"
IFS=:
read -r -a header
IFS="$oIFS"
eval ${prefix}HEADER="(\"\${header[@]}\")"
local hdr
local -a colvars=()
for hdr in "${header[@]}"; do
hdr="${hdr//[^A-Za-z0-9_]/_}"
# hdr="${hdr^^*}" would do in Bash-4.0 and later
hdr="$(echo "$hdr" | sed -e 's/.*/\U&/')"
colvars+=("$hdr")
done
local -a row
IFS=:
local i=0
while eval read -r -a row; do
local j=0
local val
for val in "${row[@]}"; do
eval ${prefix}${colvars[$j]}[$i]=\"\$val\"
let ++j
done
let ++i
done
IFS="$oIFS"
eval ${prefix}NROWS=$i
} < <(replayStdout)
}
# Utility function for creating servald fixtures:
# - set $servald variable (executable under test)
# - set the current instance to be "Z"
@ -641,7 +692,6 @@ create_identities() {
done
done
executeOk_servald keyring list "${servald_options[@]}"
assertStdoutLineCount '==' $N
for ((i = 1; i <= N; ++i)); do
local sidvar=SID$instance_name$i
local didvar=DID$instance_name$i

View File

@ -78,63 +78,6 @@ assert_rhizome_list() {
rhizome_list_file_count=$(( $(replayStdout | wc -l) - 2 ))
}
# Parse the standard output produced by the immediately preceding "rhizome list"
# command into the following shell variables:
# NCOLS the number of columns
# NROWS the number of data rows (not counting headers)
# HEADER[c] the C-th header label, 0 <= C <= NCOLS-1
# <label>[R] where <label> is a header label with all non-alphanumerics
# replaced by underscore '_' and all alphas converted to upper case, eg,
# .author -> _AUTHOR, is the value of that column in the R-th row, 0 <=
# R < NROWS
#
# Warning: overwrites existing shell variables. Names of overwritten shell
# variables are derived directly from the output of rhizome list, so cannot be
# controlled. If a prefix is supplied, all variables are prefixed with that.
rhizome_list_unpack() {
local prefix="$1"
{
local n
read n
eval ${prefix}NCOLS=\"\$n\"
declare -a ${prefix}HEADER
local -a header
local oIFS="$IFS"
IFS=:
read -r -a header
IFS="$oIFS"
eval ${prefix}HEADER="(\"\${header[@]}\")"
local hdr
local -a colvars=()
for hdr in "${header[@]}"; do
case "$hdr" in
id)
hdr=BID;;
*)
hdr="${hdr//[^A-Za-z0-9_]/_}"
# hdr="${hdr^^*}" would do in Bash-4.0 and later
hdr="$(echo "$hdr" | sed -e 's/.*/\U&/')"
;;
esac
colvars+=("$hdr")
done
local -a row
IFS=:
local i=0
while eval read -r -a row; do
local j=0
local val
for val in "${row[@]}"; do
eval ${prefix}${colvars[$j]}[$i]=\"\$val\"
let ++j
done
let ++i
done
IFS="$oIFS"
eval ${prefix}NROWS=$i
} < "$TFWSTDOUT"
}
rhizome_list_dump() {
local ncols
local -a headers

View File

@ -80,10 +80,10 @@ test_publish() {
stop_servald_server +D
set_instance +A
executeOk_servald dna lookup "$DIDB"
assertStdoutLineCount '==' 1
assertStdoutLineCount '==' 3
assertStdoutGrep --matches=1 "^sid://$SIDB/local/$DIDB:$DIDB:$NAMEB\$"
executeOk_servald dna lookup "$DIDC"
assertStdoutLineCount '==' 2
assertStdoutLineCount '==' 4
assertStdoutGrep --matches=1 "^sid://$SIDC/local/$DIDC:$DIDC:$NAMEC\$"
assertStdoutGrep --matches=1 "^sid://$SIDD/local/$DIDD:$DIDD:$NAMED\$"
assert_status_all_servald_servers running
@ -147,13 +147,13 @@ test_routing() {
executeOk_servald route print
assertStdoutGrep --matches=1 "^$SIDA:UNICAST:"
executeOk_servald dna lookup "$DIDC"
assertStdoutLineCount '==' 1
assertStdoutLineCount '==' 3
assertStdoutGrep --matches=1 "^sid://$SIDC/local/$DIDC:$DIDC:$NAMEC\$"
set_instance +C
executeOk_servald route print
assertStdoutGrep --matches=1 "^$SIDA:UNICAST:"
executeOk_servald dna lookup "$DIDB"
assertStdoutLineCount '==' 1
assertStdoutLineCount '==' 3
assertStdoutGrep --matches=1 "^sid://$SIDB/local/$DIDB:$DIDB:$NAMEB\$"
executeOk_servald mdp ping $SIDB 3
tfw_cat --stdout --stderr

View File

@ -258,150 +258,157 @@ EOF
}
test_ExecArgs() {
executeOk_servald dna lookup 12345
assertStdoutIs -e "uri:dumb:12345:Hello, World!\n"
assertStdoutLineCount '==' 3
assertStdoutGrep --stdout --matches=1 "^uri:dumb:12345:Hello, World!\$"
}
doc_ReplyOk1="DNA helper returns one valid reply"
test_ReplyOk1() {
executeOk_servald dna lookup 00001
assertStdoutIs -e "sip://$SID_JOE_A@10.1.1.1:00001:Joe A. Bloggs\n"
assertStdoutLineCount '==' 3
assertStdoutGrep --stdout --matches=1 "^sip://$SID_JOE_A@10.1.1.1:00001:Joe A. Bloggs\$"
}
doc_ReplyOk2="DNA helper returns two valid replies"
test_ReplyOk2() {
executeOk_servald dna lookup 00002
assertStdoutIs -e "sip://$SID_JOE_A@10.1.1.1:00002:Joe A. Bloggs\nsip://$SID_JOE_B@10.1.1.1:00002:Joe B. Bloggs\n"
assertStdoutLineCount '==' 4
assertStdoutGrep --stdout --matches=1 "^sip://$SID_JOE_A@10.1.1.1:00002:Joe A. Bloggs\$"
assertStdoutGrep --stdout --matches=1 "^sip://$SID_JOE_B@10.1.1.1:00002:Joe B. Bloggs\$"
}
doc_ReplyOk3="DNA helper returns three valid replies"
test_ReplyOk3() {
executeOk_servald dna lookup 00003
assertStdoutIs -e "sip://$SID_JOE_A@10.1.1.1:00003:Joe A. Bloggs\nsip://$SID_JOE_B@10.1.1.1:00003:Joe B. Bloggs\nsip://$SID_JOE_C@10.1.1.1:00003:Joe C. Bloggs\n"
assertStdoutLineCount '==' 5
assertStdoutGrep --stdout --matches=1 "^sip://$SID_JOE_A@10.1.1.1:00003:Joe A. Bloggs\$"
assertStdoutGrep --stdout --matches=1 "^sip://$SID_JOE_B@10.1.1.1:00003:Joe B. Bloggs\$"
assertStdoutGrep --stdout --matches=1 "^sip://$SID_JOE_C@10.1.1.1:00003:Joe C. Bloggs\$"
}
doc_UriEmpty="DNA helper returns empty URI"
test_UriEmpty() {
executeOk_servald dna lookup 00004
assertStdoutIs ""
assertStdoutLineCount '==' 2
assertGrep "$LOGA" 'ERROR:.*DNAHELPER.*empty URI'
}
doc_UriInvalid1="DNA helper returns invalid URI, missing scheme"
test_UriInvalid1() {
executeOk_servald dna lookup 000051
assertStdoutIs ""
assertStdoutLineCount '==' 2
assertGrep "$LOGA" 'ERROR:.*DNAHELPER.*Bluebottle.*invalid URI'
}
doc_UriInvalid2="DNA helper returns invalid URI, invalid char"
test_UriInvalid2() {
executeOk_servald dna lookup 000052
assertStdoutIs ""
assertStdoutLineCount '==' 2
assertGrep "$LOGA" 'ERROR:.*DNAHELPER.*sip://Sea goon.*invalid URI'
}
doc_UriInvalid3="DNA helper returns invalid URI, empty hierarchical part"
test_UriInvalid3() {
executeOk_servald dna lookup 000053
assertStdoutIs ""
assertStdoutLineCount '==' 2
assertGrep "$LOGA" 'ERROR:.*DNAHELPER.*sip:.*invalid URI'
}
doc_TokenMismatch="DNA helper returns mismatched token"
test_TokenMismatch() {
executeOk_servald dna lookup 000061
assertStdoutIs ""
assertStdoutLineCount '==' 2
assertGrep "$LOGA" 'ERROR:.*DNAHELPER.*mismatched token'
}
doc_TokenEmpty="DNA helper returns empty token"
test_TokenEmpty() {
executeOk_servald dna lookup 000062
assertStdoutIs ""
assertStdoutLineCount '==' 2
assertGrep "$LOGA" 'ERROR:.*DNAHELPER.*empty token'
}
doc_TokenInvalid="DNA helper returns invalid token"
test_TokenInvalid() {
executeOk_servald dna lookup 000063
assertStdoutIs ""
assertStdoutLineCount '==' 2
assertGrep "$LOGA" 'ERROR:.*DNAHELPER.*invalid token'
}
doc_TokenInvalidLong="DNA helper returns invalid token, too long"
test_TokenInvalidLong() {
executeOk_servald dna lookup 000064
assertStdoutIs ""
assertStdoutLineCount '==' 2
assertGrep "$LOGA" 'ERROR:.*DNAHELPER.*reply.*invalid'
}
doc_TokenInvalidShort="DNA helper returns invalid token, too short"
test_TokenInvalidShort() {
executeOk_servald dna lookup 000065
assertStdoutIs ""
assertStdoutLineCount '==' 2
assertGrep "$LOGA" 'ERROR:.*DNAHELPER.*invalid token'
}
doc_DidMismatch="DNA helper returns mismatched DID"
test_DidMismatch() {
executeOk_servald dna lookup 000071
assertStdoutIs ""
assertStdoutLineCount '==' 2
assertGrep "$LOGA" 'ERROR:.*DNAHELPER.*mismatched DID'
}
doc_DidEmpty="DNA helper returns empty DID"
test_DidEmpty() {
executeOk_servald dna lookup 000072
assertStdoutIs ""
assertStdoutLineCount '==' 2
assertGrep "$LOGA" 'ERROR:.*DNAHELPER.*empty DID'
}
doc_DidInvalid="DNA helper returns invalid DID"
test_DidInvalid() {
executeOk_servald dna lookup 000073
assertStdoutIs ""
assertStdoutLineCount '==' 2
assertGrep "$LOGA" 'ERROR:.*DNAHELPER.*invalid DID'
}
doc_DidInvalidLong="DNA helper returns invalid DID, too long"
test_DidInvalidLong() {
executeOk_servald dna lookup 000074
assertStdoutIs ""
assertStdoutLineCount '==' 2
assertGrep "$LOGA" 'ERROR:.*DNAHELPER.*reply.*invalid'
}
doc_DidInvalidShort="DNA helper returns invalid DID, too short"
test_DidInvalidShort() {
executeOk_servald dna lookup 000075
assertStdoutIs ""
assertStdoutLineCount '==' 2
assertGrep "$LOGA" 'ERROR:.*DNAHELPER.*invalid DID'
}
doc_ReplyInvalidMissingDelim="DNA helper returns invalid reply, missing delimiter"
test_ReplyInvalidMissingDelim() {
executeOk_servald dna lookup 000081
assertStdoutIs ""
assertStdoutLineCount '==' 2
assertGrep "$LOGA" 'ERROR:.*DNAHELPER.*reply.*invalid'
}
doc_ReplyInvalidLongName="DNA helper returns invalid reply, name too long"
test_ReplyInvalidLongName() {
executeOk_servald dna lookup 000082
assertStdoutIs ""
assertStdoutLineCount '==' 2
assertGrep "$LOGA" 'ERROR:.*DNAHELPER.*reply.*invalid'
}
doc_ReplyInvalidEmpty="DNA helper returns invalid reply, empty line"
test_ReplyInvalidEmpty() {
executeOk_servald dna lookup 000083
assertStdoutIs ""
assertStdoutLineCount '==' 2
assertGrep "$LOGA" 'ERROR:.*DNAHELPER.*reply .\\n. invalid'
}
doc_ReplyInvalidMissingNewline="DNA helper returns invalid reply, missing newline"
test_ReplyInvalidMissingNewline() {
executeOk_servald dna lookup 000084
assertStdoutIs ""
assertStdoutLineCount '==' 2
assertGrep "$LOGA" 'ERROR:.*DNAHELPER.*reply.*spurious'
assertGrep "$LOGA" 'ERROR:.*DNAHELPER.*reply timeout'
}
@ -409,30 +416,34 @@ test_ReplyInvalidMissingNewline() {
doc_HelperTimeout="DNA helper process takes too long to reply and is restarted"
test_HelperTimeout() {
executeOk_servald dna lookup 00009
assertStdoutIs ""
assertStdoutLineCount '==' 2
assertGrep "$LOGA" 'ERROR:.*DNAHELPER.*reply timeout'
assertGrep "$LOGA" 'INFO:.*DNAHELPER.*process.*terminated by signal 15'
executeOk_servald dna lookup 00001
assertStdoutIs -e "sip://$SID_JOE_A@10.1.1.1:00001:Joe A. Bloggs\n"
assertStdoutLineCount '==' 3
assertStdoutGrep --stdout --matches=1 "^sip://$SID_JOE_A@10.1.1.1:00001:Joe A. Bloggs\$"
}
doc_ReplySpurious="DNA helper spurious output after DONE is ignored"
test_ReplySpurious() {
executeOk_servald dna lookup 00010
assertStdoutIs -e "sip://$SID_JOE_E@10.1.1.1:00010:Joe E. Bloggs\n"
assertStdoutLineCount '==' 3
assertStdoutGrep --stdout --matches=1 "^sip://$SID_JOE_E@10.1.1.1:00010:Joe E. Bloggs\$"
assertGrep "$LOGA" 'WARN:.*DNAHELPER.*spurious output'
executeOk_servald dna lookup 00001
assertStdoutIs -e "sip://$SID_JOE_A@10.1.1.1:00001:Joe A. Bloggs\n"
assertStdoutLineCount '==' 3
assertStdoutGrep --stdout --matches=1 "^sip://$SID_JOE_A@10.1.1.1:00001:Joe A. Bloggs\$"
}
doc_HelperDies="DNA helper process dies unexpectedly and is restarted"
test_HelperDies() {
executeOk_servald dna lookup 00011
assertStdoutIs ""
assertStdoutLineCount '==' 2
assertGrep "$LOGA" 'INFO:.*DNAHELPER.*process.*exited normally with status 42'
assertGrep "$LOGA" 'ERROR:.*DNAHELPER.*goodbye cruel world\\n'
executeOk_servald dna lookup 00001
assertStdoutIs -e "sip://$SID_JOE_A@10.1.1.1:00001:Joe A. Bloggs\n"
assertStdoutLineCount '==' 3
assertStdoutGrep --stdout --matches=1 "^sip://$SID_JOE_A@10.1.1.1:00001:Joe A. Bloggs\$"
}
runTests "$@"

View File

@ -54,7 +54,7 @@ set_server_vars() {
doc_LookupWildcard="Lookup by wildcard"
test_LookupWildcard() {
executeOk_servald dna lookup "*"
assertStdoutLineCount '==' 2
assertStdoutLineCount '==' 4
assertStdoutGrep --matches=1 "^sid://$SIDA/local/$DIDA:$DIDA:$NAMEA\$"
assertStdoutGrep --matches=1 "^sid://$SIDB/local/$DIDB:$DIDB:$NAMEB\$"
}
@ -62,7 +62,7 @@ test_LookupWildcard() {
doc_LookupEmpty="Lookup by empty string"
test_LookupEmpty() {
executeOk_servald dna lookup ""
assertStdoutLineCount '==' 2
assertStdoutLineCount '==' 4
assertStdoutGrep --matches=1 "^sid://$SIDA/local/$DIDA:$DIDA:$NAMEA\$"
assertStdoutGrep --matches=1 "^sid://$SIDB/local/$DIDB:$DIDB:$NAMEB\$"
}
@ -70,20 +70,20 @@ test_LookupEmpty() {
doc_LookupNonExistent="Lookup non-existent phone number"
test_LookupNonExistent() {
executeOk_servald dna lookup "5551234"
assertStdoutLineCount '==' 0
assertStdoutLineCount '==' 2
}
doc_LookupLocal="Lookup local phone number"
test_LookupLocal() {
executeOk_servald dna lookup "$DIDA"
assertStdoutLineCount '==' 1
assertStdoutLineCount '==' 3
assertStdoutGrep --matches=1 "^sid://$SIDA/local/$DIDA:$DIDA:$NAMEA\$"
}
doc_LookupRemote="Lookup remote phone number"
test_LookupRemote() {
executeOk_servald dna lookup "$DIDB"
assertStdoutLineCount '==' 1
assertStdoutLineCount '==' 3
assertStdoutGrep --matches=1 "^sid://$SIDB/local/$DIDB:$DIDB:$NAMEB\$"
}
@ -135,7 +135,7 @@ setup_MultiLookupHelperThree() {
}
test_MultiLookupHelperThree() {
executeOk_servald dna lookup 00003
assertStdoutLineCount '==' 3
assertStdoutLineCount '==' 5
assertStdoutGrep --matches=1 "uri:A1:00003:Name One$"
assertStdoutGrep --matches=1 "uri:B1:00003:Name Three$"
assertStdoutGrep --matches=1 "uri:C1:00003:Name Six$"
@ -147,7 +147,7 @@ setup_MultiLookupHelperTwo() {
}
test_MultiLookupHelperTwo() {
executeOk_servald dna lookup 00002
assertStdoutLineCount '==' 2
assertStdoutLineCount '==' 4
assertStdoutGrep --matches=1 "uri:A2:00002:Name Two\$"
assertStdoutGrep --matches=1 "uri:B2:00002:Name Four\$"
}
@ -158,7 +158,7 @@ setup_MultiLookupHelperOne() {
}
test_MultiLookupHelperOne() {
executeOk_servald dna lookup 00001
assertStdoutLineCount '==' 1
assertStdoutLineCount '==' 3
assertStdoutGrep --matches=1 "uri:B3:00001:Name Five\$"
}

View File

@ -68,7 +68,7 @@ doc_Repeat="Serval JNI repeated calls in same process"
test_Repeat() {
executeOk --core-backtrace java "-Djava.library.path=$LD_LIBRARY_PATH" -classpath "$PWD/classes" org.servalproject.test.ServalDTests repeat 50 'echo' 'Hello,' 'world!'
assertStdoutLineCount '==' 50
assertStdoutGrep --matches=50 '^0:Hello,:world!$'
assertStdoutGrep --matches=50 '^Hello, world! $'
}
doc_NullArg="Serval JNI null arguments throw exception"

View File

@ -47,7 +47,8 @@ setup_instances() {
}
assert_keyring_list() {
assertStdoutLineCount --stdout --stderr '==' $1
unpack_stdout_list X
assert --stdout --stderr [ $XNROWS -eq $1 ]
assertStdoutGrep --stderr --matches=$1 "^[0-9a-fA-F]\{64\}:[0-9*#+]*:.*\$"
tfw_cat --stdout
}
@ -198,8 +199,9 @@ setup_KeyringPinServer() {
test_KeyringPinServer() {
start_servald_server --keyring-pin=yellow
executeOk_servald id self
assertStdoutLineCount == 1
assertStdoutGrep --fixed-strings "$SIDA"
unpack_stdout_list X
assert --stdout --stderr [ $XNROWS -eq 1 ]
assert --stdout --stderr [ ${XSID[0]} = $SIDA ]
}
finally_KeyringPinServer() {
stop_servald_server
@ -216,8 +218,9 @@ setup_EntryPinServer() {
test_EntryPinServer() {
start_servald_server --entry-pin=yodel
executeOk_servald id self
assertStdoutLineCount == 1
assertStdoutGrep --fixed-strings "$SIDA"
unpack_stdout_list X
assert --stdout --stderr [ $XNROWS -eq 1 ]
assert --stdout --stderr [ ${XSID[0]} = $SIDA ]
}
finally_EntryPinServer() {
stop_servald_server
@ -234,8 +237,9 @@ setup_KeyringEntryPinServer() {
test_KeyringEntryPinServer() {
start_servald_server --keyring-pin=yellow --entry-pin=yodel
executeOk_servald id self
assertStdoutLineCount == 1
assertStdoutGrep --fixed-strings "$SIDA"
unpack_stdout_list X
assert --stdout --stderr [ $XNROWS -eq 1 ]
assert --stdout --stderr [ ${XSID[0]} = $SIDA ]
}
finally_KeyringKeyringPinServer() {
stop_servald_server
@ -259,32 +263,32 @@ setup_KeyringEntryPinServer() {
}
test_KeyringEntryPinServer() {
executeOk_servald id self
assertStdoutLineCount == 1
assertStdoutGrep --fixed-strings "$SIDA"
assertStdoutLineCount == 3
assertStdoutGrep --matches=1 --fixed-strings "$SIDA"
executeOk_servald id enter pin 'one'
executeOk_servald id list
assertStdoutLineCount == 2
assertStdoutGrep --fixed-strings "sid:$SIDA"
assertStdoutGrep --fixed-strings "sid:$ONE"
assertStdoutLineCount == 4
assertStdoutGrep --matches=1 --fixed-strings "$SIDA"
assertStdoutGrep --matches=1 --fixed-strings "$ONE"
executeOk_servald id enter pin 'two'
executeOk_servald id list
assertStdoutLineCount == 4
assertStdoutGrep --fixed-strings "sid:$SIDA"
assertStdoutGrep --fixed-strings "sid:$ONE"
assertStdoutGrep --fixed-strings "sid:$TWOA"
assertStdoutGrep --fixed-strings "sid:$TWOB"
assertStdoutLineCount == 6
assertStdoutGrep --matches=1 --fixed-strings "$SIDA"
assertStdoutGrep --matches=1 --fixed-strings "$ONE"
assertStdoutGrep --matches=1 --fixed-strings "$TWOA"
assertStdoutGrep --matches=1 --fixed-strings "$TWOB"
executeOk_servald id relinquish pin 'one'
executeOk_servald id list
assertStdoutLineCount == 3
assertStdoutGrep --fixed-strings "sid:$SIDA"
assertStdoutGrep --fixed-strings "sid:$TWOA"
assertStdoutGrep --fixed-strings "sid:$TWOB"
assertStdoutLineCount == 5
assertStdoutGrep --matches=1 --fixed-strings "$SIDA"
assertStdoutGrep --matches=1 --fixed-strings "$TWOA"
assertStdoutGrep --matches=1 --fixed-strings "$TWOB"
executeOk_servald id relinquish sid "$TWOB"
tfw_cat --stderr
executeOk_servald id list
assertStdoutLineCount == 2
assertStdoutGrep --fixed-strings "sid:$SIDA"
assertStdoutGrep --fixed-strings "sid:$TWOA"
assertStdoutLineCount == 4
assertStdoutGrep --matches=1 --fixed-strings "$SIDA"
assertStdoutGrep --matches=1 --fixed-strings "$TWOA"
}
teardown_KeyringEntryPinServer() {
teardown_servald
@ -306,17 +310,17 @@ setup_ListTags() {
}
test_ListTags() {
executeOk_servald id list
assertStdoutLineCount == 3
assertStdoutGrep --fixed-strings "sid:$ONE"
assertStdoutGrep --fixed-strings "sid:$TWO"
assertStdoutGrep --fixed-strings "sid:$THREE"
assertStdoutLineCount == 5
assertStdoutGrep --fixed-strings "$ONE"
assertStdoutGrep --fixed-strings "$TWO"
assertStdoutGrep --fixed-strings "$THREE"
executeOk_servald id list 'tag1'
assertStdoutLineCount == 2
assertStdoutGrep --fixed-strings "sid:$ONE"
assertStdoutGrep --fixed-strings "sid:$TWO"
assertStdoutLineCount == 4
assertStdoutGrep --fixed-strings "$ONE"
assertStdoutGrep --fixed-strings "$TWO"
executeOk_servald id list 'tag1' 'First Value'
assertStdoutLineCount == 1
assertStdoutGrep --fixed-strings "sid:$ONE"
assertStdoutLineCount == 3
assertStdoutGrep --fixed-strings "$ONE"
}
teardown_ListTags() {
teardown_servald

View File

@ -100,17 +100,17 @@ check_meshms_bundles() {
# Dump the MeshMS bundles to the log and check consistency
# The only "file" bundle should be the conversation list
executeOk_servald rhizome list file
rhizome_list_unpack X
unpack_stdout_list X
assert --stdout --stderr [ $XNROWS -eq 1 ]
assert --stdout --stderr [ ${XBID[0]} = $CONV_BID ]
assert --stdout --stderr [ ${XID[0]} = $CONV_BID ]
executeOk_servald rhizome extract bundle $CONV_BID manifest.conv payload.conv $CONV_SECRET
tfw_cat -v manifest.conv --hexdump payload.conv
# The only "MeshMS2" bundles should be the two ply bundles
executeOk_servald rhizome list MeshMS2
rhizome_list_unpack X
unpack_stdout_list X
assert --stdout [ $XNROWS -eq 2 ]
local bid
for bid in ${XBID[*]}; do
for bid in ${XID[*]}; do
executeOk_servald rhizome extract bundle $bid manifest.$bid payload.$bid
tfw_cat -v manifest.$bid --hexdump payload.$bid
done