1 /* vi: set et sw=4 ts=8 cino=t0,(0: */
2 /* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 8 -*- */
3 /*
4  * This file is part of mission-control
5  *
6  * Copyright (C) 2007-2009 Nokia Corporation.
7  * Copyright (C) 2009 Collabora Ltd.
8  *
9  * Contact: Naba Kumar  <naba.kumar@nokia.com>
10  *
11  * This library is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Lesser General Public License
13  * version 2.1 as published by the Free Software Foundation.
14  *
15  * This library is distributed in the hope that it will be useful, but
16  * WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with this library; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
23  * 02110-1301 USA
24  *
25  */
26 
27 /**
28  * SECTION:mcd-master
29  * @title: McdMaster
30  * @short_description: Server master class
31  * @see_also:
32  * @stability: Unstable
33  * @include: mcd-master.h
34  *
35  * This class implements actual mission-control. It keeps track of
36  * individual account presence and connection states in a McdPresenceFrame
37  * member object, which is available as a property.
38  *
39  * The McdPresenceFrame object could be easily utilized for
40  * any presence releated events and actions, either within this class or
41  * any other class subclassing it or using it.
42  *
43  * It is basically a container for all McdManager objects and
44  * takes care of their management. It also takes care of sleep and awake
45  * cycles (e.g. translates to auto away somewhere down the hierarchy).
46  */
47 
48 #include <config.h>
49 
50 #ifdef HAVE_SYS_STAT_H
51 #include <sys/stat.h>
52 #endif
53 #ifdef HAVE_SYS_TYPES_H
54 #include <sys/types.h>
55 #endif
56 
57 #include <gmodule.h>
58 #include <string.h>
59 #include <dbus/dbus.h>
60 #include <dbus/dbus-glib.h>
61 #include <dbus/dbus-glib-lowlevel.h>
62 #include <telepathy-glib/telepathy-glib.h>
63 
64 #ifdef G_OS_WIN32
65 #include <io.h>
66 #endif
67 
68 #include "mcd-master.h"
69 #include "mcd-master-priv.h"
70 #include "mcd-manager.h"
71 #include "mcd-dispatcher.h"
72 #include "mcd-account-manager.h"
73 #include "mcd-account-manager-priv.h"
74 #include "mcd-account-conditions.h"
75 #include "mcd-account-priv.h"
76 #include "plugin-loader.h"
77 
78 #ifdef G_OS_UNIX
79 # ifndef HAVE_UMASK
80 #   error On Unix, MC relies on umask() for account privacy
81 # endif
82 #endif
83 
84 G_DEFINE_TYPE (McdMaster, mcd_master, MCD_TYPE_OPERATION);
85 
86 struct _McdMasterPrivate
87 {
88     McdAccountManager *account_manager;
89     McdDispatcher *dispatcher;
90 
91     /* We create these for our member objects */
92     TpDBusDaemon *dbus_daemon;
93     TpSimpleClientFactory *client_factory;
94 
95     /* Current pending sleep timer */
96     gint shutdown_timeout_id;
97 
98     gboolean is_disposed;
99     gboolean low_memory;
100     gboolean idle;
101 };
102 
103 enum
104 {
105     PROP_0,
106     PROP_PRESENCE_FRAME,
107     PROP_DBUS_CONNECTION,
108     PROP_DBUS_DAEMON,
109     PROP_DISPATCHER,
110     PROP_ACCOUNT_MANAGER,
111 };
112 
113 /* Used to poison 'default_master' when the object it points to is disposed.
114  * The default_master should basically be alive for the duration of the MC run.
115  */
116 #define POISONED_MASTER ((McdMaster *) 0xdeadbeef)
117 static McdMaster *default_master = NULL;
118 
119 static void
_mcd_master_get_property(GObject * obj,guint prop_id,GValue * val,GParamSpec * pspec)120 _mcd_master_get_property (GObject * obj, guint prop_id,
121 			  GValue * val, GParamSpec * pspec)
122 {
123     McdMasterPrivate *priv = MCD_MASTER (obj)->priv;
124 
125     switch (prop_id)
126     {
127     case PROP_DISPATCHER:
128 	g_value_set_object (val, priv->dispatcher);
129 	break;
130     case PROP_DBUS_DAEMON:
131 	g_value_set_object (val, priv->dbus_daemon);
132 	break;
133     case PROP_DBUS_CONNECTION:
134         g_value_set_pointer (val, tp_proxy_get_dbus_connection (
135                              TP_PROXY (priv->dbus_daemon)));
136 	break;
137     case PROP_ACCOUNT_MANAGER:
138 	g_value_set_object (val, priv->account_manager);
139 	break;
140     default:
141 	G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
142 	break;
143     }
144 }
145 
146 static void
_mcd_master_set_property(GObject * obj,guint prop_id,const GValue * val,GParamSpec * pspec)147 _mcd_master_set_property (GObject *obj, guint prop_id,
148 			  const GValue *val, GParamSpec *pspec)
149 {
150     McdMasterPrivate *priv = MCD_MASTER (obj)->priv;
151 
152     switch (prop_id)
153     {
154     case PROP_DBUS_DAEMON:
155 	g_assert (priv->dbus_daemon == NULL);
156 	priv->dbus_daemon = g_value_dup_object (val);
157 	break;
158     default:
159 	G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
160 	break;
161     }
162 }
163 
164 static void
_mcd_master_dispose(GObject * object)165 _mcd_master_dispose (GObject * object)
166 {
167     McdMasterPrivate *priv = MCD_MASTER (object)->priv;
168 
169     if (priv->is_disposed)
170     {
171 	return;
172     }
173     priv->is_disposed = TRUE;
174 
175     tp_clear_object (&priv->account_manager);
176     tp_clear_object (&priv->dbus_daemon);
177     tp_clear_object (&priv->dispatcher);
178     tp_clear_object (&priv->client_factory);
179 
180     if (default_master == (McdMaster *) object)
181     {
182         default_master = POISONED_MASTER;
183     }
184 
185     G_OBJECT_CLASS (mcd_master_parent_class)->dispose (object);
186 }
187 
188 static GObject *
mcd_master_constructor(GType type,guint n_params,GObjectConstructParam * params)189 mcd_master_constructor (GType type, guint n_params,
190 			GObjectConstructParam *params)
191 {
192     GObjectClass *object_class = (GObjectClass *)mcd_master_parent_class;
193     McdMaster *master;
194     McdMasterPrivate *priv;
195 
196     master =  MCD_MASTER (object_class->constructor (type, n_params, params));
197     priv = master->priv;
198 
199     g_return_val_if_fail (master != NULL, NULL);
200 
201 #ifdef HAVE_UMASK
202     /* mask out group and other rwx bits when creating files */
203     umask (0077);
204 #endif
205 
206     priv->client_factory = tp_simple_client_factory_new (priv->dbus_daemon);
207     priv->account_manager = mcd_account_manager_new (priv->client_factory);
208 
209     priv->dispatcher = mcd_dispatcher_new (priv->dbus_daemon, master);
210     g_assert (MCD_IS_DISPATCHER (priv->dispatcher));
211 
212     _mcd_account_manager_setup (priv->account_manager);
213 
214     dbus_connection_set_exit_on_disconnect (
215         dbus_g_connection_get_connection (
216             tp_proxy_get_dbus_connection (TP_PROXY (priv->dbus_daemon))),
217         TRUE);
218 
219     return (GObject *) master;
220 }
221 
222 static void
mcd_master_class_init(McdMasterClass * klass)223 mcd_master_class_init (McdMasterClass * klass)
224 {
225     GObjectClass *object_class = G_OBJECT_CLASS (klass);
226     g_type_class_add_private (object_class, sizeof (McdMasterPrivate));
227 
228     object_class->constructor = mcd_master_constructor;
229     object_class->get_property = _mcd_master_get_property;
230     object_class->set_property = _mcd_master_set_property;
231     object_class->dispose = _mcd_master_dispose;
232 
233     /* Properties */
234     g_object_class_install_property
235         (object_class, PROP_DISPATCHER,
236          g_param_spec_object ("dispatcher",
237                               "Dispatcher",
238                               "Dispatcher",
239                               MCD_TYPE_DISPATCHER,
240                               G_PARAM_READABLE));
241 
242     g_object_class_install_property
243         (object_class, PROP_DBUS_DAEMON,
244          g_param_spec_object ("dbus-daemon", "DBus daemon", "DBus daemon",
245                               TP_TYPE_DBUS_DAEMON,
246                               G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
247 
248     g_object_class_install_property
249         (object_class, PROP_DBUS_CONNECTION,
250          g_param_spec_pointer ("dbus-connection",
251                                "D-Bus Connection",
252                                "D-Bus Connection",
253                                G_PARAM_READABLE));
254 
255     g_object_class_install_property
256         (object_class, PROP_ACCOUNT_MANAGER,
257          g_param_spec_object ("account-manager",
258                               "AccountManager", "AccountManager",
259                               MCD_TYPE_ACCOUNT_MANAGER,
260                               G_PARAM_READABLE));
261 }
262 
263 static void
mcd_master_init(McdMaster * master)264 mcd_master_init (McdMaster * master)
265 {
266     master->priv = G_TYPE_INSTANCE_GET_PRIVATE (master,
267         MCD_TYPE_MASTER, McdMasterPrivate);
268 
269     if (!default_master)
270 	default_master = master;
271 
272     /* This newer plugin API is currently always enabled       */
273     /* .... and is enabled before anything else as potentially *
274      * any mcd component could have a new-API style plugin     */
275     _mcd_plugin_loader_init ();
276 }
277 
278 McdMaster *
mcd_master_get_default(void)279 mcd_master_get_default (void)
280 {
281     if (!default_master)
282 	default_master = MCD_MASTER (g_object_new (MCD_TYPE_MASTER, NULL));
283 
284     g_return_val_if_fail (default_master != POISONED_MASTER, NULL);
285 
286     return default_master;
287 }
288 
289 /*
290  * _mcd_master_lookup_manager:
291  * @master: the #McdMaster.
292  * @unique_name: the name of the manager.
293  *
294  * Gets the manager whose name is @unique_name. If the manager object doesn't
295  * exists yet, it is created.
296  *
297  * Returns: a #McdManager. Caller must call g_object_ref() on it to ensure it
298  * will stay alive as long as needed.
299  */
300 McdManager *
_mcd_master_lookup_manager(McdMaster * master,const gchar * unique_name)301 _mcd_master_lookup_manager (McdMaster *master,
302                             const gchar *unique_name)
303 {
304     const GList *managers, *list;
305     McdManager *manager;
306 
307     managers = mcd_operation_get_missions (MCD_OPERATION (master));
308     for (list = managers; list; list = list->next)
309     {
310 	manager = MCD_MANAGER (list->data);
311 	if (strcmp (unique_name,
312 		    mcd_manager_get_name (manager)) == 0)
313 	    return manager;
314     }
315 
316     manager = mcd_manager_new (unique_name,
317                                master->priv->dispatcher,
318                                master->priv->client_factory);
319     if (G_UNLIKELY (!manager))
320 	g_warning ("Manager %s not created", unique_name);
321     else
322 	mcd_operation_take_mission (MCD_OPERATION (master),
323 				    MCD_MISSION (manager));
324 
325     return manager;
326 }
327 
328 /**
329  * mcd_master_get_dbus_daemon:
330  * @master: the #McdMaster.
331  *
332  * Returns: the #TpDBusDaemon.
333  */
334 TpDBusDaemon *
mcd_master_get_dbus_daemon(McdMaster * master)335 mcd_master_get_dbus_daemon (McdMaster *master)
336 {
337     g_return_val_if_fail (MCD_IS_MASTER (master), NULL);
338     return master->priv->dbus_daemon;
339 }
340 
341 /* Milliseconds to wait for Connectivity coming back up before exiting MC */
342 #define EXIT_COUNTDOWN_TIME 5000
343 
344 static gboolean
_mcd_master_exit_by_timeout(gpointer data)345 _mcd_master_exit_by_timeout (gpointer data)
346 {
347     McdMaster *self = MCD_MASTER (data);
348 
349     self->priv->shutdown_timeout_id = 0;
350 
351     /* Notify sucide */
352     mcd_mission_abort (MCD_MISSION (self));
353     return FALSE;
354 }
355 
356 void
mcd_master_shutdown(McdMaster * self,const gchar * reason)357 mcd_master_shutdown (McdMaster *self,
358                      const gchar *reason)
359 {
360     McdMasterPrivate *priv;
361 
362     g_return_if_fail (MCD_IS_MASTER (self));
363     priv = self->priv;
364 
365     if(!priv->shutdown_timeout_id)
366     {
367         DEBUG ("MC will bail out because of \"%s\" out exit after %i",
368                reason ? reason : "No reason specified",
369                EXIT_COUNTDOWN_TIME);
370 
371         priv->shutdown_timeout_id = g_timeout_add (EXIT_COUNTDOWN_TIME,
372                                                    _mcd_master_exit_by_timeout,
373                                                    self);
374     }
375     else
376     {
377         DEBUG ("Already shutting down. This one has the reason %s",
378                reason ? reason : "No reason specified");
379     }
380     mcd_debug_print_tree (self);
381 }
382