Migrate from 2.4 code repo
[platform/core/context/context-service.git] / src / dbus_server_impl.cpp
1 /*
2  * Copyright (c) 2014 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include <signal.h>
18 #include <glib.h>
19 #include <gio/gio.h>
20
21 #include <types_internal.h>
22 #include <dbus_listener_iface.h>
23 #include "server.h"
24 #include "client_request.h"
25 #include "dbus_server_impl.h"
26
27 static ctx::dbus_server_impl *_instance = NULL;
28 static GDBusConnection *dbus_connection = NULL;
29 static guint dbus_owner_id = 0;
30 static GDBusNodeInfo *dbus_node_info = NULL;
31
32 static const gchar introspection_xml[] =
33         "<node>"
34         "       <interface name='" DBUS_IFACE "'>"
35         "               <method name='" METHOD_REQUEST "'>"
36         "                       <arg type='i' name='" ARG_REQTYPE "' direction='in'/>"
37         "                       <arg type='s' name='" ARG_COOKIE "' direction='in'/>"
38         "                       <arg type='i' name='" ARG_REQID "' direction='in'/>"
39         "                       <arg type='s' name='" ARG_SUBJECT "' direction='in'/>"
40         "                       <arg type='s' name='" ARG_INPUT "' direction='in'/>"
41         "                       <arg type='i' name='" ARG_RESULT_ERR "' direction='out'/>"
42         "                       <arg type='s' name='" ARG_RESULT_ADD "' direction='out'/>"
43         "                       <arg type='s' name='" ARG_OUTPUT "' direction='out'/>"
44         "               </method>"
45         "       </interface>"
46         "</node>";
47
48 static const char* req_type_to_str(int req_type)
49 {
50         switch (req_type) {
51                 case REQ_SUBSCRIBE:
52                         return "Subscribe";
53                 case REQ_UNSUBSCRIBE:
54                         return "Unsubscribe";
55                 case REQ_READ:
56                         return "Read";
57                 case REQ_READ_SYNC:
58                         return "Read (Sync)";
59                 case REQ_WRITE:
60                         return "Write";
61                 default:
62                         return NULL;
63         }
64 }
65
66 static void handle_request(const char *sender, GVariant *param, GDBusMethodInvocation *invocation)
67 {
68         gint req_type = 0;
69         const gchar *cookie = NULL;
70         gint req_id = 0;
71         const gchar *subject = NULL;
72         const gchar *input = NULL;
73
74         g_variant_get(param, "(i&si&s&s)", &req_type, &cookie, &req_id, &subject, &input);
75         IF_FAIL_VOID_TAG(req_type > 0 && req_id > 0 && cookie && subject && input, _E, "Invalid request");
76
77         _SD("Cookie: %s", cookie);
78         _I("[%s] ReqId: %d, Subject: %s", req_type_to_str(req_type), req_id, subject);
79         _SI("Input: %s", input);
80
81         //TODO: Parameter validation
82
83         ctx::client_request *request = NULL;
84         try {
85                 request = new ctx::client_request(req_type, sender, req_id, subject, input, cookie, invocation);
86         } catch (std::bad_alloc& ba) {
87                 _E("Memory allocation failed");
88                 g_dbus_method_invocation_return_value(invocation, g_variant_new("(iss)", ERR_OPERATION_FAILED, EMPTY_JSON_OBJECT, EMPTY_JSON_OBJECT));
89                 return;
90         } catch (int e) {
91                 _E("Caught %d", e);
92                 g_dbus_method_invocation_return_value(invocation, g_variant_new("(iss)", ERR_OPERATION_FAILED, EMPTY_JSON_OBJECT, EMPTY_JSON_OBJECT));
93                 return;
94         }
95
96         ctx::server::send_request(request);
97 }
98
99 static void handle_method_call(GDBusConnection *conn, const gchar *sender,
100                 const gchar *obj_path, const gchar *iface, const gchar *method_name,
101                 GVariant *param, GDBusMethodInvocation *invocation, gpointer user_data)
102 {
103         IF_FAIL_VOID_TAG(STR_EQ(obj_path, DBUS_PATH), _W, "Invalid path: %s", obj_path);
104         IF_FAIL_VOID_TAG(STR_EQ(iface, DBUS_IFACE), _W, "Invalid interface: %s", obj_path);
105
106         if (STR_EQ(method_name, METHOD_REQUEST)) {
107                 handle_request(sender, param, invocation);
108         } else {
109                 _W("Invalid method: %s", method_name);
110         }
111 }
112
113 static void on_bus_acquired(GDBusConnection *conn, const gchar *name, gpointer user_data)
114 {
115         GDBusInterfaceVTable vtable;
116         vtable.method_call = handle_method_call;
117         vtable.get_property = NULL;
118         vtable.set_property = NULL;
119
120         guint reg_id = g_dbus_connection_register_object(conn, DBUS_PATH,
121                         dbus_node_info->interfaces[0], &vtable, NULL, NULL, NULL);
122
123         if (reg_id <= 0) {
124                 _E("Failed to acquire dbus");
125                 raise(SIGTERM);
126         }
127
128         dbus_connection = conn;
129 }
130
131 static void on_name_acquired(GDBusConnection *conn, const gchar *name, gpointer user_data)
132 {
133         _SI("Dbus name acquired: %s", name);
134 }
135
136 static void on_name_lost(GDBusConnection *conn, const gchar *name, gpointer user_data)
137 {
138         _E("Dbus name lost");
139         raise(SIGTERM);
140 }
141
142 ctx::dbus_server_impl::dbus_server_impl()
143 {
144 }
145
146 ctx::dbus_server_impl::~dbus_server_impl()
147 {
148         release();
149 }
150
151 bool ctx::dbus_server_impl::init()
152 {
153         IF_FAIL_RETURN_TAG(dbus_node_info == NULL, false, _E, "Re-initialization");
154
155         dbus_node_info = g_dbus_node_info_new_for_xml(introspection_xml, NULL);
156         IF_FAIL_RETURN_TAG(dbus_node_info != NULL, false, _E, "Initialization failed");
157
158         dbus_owner_id = g_bus_own_name(G_BUS_TYPE_SYSTEM, DBUS_DEST, G_BUS_NAME_OWNER_FLAGS_NONE,
159                         on_bus_acquired, on_name_acquired, on_name_lost, NULL, NULL);
160
161         _instance = this;
162         return true;
163 }
164
165 void ctx::dbus_server_impl::release()
166 {
167         if (dbus_connection) {
168                 g_dbus_connection_flush_sync(dbus_connection, NULL, NULL);
169         }
170
171         if (dbus_owner_id > 0) {
172                 g_bus_unown_name(dbus_owner_id);
173                 dbus_owner_id = 0;
174         }
175
176         if (dbus_connection) {
177                 g_dbus_connection_close_sync(dbus_connection, NULL, NULL);
178                 g_object_unref(dbus_connection);
179                 dbus_connection = NULL;
180         }
181
182         if (dbus_node_info) {
183                 g_dbus_node_info_unref(dbus_node_info);
184                 dbus_node_info = NULL;
185         }
186 }
187
188 void ctx::dbus_server_impl::publish(const char* dest, int req_id, const char* subject, int error, const char* data)
189 {
190         IF_FAIL_VOID_TAG(dest && subject && data, _E, "Parameter null");
191
192         _SI("Publish: %s, %d, %s, %#x, %s", dest, req_id, subject, error, data);
193
194         GVariant *param = g_variant_new("(isis)", req_id, subject, error, data);
195         IF_FAIL_VOID_TAG(param, _E, "Memory allocation failed");
196
197         GError *err = NULL;
198         g_dbus_connection_call(dbus_connection, dest, DBUS_PATH, DBUS_IFACE,
199                         METHOD_RESPOND, param, NULL, G_DBUS_CALL_FLAGS_NONE, DBUS_TIMEOUT, NULL, NULL, &err);
200         HANDLE_GERROR(err);
201 }
202
203 static void handle_signal_received(GDBusConnection *conn, const gchar *sender,
204                 const gchar *obj_path, const gchar *iface, const gchar *signal_name,
205                 GVariant *param, gpointer user_data)
206 {
207         IF_FAIL_VOID_TAG(user_data, _W, "user_data cannot be null");
208         ctx::dbus_listener_iface *listener = static_cast<ctx::dbus_listener_iface*>(user_data);
209         listener->on_signal_received(sender, obj_path, iface, signal_name, param);
210 }
211
212 int64_t ctx::dbus_server_impl::signal_subscribe(const char* sender, const char* path, const char* iface, const char* name, ctx::dbus_listener_iface* listener)
213 {
214         IF_FAIL_RETURN_TAG(dbus_connection, -1, _E, "Dbus not connected");
215         guint sid = g_dbus_connection_signal_subscribe(dbus_connection,
216                         sender, iface, name, path, NULL, G_DBUS_SIGNAL_FLAGS_NONE,
217                         handle_signal_received, listener, NULL);
218         return static_cast<int64_t>(sid);
219 }
220
221 void ctx::dbus_server_impl::signal_unsubscribe(int64_t subscription_id)
222 {
223         IF_FAIL_VOID_TAG(dbus_connection, _E, "Dbus not connected");
224         IF_FAIL_VOID_TAG(subscription_id >= 0, _W, "Invalid parameter");
225         g_dbus_connection_signal_unsubscribe(dbus_connection, static_cast<guint>(subscription_id));
226 }
227
228 void ctx::dbus_server::publish(const char* dest, int req_id, const char* subject, int error, const char* data)
229 {
230         _instance->publish(dest, req_id, subject, error, data);
231 }