e6ff11fc007d4316d110f6068dd7d719ebc6c477
[platform/framework/web/crosswalk-tizen.git] / src / extension / extension_server.cc
1 // Copyright 2015 Samsung Electronics Co, Ltd. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "extension/extension_server.h"
6
7 #include <glob.h>
8 #include <glib.h>
9 #include <glib-unix.h>
10
11 #include <string>
12 #include <vector>
13
14 #include "common/logger.h"
15 #include "common/constants.h"
16 #include "common/file_utils.h"
17 #include "common/string_utils.h"
18 #include "common/command_line.h"
19 #include "extension/extension.h"
20
21 namespace wrt {
22
23 namespace {
24
25 const char kExtensionPrefix[] = "lib";
26 const char kExtensionSuffix[] = ".so";
27
28 const char kDBusIntrospectionXML[] =
29   "<node>"
30   "  <interface name='org.tizen.wrt.Extension'>"
31   "    <method name='GetExtensions'>"
32   "      <arg name='extensions' type='a(ssas)' direction='out' />"
33   "    </method>"
34   "    <method name='CreateInstance'>"
35   "      <arg name='extension_name' type='s' direction='in' />"
36   "      <arg name='instance_id' type='s' direction='out' />"
37   "    </method>"
38   "    <method name='DestroyInstance'>"
39   "      <arg name='instance_id' type='s' direction='in' />"
40   "      <arg name='instance_id' type='s' direction='out' />"
41   "    </method>"
42   "    <method name='PostMessage'>"
43   "      <arg name='instance_id' type='s' direction='in' />"
44   "      <arg name='msg' type='s' direction='in' />"
45   "    </method>"
46   "    <method name='SendSyncMessage'>"
47   "      <arg name='instance_id' type='s' direction='in' />"
48   "      <arg name='msg' type='s' direction='in' />"
49   "      <arg name='reply' type='s' direction='out' />"
50   "    </method>"
51   "    <signal name='OnMessageToJS'>"
52   "      <arg name='instance_id' type='s' />"
53   "      <arg name='msg' type='s' />"
54   "    </signal>"
55   "  </interface>"
56   "</node>";
57
58 }  // namespace
59
60 ExtensionServer::ExtensionServer(const std::string& uuid)
61     : app_uuid_(uuid) {
62 }
63
64 ExtensionServer::~ExtensionServer() {
65 }
66
67 bool ExtensionServer::Start() {
68   return Start(StringVector());
69 }
70
71 bool ExtensionServer::Start(const StringVector& paths) {
72   // Connect to DBusServer for Application of Runtime
73   if (!dbus_application_client_.ConnectByName(
74           app_uuid_ + "." + std::string(kDBusNameForApplication))) {
75     LOGGER(ERROR) << "Failed to connect to the dbus server for Application.";
76     return false;
77   }
78
79   // Register system extensions to support Tizen Device APIs
80   RegisterSystemExtensions();
81
82   // Register user extensions
83   for (auto it = paths.begin(); it != paths.end(); ++it) {
84     if (utils::Exists(*it)) {
85       RegisterExtension(*it);
86     }
87   }
88
89   // Start DBusServer
90   using std::placeholders::_1;
91   using std::placeholders::_2;
92   using std::placeholders::_3;
93   using std::placeholders::_4;
94   dbus_server_.SetIntrospectionXML(kDBusIntrospectionXML);
95   dbus_server_.SetMethodCallback(
96       kDBusInterfaceNameForExtension,
97       std::bind(&ExtensionServer::HandleDBusMethod, this, _1, _2, _3, _4));
98   dbus_server_.Start(app_uuid_ + "." + std::string(kDBusNameForExtension));
99
100   // Send 'ready' signal to Injected Bundle.
101   NotifyEPCreatedToApplication();
102
103   return true;
104 }
105
106 void ExtensionServer::RegisterExtension(const std::string& path) {
107   Extension* ext = new Extension(path, this);
108   if (!ext->Initialize() || !RegisterSymbols(ext)) {
109     delete ext;
110     return;
111   }
112   extensions_[ext->name()] = ext;
113   LOGGER(DEBUG) << ext->name() << " is registered.";
114 }
115
116 void ExtensionServer::RegisterSystemExtensions() {
117 #ifdef EXTENSION_PATH
118   std::string extension_path(EXTENSION_PATH);
119 #else
120   #error EXTENSION_PATH is not set.
121 #endif
122   extension_path.append("/");
123   extension_path.append(kExtensionPrefix);
124   extension_path.append("*");
125   extension_path.append(kExtensionSuffix);
126
127   glob_t glob_result;
128   glob(extension_path.c_str(), GLOB_TILDE, NULL, &glob_result);
129   for (unsigned int i = 0; i < glob_result.gl_pathc; ++i) {
130     RegisterExtension(glob_result.gl_pathv[i]);
131   }
132 }
133
134 bool ExtensionServer::RegisterSymbols(Extension* extension) {
135   std::string name = extension->name();
136
137   if (extension_symbols_.find(name) != extension_symbols_.end()) {
138     LOGGER(WARN) << "Ignoring extension with name already registred. '"
139                  << name << "'";
140     return false;
141   }
142
143   Extension::StringVector entry_points = extension->entry_points();
144   for (auto it = entry_points.begin(); it != entry_points.end(); ++it) {
145     if (extension_symbols_.find(*it) != extension_symbols_.end()) {
146       LOGGER(WARN) << "Ignoring extension with entry_point already registred. '"
147                    << (*it) << "'";
148       return false;
149     }
150   }
151
152   for (auto it = entry_points.begin(); it != entry_points.end(); ++it) {
153     extension_symbols_.insert(*it);
154   }
155
156   extension_symbols_.insert(name);
157
158   return true;
159 }
160
161 void ExtensionServer::GetRuntimeVariable(const char* key, char* value,
162     size_t value_len) {
163   GVariant* ret = dbus_application_client_.Call(
164       kDBusInterfaceNameForApplication, kMethodGetRuntimeVariable,
165       g_variant_new("(s)", key), G_VARIANT_TYPE("(s)"));
166
167   if (!ret) {
168     LOGGER(ERROR) << "Failed to get runtime variable from Application. ("
169                   << key << ")";
170     return;
171   }
172
173   gchar* v;
174   g_variant_get(ret, "(&s)", &v);
175   strncpy(value, v, value_len);
176
177   g_variant_unref(ret);
178 }
179
180 void ExtensionServer::NotifyEPCreatedToApplication() {
181   dbus_application_client_.Call(
182       kDBusInterfaceNameForApplication, kMethodNotifyEPCreated,
183       g_variant_new("(s)", dbus_server_.GetClientAddress().c_str()),
184       NULL);
185 }
186
187 void ExtensionServer::HandleDBusMethod(GDBusConnection* connection,
188                                        const std::string& method_name,
189                                        GVariant* parameters,
190                                        GDBusMethodInvocation* invocation) {
191   if (method_name == kMethodGetExtensions) {
192     OnGetExtensions(invocation);
193   } else if (method_name == kMethodCreateInstance) {
194     gchar* extension_name;
195     g_variant_get(parameters, "(&s)", &extension_name);
196     OnCreateInstance(connection, extension_name, invocation);
197   } else if (method_name == kMethodDestroyInstance) {
198     gchar* instance_id;
199     g_variant_get(parameters, "(&s)", &instance_id);
200     OnDestroyInstance(instance_id, invocation);
201   } else if (method_name == kMethodSendSyncMessage) {
202     gchar* instance_id;
203     gchar* msg;
204     g_variant_get(parameters, "(&s&s)", &instance_id, &msg);
205     OnSendSyncMessage(instance_id, msg, invocation);
206   } else if (method_name == kMethodPostMessage) {
207     gchar* instance_id;
208     gchar* msg;
209     g_variant_get(parameters, "(&s&s)", &instance_id, &msg);
210     OnPostMessage(instance_id, msg);
211   }
212 }
213
214 void ExtensionServer::OnGetExtensions(GDBusMethodInvocation* invocation) {
215   GVariantBuilder builder;
216
217   g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY);
218
219   // build an array of extensions
220   auto it = extensions_.begin();
221   for ( ; it != extensions_.end(); ++it) {
222     Extension* ext = it->second;
223     // open container for extension
224     g_variant_builder_open(&builder, G_VARIANT_TYPE("(ssas)"));
225     g_variant_builder_add(&builder, "s", ext->name().c_str());
226     g_variant_builder_add(&builder, "s", ext->javascript_api().c_str());
227     // open container for entry_point
228     g_variant_builder_open(&builder, G_VARIANT_TYPE("as"));
229     auto it_entry = ext->entry_points().begin();
230     for ( ; it_entry != ext->entry_points().end(); ++it_entry) {
231       g_variant_builder_add(&builder, "s", (*it_entry).c_str());
232     }
233     // close container('as') for entry_point
234     g_variant_builder_close(&builder);
235     // close container('(ssas)') for extension
236     g_variant_builder_close(&builder);
237   }
238
239   GVariant* reply = NULL;
240   if (extensions_.size() == 0) {
241     reply = g_variant_new_array(G_VARIANT_TYPE("(ssas)"), NULL, 0);
242   } else {
243     reply = g_variant_builder_end(&builder);
244   }
245
246   g_dbus_method_invocation_return_value(
247       invocation, g_variant_new_tuple(&reply, 1));
248 }
249
250 void ExtensionServer::OnCreateInstance(
251     GDBusConnection* connection, const std::string& extension_name,
252     GDBusMethodInvocation* invocation) {
253   std::string instance_id = utils::GenerateUUID();
254
255   // find extension with given the extension name
256   auto it = extensions_.find(extension_name);
257   if (it == extensions_.end()) {
258     LOGGER(ERROR) << "Failed to find extension '" << extension_name << "'";
259     g_dbus_method_invocation_return_error(
260         invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
261         "Not found extension %s", extension_name.c_str());
262     return;
263   }
264
265   // create instance
266   ExtensionInstance* instance = it->second->CreateInstance();
267   if (!instance) {
268     LOGGER(ERROR) << "Failed to create instance of extension '"
269                   << extension_name << "'";
270     g_dbus_method_invocation_return_error(
271         invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
272         "Failed to create instance of extension %s", extension_name.c_str());
273     return;
274   }
275
276   // set callbacks
277   using std::placeholders::_1;
278   instance->SetPostMessageCallback(
279       std::bind(&ExtensionServer::PostMessageToJSCallback,
280                 this, connection, instance_id, _1));
281
282   instances_[instance_id] = instance;
283   g_dbus_method_invocation_return_value(
284       invocation, g_variant_new("(s)", instance_id.c_str()));
285 }
286
287 void ExtensionServer::OnDestroyInstance(
288     const std::string& instance_id, GDBusMethodInvocation* invocation) {
289   // find instance with the given instance id
290   auto it = instances_.find(instance_id);
291   if (it == instances_.end()) {
292     LOGGER(ERROR) << "Failed to find instance '" << instance_id << "'";
293     g_dbus_method_invocation_return_error(
294         invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
295         "Not found instance %s", instance_id.c_str());
296     return;
297   }
298
299   // destroy the instance
300   ExtensionInstance* instance = it->second;
301   delete instance;
302
303   instances_.erase(it);
304
305   g_dbus_method_invocation_return_value(
306       invocation, g_variant_new("(s)", instance_id.c_str()));
307 }
308
309 void ExtensionServer::OnSendSyncMessage(
310     const std::string& instance_id, const std::string& msg,
311     GDBusMethodInvocation* invocation) {
312   // find instance with the given instance id
313   auto it = instances_.find(instance_id);
314   if (it == instances_.end()) {
315     LOGGER(ERROR) << "Failed to find instance '" << instance_id << "'";
316     g_dbus_method_invocation_return_error(
317         invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
318         "Not found instance %s", instance_id.c_str());
319     return;
320   }
321
322   ExtensionInstance* instance = it->second;
323
324   using std::placeholders::_1;
325   instance->SetSendSyncReplyCallback(
326       std::bind(&ExtensionServer::SyncReplyCallback, this, _1, invocation));
327
328   instance->HandleSyncMessage(msg);
329
330   // reponse will be sent by SyncReplyCallback()
331 }
332
333 // async
334 void ExtensionServer::OnPostMessage(
335     const std::string& instance_id, const std::string& msg) {
336   auto it = instances_.find(instance_id);
337   if (it == instances_.end()) {
338     LOGGER(ERROR) << "Failed to find instance '" << instance_id << "'";
339     return;
340   }
341
342   ExtensionInstance* instance = it->second;
343   instance->HandleMessage(msg);
344 }
345
346 void ExtensionServer::SyncReplyCallback(
347     const std::string& reply, GDBusMethodInvocation* invocation) {
348   g_dbus_method_invocation_return_value(
349       invocation, g_variant_new("(s)", reply.c_str()));
350 }
351
352 void ExtensionServer::PostMessageToJSCallback(
353     GDBusConnection* connection, const std::string& instance_id,
354     const std::string& msg) {
355   if (!connection || g_dbus_connection_is_closed(connection)) {
356     LOGGER(ERROR) << "Client connection is closed already.";
357     return;
358   }
359
360   dbus_server_.SendSignal(connection,
361                           kDBusInterfaceNameForExtension,
362                           kSignalOnMessageToJS,
363                           g_variant_new("(ss)",
364                                         instance_id.c_str(),
365                                         msg.c_str()));
366 }
367
368 // static
369 bool ExtensionServer::StartExtensionProcess() {
370   GMainLoop* loop;
371
372   loop = g_main_loop_new(NULL, FALSE);
373
374   // Register Quit Signal Handlers
375   auto quit_callback = [](gpointer data) -> gboolean {
376     GMainLoop* loop = reinterpret_cast<GMainLoop*>(data);
377     g_main_loop_quit(loop);
378     return false;
379   };
380   g_unix_signal_add(SIGINT, quit_callback, loop);
381   g_unix_signal_add(SIGTERM, quit_callback, loop);
382
383   CommandLine* cmd = CommandLine::ForCurrentProcess();
384
385   // TODO(wy80.choi): Receive extension paths for user defined extensions.
386
387   // Receive AppID from arguments.
388   if (cmd->arguments().size() < 1) {
389     LOGGER(ERROR) << "uuid is required.";
390     return false;
391   }
392   std::string uuid = cmd->arguments()[0];
393
394   // Start ExtensionServer
395   ExtensionServer server(uuid);
396   if (!server.Start()) {
397     LOGGER(ERROR) << "Failed to start extension server.";
398     return false;
399   }
400
401   LOGGER(INFO) << "extension process has been started.";
402
403   g_main_loop_run(loop);
404
405   LOGGER(INFO) << "extension process is exiting.";
406
407   g_main_loop_unref(loop);
408
409   return true;
410 }
411
412 }  // namespace wrt