*/
using GLib;
-using Gee;
namespace zavai {
public interface Resource : Object {
- /**
- * Shut down this resource.
- *
- * Normally one does nothing here, but it is important to give resources a
- * chance to do cleanup when the program quits.
- *
- * This can be used for tasks like closing the tags on a GPX track,
- * releasing a FSO resource, restoring mixer settings and so on.
- */
- public abstract void shutdown();
+ /**
+ * Shut down this resource.
+ *
+ * Normally one does nothing here, but it is important to give resources a
+ * chance to do cleanup when the program quits.
+ *
+ * This can be used for tasks like closing the tags on a GPX track,
+ * releasing a FSO resource, restoring mixer settings and so on.
+ */
+ public abstract void shutdown();
}
public abstract class Service : Object, Resource {
- public string name { get; construct; }
-
- bool _started;
- public bool started {
- get { return _started; }
- set { _started = value; }
- default = false;
- }
-
- public signal void toggled(bool new_state);
-
- protected HashMap<string, int> requests;
-
- construct {
- requests = new HashMap<string, int>(str_hash, str_equal);
- }
-
- public void shutdown()
- {
- stop();
- }
-
- /// Activate the service
- protected virtual void start()
- {
- if (!started)
- {
- zavai.log.info("Service " + name + " started\n");
- started = true;
- toggled(started);
- }
- }
-
- /// Deactivate the service
- protected virtual void stop()
- {
- if (started)
- {
- zavai.log.info("Service " + name + " stopped\n");
- started = false;
- toggled(started);
- }
- }
-
- /**
- Request a resource using the given ID.
- *
- * If it is the first time the resource is requested, start it and
- * return true. Else, take note of the request and return false.
- *
- * If a resource is requested multiple times with the same ID, it will
- * need to be released multiple times with that ID.
- */
- public bool request(string id)
- {
- bool res = (requests.size == 0);
- if (id in requests)
- requests[id] = requests[id] + 1;
- else
- requests.set(id, 1);
- if (res) start();
- return res;
- }
-
- /**
- * Release a resource using the given ID.
- *
- * If after the call nothing is requesting the resource, stop it and
- * return true. Else, take note of the release and return false.
- *
- * If a resource is requested multiple times with the same ID, it will
- * need to be released multiple times with that ID.
- */
- public bool release(string id)
- {
- if (id in requests)
- {
- if (requests[id] > 1)
- requests[id] = requests[id] - 1;
- else
- requests.remove(id);
- } else {
- return false;
- }
- if (requests.size > 0)
- return false;
- stop();
- return true;
- }
+ public string name { get; construct; }
+
+ bool _started;
+ public bool started {
+ get { return _started; }
+ set { _started = value; }
+ }
+
+ public signal void toggled(bool new_state);
+
+ protected class Request
+ {
+ public string requestor;
+ public int count;
+ public Request(string requestor)
+ {
+ this.requestor = requestor;
+ count = 1;
+ }
+ }
+
+ protected List<Request> requests;
+
+ construct
+ {
+ started = false;
+ requests = null;
+ zavai.registry.register(this);
+ }
+
+ public void shutdown()
+ {
+ stop();
+ }
+
+ /// Activate the service
+ protected virtual void start()
+ {
+ if (!started)
+ {
+ zavai.log.info("Service " + name + " started\n");
+ started = true;
+ toggled(started);
+ }
+ }
+
+ /// Deactivate the service
+ protected virtual void stop()
+ {
+ if (started)
+ {
+ zavai.log.info("Service " + name + " stopped\n");
+ started = false;
+ toggled(started);
+ }
+ }
+
+ /**
+ Request a resource using the given ID.
+ *
+ * If it is the first time the resource is requested, start it and
+ * return true. Else, take note of the request and return false.
+ *
+ * If a resource is requested multiple times with the same ID, it will
+ * need to be released multiple times with that ID.
+ */
+ public bool request(string id)
+ {
+ bool res = (requests == null);
+ bool got = false;
+ for (weak List<Request> i = requests; i != null; i = i.next)
+ if (i.data.requestor == id)
+ {
+ ++i.data.count;
+ got = true;
+ break;
+ }
+ if (!got)
+ requests.prepend(new Request(id));
+ if (res) start();
+ return res;
+ }
+
+ /**
+ * Release a resource using the given ID.
+ *
+ * If after the call nothing is requesting the resource, stop it and
+ * return true. Else, take note of the release and return false.
+ *
+ * If a resource is requested multiple times with the same ID, it will
+ * need to be released multiple times with that ID.
+ */
+ public bool release(string id)
+ {
+ weak List<Request> el = null;
+ for (weak List<Request> i = requests; i != null; i = i.next)
+ if (i.data.requestor == id)
+ {
+ el = i;
+ break;
+ }
+
+ if (el == null)
+ return false;
+
+ --el.data.count;
+ if (el.data.count == 0)
+ requests.delete_link(el);
+
+ if (requests != null)
+ return false;
+
+ stop();
+ return true;
+ }
+}
+
+public abstract class ScriptService : Service
+{
+ protected string script;
+
+ protected bool script_start()
+ {
+ try {
+ zavai.config.find_and_run_script(name, "start");
+ return true;
+ } catch (Error e) {
+ zavai.log.error("Running " + name + " start: " + e.message);
+ return false;
+ }
+ }
+
+ protected bool script_stop()
+ {
+ try {
+ zavai.config.find_and_run_script(name, "stop");
+ return true;
+ } catch (Error e) {
+ zavai.log.error("Running " + name + " stop: " + e.message);
+ return false;
+ }
+ }
+
+ protected bool script_status()
+ {
+ string std_out;
+ string std_err;
+ string script = zavai.config.find_script(name);
+ if (script == null)
+ {
+ zavai.log.error("Hook " + name + " does not exist");
+ return false;
+ }
+ string command = script + " status";
+ try {
+ int res = zavai.config.run_script_sync(command, out std_out, out std_err);
+ if (res != 0)
+ {
+ zavai.log.error("Running " + command + ": " + std_err);
+ return false;
+ }
+ } catch (SpawnError e) {
+ zavai.log.error("Running " + command + ": " + e.message);
+ return false;
+ }
+
+ std_out._strip();
+
+ return (std_out == "on");
+ }
+}
+
+public abstract class ScriptMonitorService : Service
+{
+ protected Pid child_pid;
+ protected int child_watch_id;
+
+ ScriptMonitorService()
+ {
+ child_pid = 0;
+ child_watch_id = 0;
+ }
+
+ protected bool script_start()
+ {
+ string script = zavai.config.find_script(name);
+ zavai.log.info("Run program: " + script + " pre");
+ try {
+ // Then run our own script
+ zavai.config.run_script(script + " pre");
+ } catch (Error e) {
+ zavai.log.error("Running " + script + " pre: " + e.message);
+ return false;
+ }
+
+ zavai.log.info("Run program: " + script + " run");
+ string[] args = { script, "run" };
+ try {
+ Process.spawn_async(
+ Environment.get_home_dir(),
+ args,
+ null,
+ SpawnFlags.SEARCH_PATH | SpawnFlags.DO_NOT_REAP_CHILD,
+ null,
+ out child_pid);
+ } catch (SpawnError e) {
+ zavai.log.error("Running " + script + " run: " + e.message);
+ return false;
+ }
+
+ // Add a child watch source to know when it ends
+ ChildWatch.add(child_pid, on_child);
+
+ return true;
+ }
+
+ protected bool script_stop()
+ {
+ Posix.kill((Posix.pid_t)child_pid, Posix.SIGTERM);
+ return true;
+ }
+
+ protected void on_child(Pid pid, int status)
+ {
+ zavai.log.info("Exited");
+ Process.close_pid(pid);
+
+ try {
+ // Then run our own script
+ zavai.config.find_and_run_script(name, "post");
+ } catch (Error e) {
+ zavai.log.error("Running " + name + " post: " + e.message);
+ return;
+ }
+
+ cleanup_after_script_stop();
+
+ base.stop();
+ }
+
+ protected virtual void cleanup_after_script_stop()
+ {
+ }
+
+ /*
+ protected bool script_status()
+ {
+ }
+ */
}
}