core: Separate VAPI for misc functions from C world
[profile/ivi/rygel.git] / src / rygel / rygel-main.vala
1 /*
2  * Copyright (C) 2008 Nokia Corporation.
3  * Copyright (C) 2008 Zeeshan Ali (Khattak) <zeeshanak@gnome.org>.
4  *
5  * Author: Zeeshan Ali (Khattak) <zeeshanak@gnome.org>
6  *
7  * This file is part of Rygel.
8  *
9  * Rygel is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU Lesser General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * Rygel is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public License
20  * along with this program; if not, write to the Free Software Foundation,
21  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22  */
23
24 using Gee;
25 using GUPnP;
26
27 public class Rygel.Main : Object {
28     private static int PLUGIN_TIMEOUT = 5;
29
30     private PluginLoader plugin_loader;
31     private ContextManager context_manager;
32     private ArrayList <RootDeviceFactory> factories;
33     private ArrayList <RootDevice> root_devices;
34
35     private Configuration config;
36     private LogHandler log_handler;
37
38     private MainLoop main_loop;
39
40     private int exit_code;
41     public bool need_restart;
42
43     private Main () throws GLib.Error {
44         Environment.set_application_name (_(BuildConfig.PACKAGE_NAME));
45
46         this.log_handler = LogHandler.get_default ();
47         this.config = MetaConfig.get_default ();
48         this.plugin_loader = new PluginLoader ();
49         this.root_devices = new ArrayList <RootDevice> ();
50         this.factories = new ArrayList <RootDeviceFactory> ();
51         this.context_manager = this.create_context_manager ();
52         this.main_loop = new GLib.MainLoop (null, false);
53
54         this.exit_code = 0;
55
56         this.plugin_loader.plugin_available.connect (this.on_plugin_loaded);
57
58         SignalHandler.setup (this);
59     }
60
61     public void exit (int exit_code) {
62         this.exit_code = exit_code;
63
64         this.main_loop.quit ();
65
66         SignalHandler.cleanup ();
67     }
68
69     public void restart () {
70         this.need_restart = true;
71
72         this.exit (0);
73     }
74
75     private int run () {
76         this.plugin_loader.load_plugins ();
77
78         Timeout.add_seconds (PLUGIN_TIMEOUT, () => {
79             if (this.plugin_loader.list_plugins ().size == 0) {
80                 warning (_("No plugins found in %d seconds, giving up.."),
81                          PLUGIN_TIMEOUT);
82
83                 this.exit (-82);
84             }
85
86             return false;
87         });
88
89         this.main_loop.run ();
90
91         return this.exit_code;
92     }
93
94     private void on_plugin_loaded (PluginLoader plugin_loader,
95                                    Plugin       plugin) {
96         var iterator = this.factories.iterator ();
97         while (iterator.next ()) {
98             this.create_device.begin (plugin, iterator.get ());
99         }
100     }
101
102     private ContextManager create_context_manager () {
103         int port = 0;
104
105         try {
106             port = this.config.get_port ();
107         } catch (GLib.Error err) {}
108
109         var manager = new ContextManager (null, port);
110
111         manager.context_available.connect (this.on_context_available);
112         manager.context_unavailable.connect (this.on_context_unavailable);
113
114         return manager;
115     }
116
117     private void on_context_available (GUPnP.ContextManager manager,
118                                        GUPnP.Context        context) {
119         string iface = null;
120
121         debug (_("new network context %s (%s) available."),
122                context.interface,
123                context.host_ip);
124
125         try {
126             iface = this.config.get_interface ();
127         } catch (GLib.Error err) {}
128
129         if (iface == null || iface == context.interface) {
130             try {
131                 var factory = new RootDeviceFactory (context);
132                 this.factories.add (factory);
133
134                 var iterator = this.plugin_loader.list_plugins ().iterator ();
135                 while (iterator.next ()) {
136                     this.create_device.begin (iterator.get (), factory);
137                 }
138             } catch (GLib.Error err) {
139                 warning (_("Failed to create root device factory: %s"),
140                          err.message);
141             }
142         } else {
143             debug (_("Ignoring network context %s (%s)."),
144                    context.interface,
145                    context.host_ip);
146         }
147     }
148
149     private void on_context_unavailable (GUPnP.ContextManager manager,
150                                          GUPnP.Context        context) {
151         debug (_("Network context %s (%s) now unavailable."),
152                context.interface,
153                context.host_ip);
154
155         var factory_iter = this.factories.iterator ();
156         while (factory_iter.next ()) {
157             if (context == factory_iter.get ().context) {
158                 factory_iter.remove ();
159             }
160         }
161
162         var device_iter = this.root_devices.iterator ();
163         while (device_iter.next ()) {
164             if (context == device_iter.get ().context) {
165                 device_iter.remove ();
166             }
167         }
168     }
169
170     private async void create_device (Plugin            plugin,
171                                       RootDeviceFactory factory) {
172         // The call to factory.create(), although synchronous spins the
173         // the mainloop and therefore might in turn triger some of the signal
174         // handlers here that modify one of the lists that we might be iterating
175         // while this function is called. Modification of an ArrayList while
176         // iterating it is currently unsuppored and leads to a crash and that is
177         // why defer to mainloop here.
178         Idle.add (create_device.callback);
179         yield;
180
181         try {
182             var device = factory.create (plugin);
183
184             device.available = plugin.available;
185
186             this.root_devices.add (device);
187
188             plugin.notify["available"].connect (this.on_plugin_notify);
189         } catch (GLib.Error error) {
190             warning (_("Failed to create RootDevice for %s. Reason: %s"),
191                      plugin.name,
192                      error.message);
193         }
194     }
195
196     private void on_plugin_notify (Object    obj,
197                                    ParamSpec spec) {
198         var plugin = obj as Plugin;
199
200         foreach (var device in this.root_devices) {
201             if (device.resource_factory == plugin) {
202                 device.available = plugin.available;
203             }
204         }
205     }
206
207     private static int main (string[] args) {
208         Main main = null;
209         DBusService service;
210
211         var original_args = args;
212
213         try {
214             // Parse commandline options
215             CmdlineConfig.parse_args (ref args);
216
217             // initialize gstreamer
218             var dummy_args = new string[0];
219             Gst.init (ref dummy_args);
220
221             main = new Main ();
222             service = new DBusService (main);
223         } catch (DBus.Error err) {
224             warning (_("Failed to start D-Bus service: %s"), err.message);
225         } catch (CmdlineConfigError.VERSION_ONLY err) {
226             return 0;
227         } catch (GLib.Error err) {
228             error ("%s", err.message);
229
230             return -1;
231         }
232
233         int exit_code = main.run ();
234
235         if (main.need_restart) {
236             Misc.Posix.execvp (original_args[0], original_args);
237         }
238
239         return exit_code;
240     }
241 }
242