[rygel/wip/basic-management: 61/95] core: Implement BasicManagement NSLookup, improve BMTest



commit 5afc09e10af0bcf551b850e0448ea3166ac27c73
Author: Jussi Kukkonen <jku heimdall>
Date:   Thu May 30 16:07:30 2013 +0300

    core: Implement BasicManagement NSLookup, improve BMTest

 configure.ac                                    |    2 +-
 src/librygel-core/Makefile.am                   |    1 +
 src/librygel-core/rygel-basic-management.vala   |   65 +++---
 src/librygel-core/rygel-bm-test-nslookup.vala   |  321 ++++++++++++++++++-----
 src/librygel-core/rygel-bm-test-ping.vala       |    8 -
 src/librygel-core/rygel-bm-test-traceroute.vala |    8 -
 src/librygel-core/rygel-bm-test.vala            |  152 ++++++++++-
 7 files changed, 439 insertions(+), 118 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 3e4982e..26b3c79 100644
--- a/configure.ac
+++ b/configure.ac
@@ -58,7 +58,7 @@ VALAFLAGS="--target-glib=2.32 $VALAFLAGS"
 RYGEL_BASE_MODULES="gupnp-1.0 >= $GUPNP_REQUIRED gee-0.8 >= $GEE_REQUIRED"
 PKG_CHECK_MODULES([LIBRYGEL_CORE_DEPS], [$RYGEL_BASE_MODULES uuid >= $UUID_REQUIRED gssdp-1.0 >= 
$GSSDP_REQUIRED gio-2.0 >= $GIO_REQUIRED gmodule-2.0 libxml-2.0 >= $LIBXML_REQUIRED])
 RYGEL_BASE_MODULES_VALAFLAGS='--pkg gupnp-1.0 --pkg gee-0.8'
-LIBRYGEL_CORE_DEPS_VALAFLAGS="$RYGEL_BASE_MODULES_VALAFLAGS --pkg gssdp-1.0 --pkg gio-2.0 --pkg gmodule-2.0"
+LIBRYGEL_CORE_DEPS_VALAFLAGS="$RYGEL_BASE_MODULES_VALAFLAGS --pkg gssdp-1.0 --pkg gio-2.0 --pkg gmodule-2.0 
--pkg posix"
 AC_SUBST([LIBRYGEL_CORE_DEPS_VALAFLAGS])
 
 RYGEL_COMMON_MODULES="$RYGEL_BASE_MODULES gupnp-av-1.0 >= $GUPNP_AV_REQUIRED"
diff --git a/src/librygel-core/Makefile.am b/src/librygel-core/Makefile.am
index 64194f4..eaca37a 100644
--- a/src/librygel-core/Makefile.am
+++ b/src/librygel-core/Makefile.am
@@ -15,6 +15,7 @@ librygel_core_2_0_la_VALAFLAGS = \
        -H rygel-core.h -C --library=rygel-core-2.0 \
        --vapidir=$(srcdir) \
        --pkg uuid \
+       --pkg posix \
        $(LIBRYGEL_CORE_DEPS_VALAFLAGS) \
        $(RYGEL_COMMON_VALAFLAGS)
 
diff --git a/src/librygel-core/rygel-basic-management.vala b/src/librygel-core/rygel-basic-management.vala
index 2d67c01..984902a 100644
--- a/src/librygel-core/rygel-basic-management.vala
+++ b/src/librygel-core/rygel-basic-management.vala
@@ -86,7 +86,7 @@ public class Rygel.BasicManagement : Service {
                                         (this.cancel_test_cb);
     }
 
-    private void add_test (BMTest bm_test) {
+    private void add_test_and_return_action (BMTest bm_test, ServiceAction action) {
         current_id++;
         bm_test.id = current_id.to_string ();
 
@@ -100,6 +100,22 @@ public class Rygel.BasicManagement : Service {
                  this.test_ids += "," + test.id;
              }
         }
+
+        /* TODO: decide if test should really execute now */
+
+        bm_test.execute.begin ((obj,res) => {
+            try {
+                bm_test.execute.end (res);
+            } catch (BMTestError e) {
+                /* already executing */
+            }
+            /* TODO Test is finished, now remove test from active test list */
+        });
+
+        action.set ("TestID",
+                        typeof (string),
+                        bm_test.id);
+        action.return ();
     }
 
     // Error out if 'TestID' is wrong
@@ -215,15 +231,7 @@ public class Rygel.BasicManagement : Service {
         }
 
         // test_id to be added to TestIDs and ActiveTestID
-        this.add_test (ping as BMTest);
-
-        ping.execute ();
-
-        action.set ("TestID",
-                        typeof (string),
-                        ping.id);
-
-        action.return ();
+        this.add_test_and_return_action (ping as BMTest, action);
     }
 
     private void ping_result_cb (Service             cm,
@@ -302,23 +310,16 @@ public class Rygel.BasicManagement : Service {
                         typeof (string),
                         out interval_time_out);
 
+
         BMTestNSLookup nslookup = new BMTestNSLookup();
-        if (!nslookup.init (hostname, dns_server, repeat_count,
-                            interval_time_out)) {
-            action.return_error (402, _("Invalid argument"));
+        try {
+            nslookup.init (hostname, dns_server,
+                           repeat_count, interval_time_out);
 
-            return;
+            this.add_test_and_return_action (nslookup as BMTest, action);
+        } catch (BMTestError e) {
+            action.return_error (402, _("Invalid argument"));
         }
-
-        this.add_test (nslookup as BMTest);
-
-        nslookup.execute ();
-
-        action.set ("TestID",
-                        typeof (string),
-                        nslookup.id);
-
-        action.return ();
     }
 
     private void nslookup_result_cb (Service             cm,
@@ -395,15 +396,7 @@ public class Rygel.BasicManagement : Service {
             return;
         }
 
-        this.add_test (traceroute as BMTest);
-
-        traceroute.execute ();
-
-        action.set ("TestID",
-                        typeof (string),
-                        traceroute.id);
-
-        action.return ();
+        this.add_test_and_return_action (traceroute as BMTest, action);
     }
 
     private void traceroute_result_cb (Service             cm,
@@ -512,7 +505,11 @@ public class Rygel.BasicManagement : Service {
             return;
         }
 
-        bm_test.cancel();
+        try {
+            bm_test.cancel();
+        } catch (BMTestError e) {
+            warning ("Canceled test was not running\n");
+        }
 
         action.return ();
     }
diff --git a/src/librygel-core/rygel-bm-test-nslookup.vala b/src/librygel-core/rygel-bm-test-nslookup.vala
index 6c0e8f9..46faab6 100644
--- a/src/librygel-core/rygel-bm-test-nslookup.vala
+++ b/src/librygel-core/rygel-bm-test-nslookup.vala
@@ -1,10 +1,8 @@
 /*
- * Copyright (C) 2008 OpenedHand Ltd.
- * Copyright (C) 2008 Zeeshan Ali <zeenix gmail com>.
  * Copyright (C) 2013 Intel Corporation.
  *
- * Author: Jorn Baayen <jorn openedhand com>
- *         Zeeshan Ali <zeenix gmail com>
+ * Author: Christophe Guiraud,
+ *         Jussi Kukkonen 
  *
  * This file is part of Rygel.
  *
@@ -25,83 +23,286 @@
 
 using GLib;
 
-// Helper class for BMTestNSLookup.
 internal class Rygel.BMTestNSLookup : BMTest {
+    private const string HEADER =
+        "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
+        "<bms:NSLookupResult " +
+            "xmlns:bms=\"urn:schemas-upnp-org:dm:bms\" " +
+            "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"; " +
+            "xsi:schemaLocation=\"" +
+                "urn:schemas-upnp-org:dm:bms " +
+                "http://www.upnp.org/schemas/dm/bms.xsd\";>\n";
 
-    private string hostname;
-    private string dns_server;
-    private uint repeat_count;
-    private uint32 interval_time_out;
-
-    private string status;
-    private string additional_info;
-    private uint success_count;
-    private string result;
-
-    private static const uint NSLOOKUP_MIN_REPEAT_COUNT = 1;
-    private static const uint NSLOOKUP_MAX_REPEAT_COUNT = 100;
-    private static const uint NSLOOKUP_DEFAULT_REPEAT_COUNT = 1;
-    private static const uint NSLOOKUP_MIN_REQUEST_INTERVAL_TIMEOUT = 1000;
-    private static const uint NSLOOKUP_MAX_REQUEST_INTERVAL_TIMEOUT = 30000;
-    private static const uint NSLOOKUP_DEFAULT_REQUEST_INTERVAL_TIMEOUT = 10000;
-    private static const uint NSLOOKUP_MAX_RESULT_ANSWER_STR_SIZE = 32;
-    private static const uint NSLOOKUP_MAX_RESULT_NAME_STR_SIZE = 256;
-    private static const uint NSLOOKUP_MAX_RESULT_IPS_STR_SIZE = 1024;
-    private static const uint NSLOOKUP_MAX_RESULT_ARRAY_SIZE = 7;
+    private const string FOOTER = "</bms:NSLookupResult>\n";
 
-    public BMTestNSLookup() {
-        base("NSLookup");
+    private enum ProcessState {
+        INIT,
+        SERVER,
+        NAME,
     }
 
-    public override void execute() {
-        stdout.printf("*NSLookup* execute()\n");
+    private enum GenericStatus {
+        SUCCESS,
+        ERROR_DNS_SERVER_NOT_RESOLVED,
+        ERROR_INTERNAL;
+        
+        public string to_string() {
+            switch (this) {
+                case SUCCESS:
+                    return "Success";
+                case ERROR_DNS_SERVER_NOT_RESOLVED:
+                    return "Error_DNSServerNotResolved";
+                case ERROR_INTERNAL:
+                    return "Error_Internal";
+                default:
+                    assert_not_reached();
+            }
+        }
     }
 
-    public override void cancel() {
-        stdout.printf("*NSLookup* cancel()\n");
+    private enum ResultStatus {
+        SUCCESS,
+        ERROR_DNS_SERVER_NOT_AVAILABLE,
+        ERROR_HOSTNAME_NOT_RESOLVED,
+        ERROR_TIMEOUT,
+        ERROR_OTHER;
+
+        public string to_string() {
+            switch (this) {
+                case SUCCESS:
+                    return "Success";
+                case ERROR_DNS_SERVER_NOT_AVAILABLE:
+                    return "Error_DNSServerNotAvailable";
+                case ERROR_HOSTNAME_NOT_RESOLVED:
+                    return "Error_HostNameNotResolved";
+                case ERROR_TIMEOUT:
+                    return "Error_Timeout";
+                case ERROR_OTHER:
+                    return "Error_Other";
+                default:
+                    assert_not_reached();
+            }
+        }
     }
 
-    public bool init(string hostname, string dns_server, uint repeat_count,
-                     uint32 interval_time_out) {
-        stdout.printf("*NSLookup* init()\n");
+    private enum AnswerType {
+        NONE,
+        AUTHORITATIVE,
+        NON_AUTHORITATIVE;
 
-        if (hostname == null) {
-            return false;
+        public string to_string() {
+            switch (this) {
+                case NONE:
+                    return "None";
+                case AUTHORITATIVE:
+                    return "Authoritative";
+                case NON_AUTHORITATIVE:
+                    return "NonAuthoritative";
+                default:
+                    assert_not_reached();
+            }
         }
+    }
 
-        if (dns_server == null) {
-            return false;
-        }
+    /* TODO make sure all these are respected */
+    private static const uint MAX_REPEAT_COUNT = 100;
+    private static const uint MIN_INTERVAL_TIMEOUT = 1000;
+    private static const uint MAX_INTERVAL_TIMEOUT = 30000;
+    private static const uint MAX_RESULT_ANSWER_STR_SIZE = 32; // ?
+    private static const uint MAX_RESULT_NAME_STR_SIZE = 256; // ?
+    private static const uint MAX_RESULT_IPS_STR_SIZE = 1024; // length of addresses csv ?
+    private static const uint MAX_RESULT_ARRAY_SIZE = 7; // ?
+
+    private struct Result {
+        private ProcessState state;
+        private string name_server_address;
+        private string returned_host_name;
+        private string[] addresses;
+        private ResultStatus status;
+        private AnswerType answer_type;
+        uint execution_time;
 
-        if (repeat_count == 0) {
-            repeat_count = NSLOOKUP_DEFAULT_REPEAT_COUNT;
-        } else if (repeat_count < NSLOOKUP_MIN_REPEAT_COUNT &&
-                   repeat_count > NSLOOKUP_MAX_REPEAT_COUNT) {
-            return false;
+        private string get_addresses_csv () {
+            var builder = new StringBuilder ("");
+            foreach (var address in addresses) {
+                if (builder.len != 0)
+                    builder.append (",");
+                builder.append (address);
+            }
+            return builder.str;
         }
 
-        if (interval_time_out == 0) {
-            interval_time_out = NSLOOKUP_DEFAULT_REQUEST_INTERVAL_TIMEOUT;
-        } else if (interval_time_out < NSLOOKUP_MIN_REQUEST_INTERVAL_TIMEOUT &&
-                   interval_time_out > NSLOOKUP_MAX_REQUEST_INTERVAL_TIMEOUT) {
-            return false;
+        public string to_xml_fragment() {
+            /* TODO limit the returned values */
+
+            return ("<Result>\n" +
+                    "<Status>%s</Status>\n" +
+                    "<AnswerType>%s</AnswerType>\n" +
+                    "<HostNameReturned>%s</HostNameReturned>\n" +
+                    "<IPAddresses>%s</IPAddresses>\n" +
+                    "<DNSServerIP>%s</DNSServerIP>\n" +
+                    "<ResponseTime>%u</ResponseTime>\n" +
+                    "</Result>\n").printf (this.status.to_string(),
+                                           this.answer_type.to_string(),
+                                           this.returned_host_name,
+                                           this.get_addresses_csv (),
+                                           this.name_server_address,
+                                           this.execution_time); 
         }
+    }
+
+    private Result[] results;
+    private GenericStatus generic_status;
+    private Timer timer = new Timer ();
+
+    public BMTestNSLookup() {
+        base("NSLookup");
+    }
+
+
+    public void init(string host_name, string? name_server, uint repetitions,
+                     uint32 interval_time_out) throws BMTestError {
+        command = { "nslookup" };
+        generic_status = GenericStatus.ERROR_INTERNAL;
+        results = {};
+
+        /* TODO should invalid values fail now instead of just limit ? */
+        repetitions = uint.max (1, repetitions);
+        this.repetitions = uint.min (repetitions, MAX_REPEAT_COUNT);
+
+        interval_time_out = uint.max (MIN_INTERVAL_TIMEOUT, interval_time_out);
+        interval_time_out = uint.min (interval_time_out, MAX_INTERVAL_TIMEOUT);
+        command += ("-timeout=%u").printf (interval_time_out/1000);
+
+        if (host_name == null || host_name.length < 1)
+            throw new BMTestError.INIT_FAILED ("Host name is required"); 
+        command += host_name;
+
+        if (name_server != null && name_server.length > 0)
+            command += name_server;
 
-        this.hostname = hostname;
-        this.dns_server = dns_server;
-        this.repeat_count = repeat_count;
-        this.interval_time_out = interval_time_out;
+    }
+
+    protected override void init_iteration () {
+        base.init_iteration ();
+        var result = Result () {
+            state = ProcessState.INIT,
+            name_server_address = "",
+            returned_host_name = "",
+            addresses = {},
+            status = ResultStatus.ERROR_OTHER,
+            answer_type = AnswerType.NONE,
+            execution_time = 0
+        };
+        results += result;
+
+        timer.start ();
+    }
+
+    protected override void finish_iteration () {
+        var execution_time = (uint)Math.round(timer.elapsed (null) * 1000);
+        results[results.length - 1].execution_time = execution_time;
 
-        return true;
+        base.finish_iteration ();
     }
 
+    protected override void handle_output (string line) {
+        var result = results[results.length - 1];
+        line.strip();
+        if (line.has_prefix ("Server:")) {
+            if (result.state != ProcessState.INIT)
+                warning ("nslookup parser: Unexpected 'Server:' line.\n");
+            result.state = ProcessState.SERVER;
+        } else if (line.has_prefix ("Name:")) {
+            if (result.state == ProcessState.INIT)
+                warning ("nslookup parser: Unexpected 'Name:' line");
+            else if (result.state == ProcessState.SERVER)
+                result.returned_host_name = line.substring ("Name:".length).strip(); 
+            result.state = ProcessState.NAME;
+        } else if (line.has_prefix ("Address:")) {
+            if (result.state == ProcessState.SERVER) {
+                var address = line.substring ("Address:".length).strip();
+                result.name_server_address = address.split ("#", 2)[0];
+                generic_status = GenericStatus.SUCCESS;
+            } else if (result.state == ProcessState.NAME) {
+                result.addresses += line.substring ("Address:".length).strip();
+                result.status = ResultStatus.SUCCESS;
+                if (result.answer_type == AnswerType.NONE)
+                    result.answer_type = AnswerType.AUTHORITATIVE;
+            } else
+                warning ("nslookup parser: Unexpected 'Address:' line");
+        } else if (line.has_prefix ("Non-authoritative answer:")) {
+            result.answer_type = AnswerType.NON_AUTHORITATIVE;
+        } else if (line.contains ("server can't find")) {
+            result.status = ResultStatus.ERROR_HOSTNAME_NOT_RESOLVED;
+        } else if (line.contains ("couldn't get address for")) {
+            generic_status = GenericStatus.ERROR_DNS_SERVER_NOT_RESOLVED;
+            result.status = ResultStatus.ERROR_DNS_SERVER_NOT_AVAILABLE;
+            /* TODO should cancel here: future iterations won't help */
+        } else if (line.contains ("no servers could be reached")) {
+            result.status = ResultStatus.ERROR_DNS_SERVER_NOT_AVAILABLE;
+        }
+
+        /* there has to be a nicer way to do this... */
+        results[results.length - 1] = result;
+    }
+
+
     public void get_results(out string status, out string additional_info,
-                            out uint success_count, out string result) {
-        stdout.printf("*NSLookup* get_results()\n");
+                            out uint success_count, out string result_string) {
+        success_count = 0;
+        StringBuilder builder = new StringBuilder (HEADER);
+
+        foreach (var result in results) {
+            builder.append (result.to_xml_fragment ());
+            if (result.status == ResultStatus.SUCCESS)
+                success_count++;
+        }
+        builder.append (FOOTER);
+        result_string = builder.str;
+
+        status = generic_status.to_string();
+        additional_info = "";
+    }
+
+/*
+    //valac --pkg gio-2.0 --pkg posix -X -lm  rygel-bm-test-nslookup.vala rygel-bm-test.vala
+
+    private static int main(string[] args) {
+        MainLoop loop = new MainLoop();
+        BMTestNSLookup nslookup = new BMTestNSLookup ();
+
+        if (args.length < 2) {
+            print ("Usage: %s <hostname> [<nameserver> [<repetitions> [<timeout>]]]\n", args[0]);
+            return 0;
+        }
+        
+        try {
+            nslookup.init (args[1],
+                           args.length > 2 ? args[2] : null,
+                           args.length > 3 ? int.parse (args[3]): 0,
+                           args.length > 4 ? int.parse (args[4]) : 0);
+        } catch (BMTestError e) {
+            warning ("Incorrect parameters");
+        }
+
+        nslookup.execute.begin((obj, res)=> {
+            try {
+                string status;
+                string info;
+                string results;
+                uint count;
+                nslookup.execute.end(res);
+                nslookup.get_results (out status, out info, out count, out results);
+                print ("\nStatus: %s, %u successful iterations.\nResults:\n%s", status, count, results);
+            } catch (Error e) {
+                print ("Oops: %s\n", e.message);
+            }
+            loop.quit();
+        });
+        loop.run();
 
-        status = this.status;
-        additional_info = this.additional_info;
-        success_count = this.success_count;
-        result = this.result;
+        return 0;
     }
+*/
 }
diff --git a/src/librygel-core/rygel-bm-test-ping.vala b/src/librygel-core/rygel-bm-test-ping.vala
index 969a2db..fa250bb 100644
--- a/src/librygel-core/rygel-bm-test-ping.vala
+++ b/src/librygel-core/rygel-bm-test-ping.vala
@@ -62,14 +62,6 @@ internal class Rygel.BMTestPing : BMTest {
         base("Ping");
     }
 
-    public override void execute() {
-        stdout.printf("*Ping* execute()\n");
-    }
-
-    public override void cancel() {
-        stdout.printf("*Ping* cancel()\n");
-    }
-
     public bool init(string host, uint repeat_count, uint data_block_size,
                      uint dscp, uint32 interval_time_out) {
         stdout.printf("*Ping* init()\n");
diff --git a/src/librygel-core/rygel-bm-test-traceroute.vala b/src/librygel-core/rygel-bm-test-traceroute.vala
index 963ad7d..e966cb3 100644
--- a/src/librygel-core/rygel-bm-test-traceroute.vala
+++ b/src/librygel-core/rygel-bm-test-traceroute.vala
@@ -58,14 +58,6 @@ internal class Rygel.BMTestTraceroute : BMTest {
         base("Traceroute");
     }
 
-    public override void execute() {
-        stdout.printf("*Traceroute* execute()\n");
-    }
-
-    public override void cancel() {
-        stdout.printf("*Traceroute* cancel()\n");
-    }
-
     public bool init(string host, uint32 wait_time_out, uint data_block_size,
                      uint max_hop_count, uint dscp) {
         stdout.printf("*Traceroute* init()\n");
diff --git a/src/librygel-core/rygel-bm-test.vala b/src/librygel-core/rygel-bm-test.vala
index fb2f6ae..c3b0307 100644
--- a/src/librygel-core/rygel-bm-test.vala
+++ b/src/librygel-core/rygel-bm-test.vala
@@ -1,10 +1,8 @@
 /*
- * Copyright (C) 2008 OpenedHand Ltd.
- * Copyright (C) 2008 Zeeshan Ali <zeenix gmail com>.
  * Copyright (C) 2013 Intel Corporation.
  *
- * Author: Jorn Baayen <jorn openedhand com>
- *         Zeeshan Ali <zeenix gmail com>
+ * Author: Christophe Guiraud,
+ *         Jussi Kukkonen 
  *
  * This file is part of Rygel.
  *
@@ -25,19 +23,159 @@
 
 using GLib;
 
-// Helper class for BMTest.
+
+internal errordomain Rygel.BMTestError {
+    NOT_POSSIBLE,
+    INIT_FAILED,
+}
+
 internal abstract class Rygel.BMTest : Object {
     public string type;
     public string state;
     public string id;
 
+    public bool executing { private set; get; default = false; }
+
+    /* properties for implementations to access */
+    protected SpawnFlags flags = SpawnFlags.SEARCH_PATH |
+                                 SpawnFlags.LEAVE_DESCRIPTORS_OPEN;
+    protected string[] command;
+    protected uint execution_time { private set; get; default = 0; } 
+    protected uint repetitions;
+
+    private int std_out;
+    private int std_err;
+    private Pid child_pid;
+    private SourceFunc async_callback;
+    private bool canceled;
+    private uint iteration;
+
+    /* These virtual/abstract functions will be called from execute():
+     * - For every iteration:
+     *    - init_iteration()
+     *    - calls to handle_output() and handle_error(),
+     *    - finish_iteration()
+     * - then finish() 
+     */
+    protected virtual void init_iteration () {}
+    protected virtual void handle_output (string line) {}
+    protected virtual void handle_error (string line) {
+        warning ("%s stderr: %s", command[0], line);
+    }
+    protected virtual void finish_iteration () {
+        iteration++;
+        if (iteration < repetitions) {
+            run_iteration ();
+        } else {
+            async_callback ();
+        }
+    }
+    protected virtual void finish (bool canceled) {}
+
+        
+    private void child_setup () {
+        /* try to prevent possible changes in output */
+        Environment.set_variable("LC_MESSAGES", "C", true);
+
+        /* Create new session to detach from tty, but set a process
+         * group so all children can be ḱilled if need be */
+        Posix.setsid ();
+        Posix.setpgid (0, 0);
+    }
+
+    private void run_iteration () {
+        try {
+            init_iteration ();
+            Process.spawn_async_with_pipes (null,
+                                            command,
+                                            null,
+                                            flags,
+                                            child_setup,
+                                            out child_pid,
+                                            null,
+                                            out std_out,
+                                            out std_err);
+
+            var out_channel = new IOChannel.unix_new (std_out);
+            out_channel.add_watch (IOCondition.OUT | IOCondition.HUP,
+                                   out_watch);
+
+            var err_channel = new IOChannel.unix_new (std_err);
+            err_channel.add_watch (IOCondition.OUT | IOCondition.HUP,
+                                   err_watch);
+        } catch (SpawnError e) {
+            /* TODO cancel all iterations and let the implementation know
+             * we failed to spawn */
+        }
+    }
+
+    private bool out_watch (IOChannel channel, IOCondition condition) {
+        try {
+            string line;
+            IOStatus status = channel.read_line (out line, null, null);
+            if (line != null)
+                handle_output (line);
+
+            if (status == IOStatus.EOF) {
+                finish_iteration ();
+                return false;
+            }
+        } catch (Error e) {
+            warning ("Failed readline() from nslookup stdout: %s", e.message);
+            finish_iteration();
+            return false;
+        }
+
+        return true;
+    }
+
+    private bool err_watch (IOChannel channel, IOCondition condition) {
+        try {
+            string line;
+            IOStatus status = channel.read_line (out line, null, null);
+            if (line != null)
+                handle_error (line);
+            if (status == IOStatus.EOF)
+                return false;
+        } catch (Error e) {
+            warning ("Failed readline() from nslookup stderr: %s", e.message);
+            return false;
+        }
+
+        return true;
+    }
+
+
     public BMTest(string type) {
         this.type = type;
         this.state = "Requested";
         this.id = null;
     }
 
-    public abstract void execute();
+    public async void execute () throws BMTestError {
+        if (executing)
+            throw new BMTestError.NOT_POSSIBLE ("Already executing");
 
-    public abstract void cancel();
+        executing = true;
+        canceled = false;
+        iteration = 0;
+
+        run_iteration ();
+
+        async_callback = execute.callback;
+        yield;
+
+        executing = false;
+        finish (canceled);
+        return ;
+    }
+
+    public void cancel () throws BMTestError {
+        if (!executing)
+            throw new BMTestError.NOT_POSSIBLE ("Not executing"); 
+
+        Posix.killpg (child_pid, Posix.SIGTERM);
+
+        canceled = true;
+    }
 }


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]