rework deprecated g_type_class_add_private()
[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         G_ADD_PRIVATE(TlmDbusServerP2P));
88
89 static void
90 _set_property (
91         GObject *object,
92         guint property_id,
93         const GValue *value,
94         GParamSpec *pspec)
95 {
96     TlmDbusServerP2P *self = TLM_DBUS_SERVER_P2P (object);
97     switch (property_id) {
98         case IFACE_PROP_TYPE: {
99             break;
100         }
101         case PROP_ADDRESS: {
102             self->priv->address = g_value_dup_string (value);
103             break;
104         }
105         default:
106             G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
107     }
108 }
109
110 static void
111 _get_property (
112         GObject *object,
113         guint property_id,
114         GValue *value,
115         GParamSpec *pspec)
116 {
117     TlmDbusServerP2P *self = TLM_DBUS_SERVER_P2P (object);
118
119     switch (property_id) {
120         case IFACE_PROP_TYPE: {
121             g_value_set_uint (value, (guint)TLM_DBUS_SERVER_BUSTYPE_P2P);
122             break;
123         }
124         case PROP_ADDRESS: {
125             g_value_set_string (value, g_dbus_server_get_client_address (
126                     self->priv->bus_server));
127             break;
128         }
129         default:
130             G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
131     }
132 }
133
134 static gboolean
135 _compare_by_pointer (
136         gpointer key,
137         gpointer value,
138         gpointer dead_object)
139 {
140     return value == dead_object;
141 }
142
143 static void
144 _on_login_object_dispose (
145         gpointer data,
146         GObject *dead)
147 {
148     TlmDbusServerP2P *server = TLM_DBUS_SERVER_P2P (data);
149     g_return_if_fail (server);
150     g_hash_table_foreach_steal (server->priv->login_object_adapters,
151                 _compare_by_pointer, dead);
152 }
153
154 static void
155 _clear_login_object_watchers (
156         gpointer connection,
157         gpointer login_object,
158         gpointer user_data)
159 {
160     g_signal_handlers_disconnect_by_func (connection, _on_connection_closed,
161             user_data);
162     g_object_weak_unref (G_OBJECT(login_object), _on_login_object_dispose,
163             user_data);
164 }
165
166 static void
167 _add_login_object_watchers (
168         gpointer connection,
169         gpointer login_object,
170         TlmDbusServerP2P *server)
171 {
172     g_signal_connect (connection, "closed", G_CALLBACK(_on_connection_closed),
173             server);
174     g_object_weak_ref (G_OBJECT (login_object), _on_login_object_dispose,
175             server);
176     g_hash_table_insert (server->priv->login_object_adapters, connection,
177             login_object);
178 }
179
180 static void
181 _dispose (
182         GObject *object)
183 {
184     TlmDbusServerP2P *self = TLM_DBUS_SERVER_P2P (object);
185
186     _tlm_dbus_server_p2p_stop (TLM_DBUS_SERVER (self));
187
188     G_OBJECT_CLASS (tlm_dbus_server_p2p_parent_class)->dispose (object);
189 }
190
191 static void
192 _finalize (
193         GObject *object)
194 {
195     TlmDbusServerP2P *self = TLM_DBUS_SERVER_P2P (object);
196     if (self->priv->address) {
197         if (g_str_has_prefix (self->priv->address, "unix:path=")) {
198             const gchar *path = g_strstr_len(self->priv->address, -1,
199                     "unix:path=") + 10;
200             if (path) {
201                 g_unlink (path);
202             }
203         }
204         g_free (self->priv->address);
205         self->priv->address = NULL;
206     }
207     G_OBJECT_CLASS (tlm_dbus_server_p2p_parent_class)->finalize (object);
208 }
209
210 static void
211 tlm_dbus_server_p2p_class_init (
212         TlmDbusServerP2PClass *klass)
213 {
214     GObjectClass* object_class = G_OBJECT_CLASS (klass);
215     GParamSpec *address_spec = NULL;
216
217     object_class->get_property = _get_property;
218     object_class->set_property = _set_property;
219     object_class->dispose = _dispose;
220     object_class->finalize = _finalize;
221
222     g_object_class_override_property (object_class, IFACE_PROP_TYPE, "bustype");
223
224     address_spec = g_param_spec_string ("address", "server address",
225             "Server socket address",  NULL,
226             G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
227             G_PARAM_STATIC_STRINGS);
228     g_object_class_install_property (object_class, PROP_ADDRESS, address_spec);
229
230     signals[SIG_CLIENT_ADDED] = g_signal_new ("client-added",
231             TLM_TYPE_DBUS_SERVER_P2P,
232             G_SIGNAL_RUN_LAST,
233             0,
234             NULL,
235             NULL,
236             NULL,
237             G_TYPE_NONE,
238             1,
239             TLM_TYPE_LOGIN_ADAPTER);
240
241     signals[SIG_CLIENT_REMOVED] = g_signal_new ("client-removed",
242             TLM_TYPE_DBUS_SERVER_P2P,
243             G_SIGNAL_RUN_LAST,
244             0,
245             NULL,
246             NULL,
247             NULL,
248             G_TYPE_NONE,
249             1,
250             TLM_TYPE_LOGIN_ADAPTER);
251 }
252
253 static void
254 tlm_dbus_server_p2p_init (
255         TlmDbusServerP2P *self)
256 {
257     self->priv = tlm_dbus_server_p2p_get_instance_private (self);
258     self->priv->bus_server = NULL;
259     self->priv->address = NULL;
260     self->priv->uid = 0;
261     self->priv->login_object_adapters = g_hash_table_new_full (g_direct_hash,
262             g_direct_equal, NULL, g_object_unref);
263 }
264
265 static void
266 _on_connection_closed (
267         GDBusConnection *connection,
268         gboolean remote_peer_vanished,
269         GError *error,
270         gpointer user_data)
271 {
272     TlmDbusServerP2P *server = TLM_DBUS_SERVER_P2P (user_data);
273     gpointer login_object = g_hash_table_lookup (
274             server->priv->login_object_adapters, connection);
275     if  (login_object) {
276         _clear_login_object_watchers (connection, login_object, user_data);
277         DBG("p2p dbus connection(%p) closed (peer vanished : %d)"
278                 " with error: %s", connection, remote_peer_vanished,
279                 error ? error->message : "NONE");
280
281         g_signal_emit (server, signals[SIG_CLIENT_REMOVED], 0, login_object);
282         g_hash_table_remove (server->priv->login_object_adapters, connection);
283     }
284 }
285
286 static void
287 _tlm_dbus_server_p2p_add_login_obj (
288         TlmDbusServerP2P *server,
289         GDBusConnection *connection)
290 {
291     TlmDbusLoginAdapter *login_object = NULL;
292
293     DBG("export interfaces on connection %p", connection);
294
295     login_object = tlm_dbus_login_adapter_new_with_connection (connection);
296
297     _add_login_object_watchers (connection, login_object, server);
298     g_signal_emit (server, signals[SIG_CLIENT_ADDED], 0, login_object);
299 }
300
301 static gboolean
302 _on_client_request (
303         GDBusServer *dbus_server,
304         GDBusConnection *connection,
305         gpointer user_data)
306 {
307     TlmDbusServerP2P *server = TLM_DBUS_SERVER_P2P (user_data);
308     if (!server) {
309         WARN ("memory corruption");
310         return TRUE;
311     }
312     _tlm_dbus_server_p2p_add_login_obj (server, connection);
313     return TRUE;
314 }
315
316 gboolean
317 _tlm_dbus_server_p2p_start (
318         TlmDbusServer *self)
319 {
320     g_return_val_if_fail (TLM_IS_DBUS_SERVER_P2P (self), FALSE);
321
322     DBG("start P2P DBus Server");
323
324     TlmDbusServerP2P *server = TLM_DBUS_SERVER_P2P (self);
325     if (!server->priv->bus_server) {
326         GError *err = NULL;
327         gchar *guid = g_dbus_generate_guid ();
328         server->priv->bus_server = g_dbus_server_new_sync (
329                 server->priv->address, G_DBUS_SERVER_FLAGS_NONE, guid, NULL,
330                 NULL, &err);
331         g_free (guid);
332
333         if (!server->priv->bus_server) {
334             WARN ("Failed to start server at address '%s':%s",
335                     server->priv->address, err ? err->message : "NULL");
336             g_error_free (err);
337             return FALSE;
338         }
339
340         g_signal_connect (server->priv->bus_server, "new-connection",
341                 G_CALLBACK(_on_client_request), server);
342     }
343
344     if (!g_dbus_server_is_active (server->priv->bus_server)) {
345         const gchar *path = NULL;
346         g_dbus_server_start (server->priv->bus_server);
347         path = g_strstr_len(server->priv->address, -1, "unix:path=") + 10;
348         if (path) {
349             if (chown (path, server->priv->uid, -1) < 0) {
350                 WARN("Unable to set ownership");
351             }
352             if (g_chmod (path, S_IRUSR | S_IWUSR) < 0) {
353                 WARN("Unable to set mode '%d' for '%s'",
354                     S_IRUSR|S_IWUSR, path);
355             }
356         }
357     }
358     DBG("dbus server started at : %s", server->priv->address);
359
360     return TRUE;
361 }
362
363 gboolean
364 _tlm_dbus_server_p2p_stop (
365         TlmDbusServer *self)
366 {
367     g_return_val_if_fail (TLM_IS_DBUS_SERVER_P2P (self), FALSE);
368     DBG ("self %p", self);
369
370     TlmDbusServerP2P *server = TLM_DBUS_SERVER_P2P (self);
371
372     if (server->priv->login_object_adapters) {
373         DBG("cleanup watchers");
374         g_hash_table_foreach (server->priv->login_object_adapters,
375                 _clear_login_object_watchers, server);
376         g_hash_table_unref (server->priv->login_object_adapters);
377         server->priv->login_object_adapters = NULL;
378     }
379
380     if (server->priv->bus_server) {
381         DBG("stop P2P DBus Server");
382         g_signal_handlers_disconnect_by_func (server->priv->bus_server,
383                 _on_client_request, server);
384         if (g_dbus_server_is_active (server->priv->bus_server))
385             g_dbus_server_stop (server->priv->bus_server);
386         g_object_unref (server->priv->bus_server);
387         server->priv->bus_server = NULL;
388     }
389
390     return TRUE;
391 }
392
393 static pid_t
394 _tlm_dbus_server_p2p_get_remote_pid (
395         TlmDbusServer *self,
396         GDBusMethodInvocation *invocation)
397 {
398     pid_t remote_pid = 0;
399     GDBusConnection *connection = NULL;
400     gint peer_fd = -1;
401     struct ucred peer_cred;
402     socklen_t cred_size = sizeof(peer_cred);
403
404     g_return_val_if_fail (invocation && TLM_IS_DBUS_SERVER_P2P (self),
405             remote_pid);
406
407     connection = g_dbus_method_invocation_get_connection (invocation);
408     peer_fd = g_socket_get_fd (g_socket_connection_get_socket (
409             G_SOCKET_CONNECTION (g_dbus_connection_get_stream(connection))));
410     if (peer_fd < 0 || getsockopt (peer_fd, SOL_SOCKET, SO_PEERCRED,
411             &peer_cred, &cred_size) != 0) {
412         WARN ("getsockopt() for SO_PEERCRED failed");
413         return remote_pid;
414     }
415     DBG ("remote p2p peer pid=%d uid=%d gid=%d", peer_cred.pid, peer_cred.uid,
416             peer_cred.gid);
417
418     return peer_cred.pid;
419 }
420
421 static void
422 _tlm_dbus_server_p2p_interface_init (
423         TlmDbusServerInterface *iface)
424 {
425     iface->start = _tlm_dbus_server_p2p_start;
426     iface->stop = _tlm_dbus_server_p2p_stop;
427     iface->get_remote_pid = _tlm_dbus_server_p2p_get_remote_pid;
428 }
429
430 TlmDbusServerP2P *
431 tlm_dbus_server_p2p_new (
432         const gchar *address,
433         uid_t uid)
434 {
435     TlmDbusServerP2P *server = TLM_DBUS_SERVER_P2P (
436         g_object_new (TLM_TYPE_DBUS_SERVER_P2P, "address", address, NULL));
437
438     DBG("create P2P DBus Server: %p", server);
439     if (!server) {
440         return NULL;
441     }
442     server->priv->uid = uid;
443
444     if (g_str_has_prefix(address, "unix:path=")) {
445         const gchar *file_path = g_strstr_len (address, -1, "unix:path=") + 10;
446
447         if (g_file_test(file_path, G_FILE_TEST_EXISTS)) {
448             g_unlink (file_path);
449         }
450         gchar *base_path = g_path_get_dirname (file_path);
451
452         if (g_mkdir_with_parents (base_path,
453                 S_IRUSR |S_IWUSR |S_IXUSR |S_IXGRP |S_IXOTH) == -1) {
454             gchar strerr_buf[MAX_STRERROR_LEN] = {0,};
455             WARN ("Could not create '%s', error: %s", base_path,
456                     strerror_r(errno, strerr_buf, MAX_STRERROR_LEN));
457             g_object_unref (server);
458             server = NULL;
459         }
460         g_free (base_path);
461     }
462
463     return server;
464 }