5cfa3d8abc7d79d0079b1eb8beab28e4c9127c5e
[platform/core/system/tlm.git] / src / daemon / dbus / tlm-dbus-server-p2p.c
1 /* vi: set et sw=4 ts=4 cino=t0,(0: */
2 /* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 /*
4  * This file is part of tlm
5  *
6  * Copyright (C) 2014 Intel Corporation.
7  *
8  * Contact: Imran Zaman <imran.zaman@intel.com>
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public
12  * License as published by the Free Software Foundation; either
13  * version 2.1 of the License, or (at your option) any later version.
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 #include <errno.h>
26 #include <string.h>
27 #include <glib/gstdio.h>
28 #include <unistd.h>
29 #include <sys/types.h>
30 #include <sys/socket.h>
31 #include <sys/stat.h>
32 #include <fcntl.h>
33
34 #include "common/dbus/tlm-dbus.h"
35 #include "common/tlm-log.h"
36
37 #include "tlm-dbus-server-p2p.h"
38 #include "tlm-dbus-server-interface.h"
39 #include "tlm-dbus-login-adapter.h"
40
41 enum
42 {
43     PROP_0,
44
45     IFACE_PROP_TYPE,
46     PROP_ADDRESS,
47
48     N_PROPERTIES
49 };
50
51 enum {
52     SIG_CLIENT_ADDED,
53     SIG_CLIENT_REMOVED,
54
55     SIG_MAX
56 };
57
58 static guint signals[SIG_MAX];
59
60 struct _TlmDbusServerP2PPrivate
61 {
62     GHashTable *login_object_adapters;
63     GDBusServer *bus_server;
64     gchar *address;
65     uid_t uid;
66 };
67
68 static void
69 _tlm_dbus_server_p2p_interface_init (
70         TlmDbusServerInterface *iface);
71
72 gboolean
73 _tlm_dbus_server_p2p_stop (
74         TlmDbusServer *self);
75
76 static void
77 _on_connection_closed (
78         GDBusConnection *connection,
79         gboolean remote_peer_vanished,
80         GError *error,
81         gpointer user_data);
82
83 G_DEFINE_TYPE_WITH_CODE (TlmDbusServerP2P,
84         tlm_dbus_server_p2p, G_TYPE_OBJECT,
85         G_IMPLEMENT_INTERFACE (TLM_TYPE_DBUS_SERVER,
86                 _tlm_dbus_server_p2p_interface_init));
87
88 #define TLM_DBUS_SERVER_P2P_GET_PRIV(obj) G_TYPE_INSTANCE_GET_PRIVATE ((obj),\
89         TLM_TYPE_DBUS_SERVER_P2P, TlmDbusServerP2PPrivate)
90
91 static void
92 _set_property (
93         GObject *object,
94         guint property_id,
95         const GValue *value,
96         GParamSpec *pspec)
97 {
98     TlmDbusServerP2P *self = TLM_DBUS_SERVER_P2P (object);
99     switch (property_id) {
100         case IFACE_PROP_TYPE: {
101             break;
102         }
103         case PROP_ADDRESS: {
104             self->priv->address = g_value_dup_string (value);
105             break;
106         }
107         default:
108             G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
109     }
110 }
111
112 static void
113 _get_property (
114         GObject *object,
115         guint property_id,
116         GValue *value,
117         GParamSpec *pspec)
118 {
119     TlmDbusServerP2P *self = TLM_DBUS_SERVER_P2P (object);
120
121     switch (property_id) {
122         case IFACE_PROP_TYPE: {
123             g_value_set_uint (value, (guint)TLM_DBUS_SERVER_BUSTYPE_P2P);
124             break;
125         }
126         case PROP_ADDRESS: {
127             g_value_set_string (value, g_dbus_server_get_client_address (
128                     self->priv->bus_server));
129             break;
130         }
131         default:
132             G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
133     }
134 }
135
136 static gboolean
137 _compare_by_pointer (
138         gpointer key,
139         gpointer value,
140         gpointer dead_object)
141 {
142     return value == dead_object;
143 }
144
145 static void
146 _on_login_object_dispose (
147         gpointer data,
148         GObject *dead)
149 {
150     TlmDbusServerP2P *server = TLM_DBUS_SERVER_P2P (data);
151     g_return_if_fail (server);
152     g_hash_table_foreach_steal (server->priv->login_object_adapters,
153                 _compare_by_pointer, dead);
154 }
155
156 static void
157 _clear_login_object_watchers (
158         gpointer connection,
159         gpointer login_object,
160         gpointer user_data)
161 {
162     g_signal_handlers_disconnect_by_func (connection, _on_connection_closed,
163             user_data);
164     g_object_weak_unref (G_OBJECT(login_object), _on_login_object_dispose,
165             user_data);
166 }
167
168 static void
169 _add_login_object_watchers (
170         gpointer connection,
171         gpointer login_object,
172         TlmDbusServerP2P *server)
173 {
174     g_signal_connect (connection, "closed", G_CALLBACK(_on_connection_closed),
175             server);
176     g_object_weak_ref (G_OBJECT (login_object), _on_login_object_dispose,
177             server);
178     g_hash_table_insert (server->priv->login_object_adapters, connection,
179             login_object);
180 }
181
182 static void
183 _dispose (
184         GObject *object)
185 {
186     TlmDbusServerP2P *self = TLM_DBUS_SERVER_P2P (object);
187
188     _tlm_dbus_server_p2p_stop (TLM_DBUS_SERVER (self));
189
190     G_OBJECT_CLASS (tlm_dbus_server_p2p_parent_class)->dispose (object);
191 }
192
193 static void
194 _finalize (
195         GObject *object)
196 {
197     TlmDbusServerP2P *self = TLM_DBUS_SERVER_P2P (object);
198     if (self->priv->address) {
199         if (g_str_has_prefix (self->priv->address, "unix:path=")) {
200             const gchar *path = g_strstr_len(self->priv->address, -1,
201                     "unix:path=") + 10;
202             if (path) {
203                 g_unlink (path);
204             }
205         }
206         g_free (self->priv->address);
207         self->priv->address = NULL;
208     }
209     G_OBJECT_CLASS (tlm_dbus_server_p2p_parent_class)->finalize (object);
210 }
211
212 static void
213 tlm_dbus_server_p2p_class_init (
214         TlmDbusServerP2PClass *klass)
215 {
216     GObjectClass* object_class = G_OBJECT_CLASS (klass);
217     GParamSpec *address_spec = NULL;
218
219     g_type_class_add_private (object_class, sizeof (TlmDbusServerP2PPrivate));
220
221     object_class->get_property = _get_property;
222     object_class->set_property = _set_property;
223     object_class->dispose = _dispose;
224     object_class->finalize = _finalize;
225
226     g_object_class_override_property (object_class, IFACE_PROP_TYPE, "bustype");
227
228     address_spec = g_param_spec_string ("address", "server address",
229             "Server socket address",  NULL,
230             G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
231             G_PARAM_STATIC_STRINGS);
232     g_object_class_install_property (object_class, PROP_ADDRESS, address_spec);
233
234     signals[SIG_CLIENT_ADDED] = g_signal_new ("client-added",
235             TLM_TYPE_DBUS_SERVER_P2P,
236             G_SIGNAL_RUN_LAST,
237             0,
238             NULL,
239             NULL,
240             NULL,
241             G_TYPE_NONE,
242             1,
243             TLM_TYPE_LOGIN_ADAPTER);
244
245     signals[SIG_CLIENT_REMOVED] = g_signal_new ("client-removed",
246             TLM_TYPE_DBUS_SERVER_P2P,
247             G_SIGNAL_RUN_LAST,
248             0,
249             NULL,
250             NULL,
251             NULL,
252             G_TYPE_NONE,
253             1,
254             TLM_TYPE_LOGIN_ADAPTER);
255 }
256
257 static void
258 tlm_dbus_server_p2p_init (
259         TlmDbusServerP2P *self)
260 {
261     self->priv = TLM_DBUS_SERVER_P2P_GET_PRIV(self);
262     self->priv->bus_server = NULL;
263     self->priv->address = NULL;
264     self->priv->uid = 0;
265     self->priv->login_object_adapters = g_hash_table_new_full (g_direct_hash,
266             g_direct_equal, NULL, g_object_unref);
267 }
268
269 static void
270 _on_connection_closed (
271         GDBusConnection *connection,
272         gboolean remote_peer_vanished,
273         GError *error,
274         gpointer user_data)
275 {
276     TlmDbusServerP2P *server = TLM_DBUS_SERVER_P2P (user_data);
277     gpointer login_object = g_hash_table_lookup (
278             server->priv->login_object_adapters, connection);
279     if  (login_object) {
280         _clear_login_object_watchers (connection, login_object, user_data);
281         DBG("p2p dbus connection(%p) closed (peer vanished : %d)"
282                 " with error: %s", connection, remote_peer_vanished,
283                 error ? error->message : "NONE");
284
285         g_signal_emit (server, signals[SIG_CLIENT_REMOVED], 0, login_object);
286         g_hash_table_remove (server->priv->login_object_adapters, connection);
287     }
288 }
289
290 static void
291 _tlm_dbus_server_p2p_add_login_obj (
292         TlmDbusServerP2P *server,
293         GDBusConnection *connection)
294 {
295     TlmDbusLoginAdapter *login_object = NULL;
296
297     DBG("export interfaces on connection %p", connection);
298
299     login_object = tlm_dbus_login_adapter_new_with_connection (connection);
300
301     _add_login_object_watchers (connection, login_object, server);
302     g_signal_emit (server, signals[SIG_CLIENT_ADDED], 0, login_object);
303 }
304
305 static gboolean
306 _on_client_request (
307         GDBusServer *dbus_server,
308         GDBusConnection *connection,
309         gpointer user_data)
310 {
311     TlmDbusServerP2P *server = TLM_DBUS_SERVER_P2P (user_data);
312     if (!server) {
313         WARN ("memory corruption");
314         return TRUE;
315     }
316     _tlm_dbus_server_p2p_add_login_obj (server, connection);
317     return TRUE;
318 }
319
320 gboolean
321 _tlm_dbus_server_p2p_start (
322         TlmDbusServer *self)
323 {
324     g_return_val_if_fail (TLM_IS_DBUS_SERVER_P2P (self), FALSE);
325
326     DBG("start P2P DBus Server");
327
328     TlmDbusServerP2P *server = TLM_DBUS_SERVER_P2P (self);
329     if (!server->priv->bus_server) {
330         GError *err = NULL;
331         gchar *guid = g_dbus_generate_guid ();
332         server->priv->bus_server = g_dbus_server_new_sync (
333                 server->priv->address, G_DBUS_SERVER_FLAGS_NONE, guid, NULL,
334                 NULL, &err);
335         g_free (guid);
336
337         if (!server->priv->bus_server) {
338             WARN ("Failed to start server at address '%s':%s",
339                     server->priv->address, err ? err->message : "NULL");
340             g_error_free (err);
341             return FALSE;
342         }
343
344         g_signal_connect (server->priv->bus_server, "new-connection",
345                 G_CALLBACK(_on_client_request), server);
346     }
347
348     if (!g_dbus_server_is_active (server->priv->bus_server)) {
349         const gchar *path = NULL;
350         g_dbus_server_start (server->priv->bus_server);
351         path = g_strstr_len(server->priv->address, -1, "unix:path=") + 10;
352         if (path) {
353             if (chown (path, server->priv->uid, -1) < 0) {
354                 WARN("Unable to set ownership");
355             }
356             if (g_chmod (path, S_IRUSR | S_IWUSR) < 0) {
357                 WARN("Unable to set mode '%d' for '%s'",
358                     S_IRUSR|S_IWUSR, path);
359             }
360         }
361     }
362     DBG("dbus server started at : %s", server->priv->address);
363
364     return TRUE;
365 }
366
367 gboolean
368 _tlm_dbus_server_p2p_stop (
369         TlmDbusServer *self)
370 {
371     g_return_val_if_fail (TLM_IS_DBUS_SERVER_P2P (self), FALSE);
372     DBG ("self %p", self);
373
374     TlmDbusServerP2P *server = TLM_DBUS_SERVER_P2P (self);
375
376     if (server->priv->login_object_adapters) {
377         DBG("cleanup watchers");
378         g_hash_table_foreach (server->priv->login_object_adapters,
379                 _clear_login_object_watchers, server);
380         g_hash_table_unref (server->priv->login_object_adapters);
381         server->priv->login_object_adapters = NULL;
382     }
383
384     if (server->priv->bus_server) {
385         DBG("stop P2P DBus Server");
386         g_signal_handlers_disconnect_by_func (server->priv->bus_server,
387                 _on_client_request, server);
388         if (g_dbus_server_is_active (server->priv->bus_server))
389             g_dbus_server_stop (server->priv->bus_server);
390         g_object_unref (server->priv->bus_server);
391         server->priv->bus_server = NULL;
392     }
393
394     return TRUE;
395 }
396
397 static pid_t
398 _tlm_dbus_server_p2p_get_remote_pid (
399         TlmDbusServer *self,
400         GDBusMethodInvocation *invocation)
401 {
402     pid_t remote_pid = 0;
403     GDBusConnection *connection = NULL;
404     gint peer_fd = -1;
405     struct ucred peer_cred;
406     socklen_t cred_size = sizeof(peer_cred);
407
408     g_return_val_if_fail (invocation && TLM_IS_DBUS_SERVER_P2P (self),
409             remote_pid);
410
411     connection = g_dbus_method_invocation_get_connection (invocation);
412     peer_fd = g_socket_get_fd (g_socket_connection_get_socket (
413             G_SOCKET_CONNECTION (g_dbus_connection_get_stream(connection))));
414     if (peer_fd < 0 || getsockopt (peer_fd, SOL_SOCKET, SO_PEERCRED,
415             &peer_cred, &cred_size) != 0) {
416         WARN ("getsockopt() for SO_PEERCRED failed");
417         return remote_pid;
418     }
419     DBG ("remote p2p peer pid=%d uid=%d gid=%d", peer_cred.pid, peer_cred.uid,
420             peer_cred.gid);
421
422     return peer_cred.pid;
423 }
424
425 static void
426 _tlm_dbus_server_p2p_interface_init (
427         TlmDbusServerInterface *iface)
428 {
429     iface->start = _tlm_dbus_server_p2p_start;
430     iface->stop = _tlm_dbus_server_p2p_stop;
431     iface->get_remote_pid = _tlm_dbus_server_p2p_get_remote_pid;
432 }
433
434 TlmDbusServerP2P *
435 tlm_dbus_server_p2p_new (
436         const gchar *address,
437         uid_t uid)
438 {
439     TlmDbusServerP2P *server = TLM_DBUS_SERVER_P2P (
440         g_object_new (TLM_TYPE_DBUS_SERVER_P2P, "address", address, NULL));
441
442     DBG("create P2P DBus Server: %p", server);
443     if (!server) {
444         return NULL;
445     }
446     server->priv->uid = uid;
447
448     if (g_str_has_prefix(address, "unix:path=")) {
449         const gchar *file_path = g_strstr_len (address, -1, "unix:path=") + 10;
450
451         if (g_file_test(file_path, G_FILE_TEST_EXISTS)) {
452             g_unlink (file_path);
453         }
454         gchar *base_path = g_path_get_dirname (file_path);
455
456         if (g_mkdir_with_parents (base_path,
457                 S_IRUSR |S_IWUSR |S_IXUSR |S_IXGRP |S_IXOTH) == -1) {
458             gchar strerr_buf[MAX_STRERROR_LEN] = {0,};
459             WARN ("Could not create '%s', error: %s", base_path,
460                     strerror_r(errno, strerr_buf, MAX_STRERROR_LEN));
461             g_object_unref (server);
462             server = NULL;
463         }
464         g_free (base_path);
465     }
466
467     return server;
468 }