build,core,plugins: Port to GDBus and GVariant
[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 (ngettext ("No plugins found in %d second; giving up..",
81                                    "No plugins found in %d seconds; giving up..",
82                                    PLUGIN_TIMEOUT),
83                          PLUGIN_TIMEOUT);
84
85                 this.exit (-82);
86             }
87
88             return false;
89         });
90
91         this.main_loop.run ();
92
93         return this.exit_code;
94     }
95
96     private void on_plugin_loaded (PluginLoader plugin_loader,
97                                    Plugin       plugin) {
98         var iterator = this.factories.iterator ();
99         while (iterator.next ()) {
100             this.create_device.begin (plugin, iterator.get ());
101         }
102     }
103
104     private ContextManager create_context_manager () {
105         int port = 0;
106
107         try {
108             port = this.config.get_port ();
109         } catch (GLib.Error err) {}
110
111         var manager = new ContextManager (null, port);
112
113         manager.context_available.connect (this.on_context_available);
114         manager.context_unavailable.connect (this.on_context_unavailable);
115
116         return manager;
117     }
118
119     private void on_context_available (GUPnP.ContextManager manager,
120                                        GUPnP.Context        context) {
121         string iface = null;
122
123         debug ("new network context %s (%s) available.",
124                context.interface,
125                context.host_ip);
126
127         try {
128             iface = this.config.get_interface ();
129         } catch (GLib.Error err) {}
130
131         if (iface == null || iface == context.interface) {
132             try {
133                 var factory = new RootDeviceFactory (context);
134                 this.factories.add (factory);
135
136                 var iterator = this.plugin_loader.list_plugins ().iterator ();
137                 while (iterator.next ()) {
138                     this.create_device.begin (iterator.get (), factory);
139                 }
140             } catch (GLib.Error err) {
141                 warning (_("Failed to create root device factory: %s"),
142                          err.message);
143             }
144         } else {
145             debug ("Ignoring network context %s (%s).",
146                    context.interface,
147                    context.host_ip);
148         }
149     }
150
151     private void on_context_unavailable (GUPnP.ContextManager manager,
152                                          GUPnP.Context        context) {
153         debug ("Network context %s (%s) now unavailable.",
154                context.interface,
155                context.host_ip);
156
157         var factory_iter = this.factories.iterator ();
158         while (factory_iter.next ()) {
159             if (context == factory_iter.get ().context) {
160                 factory_iter.remove ();
161             }
162         }
163
164         var device_iter = this.root_devices.iterator ();
165         while (device_iter.next ()) {
166             if (context == device_iter.get ().context) {
167                 device_iter.remove ();
168             }
169         }
170     }
171
172     private async void create_device (Plugin            plugin,
173                                       RootDeviceFactory factory) {
174         // The call to factory.create(), although synchronous spins the
175         // the mainloop and therefore might in turn triger some of the signal
176         // handlers here that modify one of the lists that we might be iterating
177         // while this function is called. Modification of an ArrayList while
178         // iterating it is currently unsuppored and leads to a crash and that is
179         // why defer to mainloop here.
180         Idle.add (create_device.callback);
181         yield;
182
183         try {
184             var device = factory.create (plugin);
185
186             device.available = plugin.available;
187
188             this.root_devices.add (device);
189
190             plugin.notify["available"].connect (this.on_plugin_notify);
191         } catch (GLib.Error error) {
192             warning (_("Failed to create RootDevice for %s. Reason: %s"),
193                      plugin.name,
194                      error.message);
195         }
196     }
197
198     private void on_plugin_notify (Object    obj,
199                                    ParamSpec spec) {
200         var plugin = obj as Plugin;
201
202         foreach (var device in this.root_devices) {
203             if (device.resource_factory == plugin) {
204                 device.available = plugin.available;
205             }
206         }
207     }
208
209     private static int main (string[] args) {
210         Main main = null;
211         DBusService service;
212
213         var original_args = args;
214
215         Intl.setlocale (LocaleCategory.ALL, "");
216         Intl.bindtextdomain (BuildConfig.GETTEXT_PACKAGE,
217                              BuildConfig.LOCALEDIR);
218         Intl.bind_textdomain_codeset (BuildConfig.GETTEXT_PACKAGE, "UTF-8");
219         Intl.textdomain (BuildConfig.GETTEXT_PACKAGE);
220
221         try {
222             // Parse commandline options
223             CmdlineConfig.parse_args (ref args);
224
225             // initialize gstreamer
226             var dummy_args = new string[0];
227             Gst.init (ref dummy_args);
228
229             main = new Main ();
230             service = new DBusService (main);
231         } catch (IOError err) {
232             warning (_("Failed to start D-Bus service: %s"), err.message);
233         } catch (CmdlineConfigError.VERSION_ONLY err) {
234             return 0;
235         } catch (GLib.Error err) {
236             error ("%s", err.message);
237         }
238
239         int exit_code = main.run ();
240
241         if (main.need_restart) {
242             Misc.Posix.execvp (original_args[0], original_args);
243         }
244
245         return exit_code;
246     }
247 }
248