c971a29b5d5fef6c75649673ca5be662c78b0127
[gregoa/zavai.git] / src / core.vala
1 /*
2  * app - zavai main window
3  *
4  * Copyright (C) 2009  Enrico Zini <enrico@enricozini.org>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 using GLib;
22
23 namespace zavai {
24
25 public interface Resource : Object {
26     /**
27      * Shut down this resource.
28      *
29      * Normally one does nothing here, but it is important to give resources a
30      * chance to do cleanup when the program quits.
31      * 
32      * This can be used for tasks like closing the tags on a GPX track,
33      * releasing a FSO resource, restoring mixer settings and so on.
34      */
35     public abstract void shutdown();
36 }
37
38 public abstract class Service : Object, Resource {
39     public string name { get; construct; }
40
41     bool _started;
42     public bool started {
43         get { return _started; }
44         set { _started = value; }
45     }
46
47     public signal void toggled(bool new_state);
48
49     protected class Request
50     {
51         public string requestor;
52         public int count;
53         public Request(string requestor)
54         {
55             this.requestor = requestor;
56             count = 1;
57         }
58     }
59
60     protected List<Request> requests;
61
62     construct
63     {
64         started = false;
65         requests = null;
66         zavai.registry.register(this);
67     }
68
69     public void shutdown()
70     {
71         stop();
72     }
73
74     /// Activate the service
75     protected virtual void start()
76     {
77         if (!started)
78         {
79             zavai.log.info("Service " + name + " started\n");
80             started = true;
81             toggled(started);
82         }
83     }
84
85     /// Deactivate the service
86     protected virtual void stop()
87     {
88         if (started)
89         {
90             zavai.log.info("Service " + name + " stopped\n");
91             started = false;
92             toggled(started);
93         }
94     }
95
96     /**
97       Request a resource using the given ID.
98      *
99      * If it is the first time the resource is requested, start it and
100      * return true. Else, take note of the request and return false.
101      *
102      * If a resource is requested multiple times with the same ID, it will
103      * need to be released multiple times with that ID.
104      */
105     public bool request(string id)
106     {
107         bool res = (requests == null);
108         bool got = false;
109         for (weak List<Request> i = requests; i != null; i = i.next)
110             if (i.data.requestor == id)
111             {
112                 ++i.data.count;
113                 got = true;
114                 break;
115             }
116         if (!got)
117             requests.prepend(new Request(id));
118         if (res) start();
119         return res;
120     }
121
122     /**
123      * Release a resource using the given ID.
124      *
125      * If after the call nothing is requesting the resource, stop it and
126      * return true. Else, take note of the release and return false.
127      *
128      * If a resource is requested multiple times with the same ID, it will
129      * need to be released multiple times with that ID.
130      */
131     public bool release(string id)
132     {
133         weak List<Request> el = null;
134         for (weak List<Request> i = requests; i != null; i = i.next)
135             if (i.data.requestor == id)
136             {
137                 el = i;
138                 break;
139             }
140
141         if (el == null)
142             return false;
143
144         requests.delete_link(el);
145
146         if (requests != null)
147             return false;
148
149         stop();
150         return true;
151     }
152 }
153
154 public abstract class ScriptService : Service
155 {
156     protected string script;
157
158     protected bool script_start()
159     {
160         string script = zavai.config.find_script(name);
161         if (script == null)
162         {
163             zavai.log.error("Hook " + name + " does not exist");
164             return false;
165         }
166         try {
167             // Then run our own script
168             zavai.config.run_script(script + " start");
169             return true;
170         } catch (Error e) {
171             zavai.log.error("Running " + script + " start: " + e.message);
172             return false;
173         }
174     }
175
176     protected bool script_stop()
177     {
178         string script = zavai.config.find_script(name);
179         if (script == null)
180         {
181             zavai.log.error("Hook " + name + " does not exist");
182             return false;
183         }
184         try {
185             // Then run our own script
186             zavai.config.run_script(script + " stop");
187             return true;
188         } catch (Error e) {
189             zavai.log.error("Running " + script + " stop: " + e.message);
190             return false;
191         }
192     }
193
194     protected bool script_status()
195     {
196         string std_out;
197         string std_err;
198         string script = zavai.config.find_script(name);
199         if (script == null)
200         {
201             zavai.log.error("Hook " + name + " does not exist");
202             return false;
203         }
204         string command = script + " status";
205         int res = zavai.config.run_script_sync(command, out std_out, out std_err);
206         if (res != 0)
207         {
208             zavai.log.error("Running " + command + ": " + std_err);
209             return false;
210         }
211
212         std_out._strip();
213
214         return (std_out == "on");
215     }
216 }
217
218 public abstract class ScriptMonitorService : Service
219 {
220     protected Pid child_pid;
221     protected int child_watch_id;
222
223     ScriptMonitorService()
224     {
225         child_pid = 0;
226         child_watch_id = 0;
227     }
228
229     protected bool script_start()
230     {
231         string command = zavai.config.homedir + "/" + name + " pre";
232         try {
233             // Then run our own script
234             zavai.config.run_script(command);
235         } catch (Error e) {
236             zavai.log.error("Running " + command + ": " + e.message);
237             return false;
238         }
239
240         command = zavai.config.homedir + "/" + name + " run";
241         zavai.log.info("Run program: " + command);
242         string[] args = command.split(" ");
243         try {
244             Process.spawn_async(
245                 Environment.get_home_dir(),
246                 args,
247                 null,
248                 SpawnFlags.SEARCH_PATH | SpawnFlags.DO_NOT_REAP_CHILD,
249                 null,
250                 out child_pid);
251         } catch (SpawnError e) {
252             zavai.log.error("Running " + command + ": " + e.message);
253             return false;
254         }
255
256         // Add a child watch source to know when it ends
257         ChildWatch.add(child_pid, on_child);
258
259         return true;
260     }
261
262     protected bool script_stop()
263     {
264         Posix.kill((Posix.pid_t)child_pid, Posix.SIGTERM);
265         return true;
266     }
267
268     protected void on_child(Pid pid, int status)
269     {
270         zavai.log.info("Exited");
271 stderr.printf("STATUS %d\n", status);
272         Process.close_pid(pid);
273
274         string command = zavai.config.homedir + "/" + name + " post";
275         try {
276             // Then run our own script
277             zavai.config.run_script(command);
278         } catch (Error e) {
279             zavai.log.error("Running " + command + ": " + e.message);
280             return;
281         }
282
283         cleanup_after_script_stop();
284
285         base.stop();
286     }
287
288     protected virtual void cleanup_after_script_stop()
289     {
290     }
291
292     /*
293     protected bool script_status()
294     {
295     }
296     */
297 }
298
299 }