/* * app - zavai main window * * Copyright (C) 2009 Enrico Zini * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ using GLib; 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(); } public abstract class Service : Object, Resource { 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 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 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 el = null; for (weak List 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"); stderr.printf("STATUS %d\n", status); 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() { } */ } }