RunXDG
[staging/xdg-launcher.git] / src / runxdg.cpp
1 /*
2  * Copyright (c) 2017 Panasonic Corporation
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a copy
5  * of this software and associated documentation files (the "Software"), to deal
6  * in the Software without restriction, including without limitation the rights
7  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8  * copies of the Software, and to permit persons to whom the Software is
9  * furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in all
12  * copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20  * SOFTWARE.
21  */
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <unistd.h>
26 #include <stdarg.h>
27 #include <sys/stat.h>
28 #include <sys/time.h>
29 #include <sys/wait.h>
30
31 #include <cstdio>
32
33 #include "cpptoml/cpptoml.h"
34
35 #include "runxdg.hpp"
36
37 #define RUNXDG_CONFIG "runxdg.toml"
38
39 void fatal(const char* format, ...)
40 {
41   va_list va_args;
42   va_start(va_args, format);
43   vfprintf(stderr, format, va_args);
44   va_end(va_args);
45
46   exit(EXIT_FAILURE);
47 }
48
49 void warn(const char* format, ...)
50 {
51   va_list va_args;
52   va_start(va_args, format);
53   vfprintf(stderr, format, va_args);
54   va_end(va_args);
55 }
56
57 void debug(const char* format, ...)
58 {
59   va_list va_args;
60   va_start(va_args, format);
61   vfprintf(stderr, format, va_args);
62   va_end(va_args);
63 }
64
65 void RunXDG::notify_ivi_control_cb (ilmObjectType object, t_ilm_uint id,
66                                     t_ilm_bool created)
67 {
68   if (object == ILM_SURFACE) {
69     struct ilmSurfaceProperties surf_props;
70
71     ilm_getPropertiesOfSurface(id, &surf_props);
72     pid_t surf_pid = surf_props.creatorPid;
73
74     if (!created) {
75       AGL_DEBUG("ivi surface (id=%d, pid=%d) destroyed.", id, surf_pid);
76       m_launcher->unregister_surfpid(surf_pid);
77       m_surfaces.erase(surf_pid);
78       return;
79     }
80
81     AGL_DEBUG("ivi surface (id=%d, pid=%d) is created.", id, surf_pid);
82
83     m_launcher->register_surfpid(surf_pid);
84     if (m_launcher->m_rid &&
85         surf_pid == m_launcher->find_surfpid_by_rid(m_launcher->m_rid)) {
86       setup_surface(id);
87     }
88     m_surfaces[surf_pid] = id;
89   } else if (object == ILM_LAYER) {
90     if (created)
91       AGL_DEBUG("ivi layer: %d created.", id);
92     else
93       AGL_DEBUG("ivi layer: %d destroyed.", id);
94   }
95 }
96
97 void RunXDG::notify_ivi_control_cb_static (ilmObjectType object, t_ilm_uint id,
98                                            t_ilm_bool created, void *user_data)
99 {
100   RunXDG *runxdg = static_cast<RunXDG*>(user_data);
101   runxdg->notify_ivi_control_cb(object, id, created);
102 }
103
104 int POSIXLauncher::launch (std::string& name)
105 {
106   pid_t pid;
107
108   pid = fork();
109   if (pid < 0) {
110     AGL_DEBUG("cannot fork()");
111     return -1;
112   }
113
114   if (pid == 0) {
115     // child
116     const char **argv = new const char * [m_args_v.size() + 1];
117     for (unsigned int i = 0; i < m_args_v.size(); ++i) {
118       argv[i] = m_args_v[i].c_str();
119     }
120     argv[m_args_v.size()] = NULL;
121
122     execv(argv[0], (char **)argv);
123
124     AGL_FATAL("fail to execve(%s)", argv[0]);
125   }
126   // parent
127
128   return pid;
129 }
130
131 void POSIXLauncher::loop (volatile sig_atomic_t& e_flag)
132 {
133   int status;
134   pid_t ret;
135
136   while (!e_flag) {
137     ret = waitpid(m_rid, &status, 0);
138     if (ret < 0) {
139       if (errno == EINTR) {
140         AGL_DEBUG("catch EINTR while waitpid()");
141         continue;
142       }
143       break;
144     }
145   }
146
147   if (ret > 0) {
148     if (WIFEXITED(status)) {
149       AGL_DEBUG("%s terminated, return %d", m_args_v[0].c_str(),
150                 WEXITSTATUS(status));
151     }
152     if (WIFSIGNALED(status)) {
153       AGL_DEBUG("%s terminated by signal %d", m_args_v[0].c_str(),
154                 WTERMSIG(status));
155     }
156   }
157
158   if (e_flag) {
159     /* parent killed by someone, so need to kill children */
160     AGL_DEBUG("killpg(0, SIGTERM)");
161     killpg(0, SIGTERM);
162   }
163 }
164
165 int AFMDBusLauncher::get_dbus_message_bus (GBusType bus_type,
166                                            GDBusConnection * &conn)
167 {
168   GError* err = NULL;
169
170   conn = g_bus_get_sync(bus_type, NULL, &err);
171   if (err) {
172     AGL_WARN("Failed to get session bus: %s", err->message);
173     g_clear_error (&err);
174     return -1;
175   }
176
177   return 0;
178 }
179
180 int AFMDBusLauncher::launch (std::string &name)
181 {
182   GDBusMessage*       msg;
183   GDBusMessage*       re;
184   GDBusConnection*    conn;
185   GError*             err = NULL;
186   GVariant*           body;
187   char*               val;
188   const char*         xdg_app = name.c_str();
189
190   if (get_dbus_message_bus(G_BUS_TYPE_SESSION, conn)) {
191     return -1;
192   }
193
194   msg = g_dbus_message_new_method_call (
195       DBUS_SERVICE,
196       DBUS_PATH,
197       DBUS_INTERFACE,
198       "start");
199
200   if (msg == NULL) {
201     AGL_WARN("Failed to allocate the dbus message");
202     g_object_unref(conn);
203     return -1;
204   }
205
206   g_dbus_message_set_body(msg, g_variant_new("(s)", xdg_app));
207
208   re = g_dbus_connection_send_message_with_reply_sync (
209       conn, msg, G_DBUS_SEND_MESSAGE_FLAGS_NONE, -1, NULL, NULL, &err);
210
211   if (err != NULL) {
212     AGL_WARN("unable to send message: %s", err->message);
213     g_clear_error(&err);
214     g_object_unref(conn);
215     g_object_unref(msg);
216     return -1;
217   }
218
219   g_dbus_connection_flush_sync(conn, NULL, &err);
220   if (err != NULL) {
221     AGL_WARN("unable to flush message queue: %s", err->message);
222     g_object_unref(conn);
223     g_object_unref(msg);
224     g_object_unref(re);
225     return -1;
226   }
227
228   body = g_dbus_message_get_body(re);
229   g_variant_get(body, "(&s)", &val);
230
231   AGL_DEBUG("dbus message get (%s)", val);
232
233   pid_t rid = std::stol(std::string(val));
234   AGL_DEBUG("RID = %d", rid);
235
236   g_object_unref(conn);
237   g_object_unref(msg);
238   g_object_unref(re);
239
240   return rid;
241 }
242
243 volatile sig_atomic_t e_flag = 0;
244
245 static void sigterm_handler (int signum)
246 {
247   e_flag = 1;
248 }
249
250 static void init_signal (void)
251 {
252   struct sigaction act, info;
253
254   /* Setup signal for SIGTERM */
255   if (!sigaction(SIGTERM, NULL, &info)) {
256     if (info.sa_handler == SIG_IGN) {
257       AGL_DEBUG("SIGTERM being ignored.");
258     } else if (info.sa_handler == SIG_DFL) {
259       AGL_DEBUG("SIGTERM being defaulted.");
260     }
261   }
262
263   act.sa_handler = &sigterm_handler;
264   if (sigemptyset(&act.sa_mask) != 0) {
265     AGL_FATAL("Cannot initialize sigaction");
266   }
267   act.sa_flags = 0;
268
269   if (sigaction(SIGTERM, &act, &info) != 0) {
270     AGL_FATAL("Cannot register signal handler for SIGTERM");
271   }
272 }
273
274 int RunXDG::init_wm (void)
275 {
276   m_wm = new LibWindowmanager();
277   if (m_wm->init(m_port, m_token.c_str())) {
278     AGL_DEBUG("cannot initialize windowmanager");
279     return -1;
280   }
281
282   std::function< void(json_object*) > h_active = [](json_object* object) {
283     AGL_DEBUG("Got Event_Active");
284   };
285
286   std::function< void(json_object*) > h_inactive = [](json_object* object) {
287     AGL_DEBUG("Got Event_Inactive");
288   };
289
290   std::function< void(json_object*) > h_visible = [](json_object* object) {
291     AGL_DEBUG("Got Event_Visible");
292   };
293
294   std::function< void(json_object*) > h_invisible = [](json_object* object) {
295     AGL_DEBUG("Got Event_Invisible");
296   };
297
298   std::function< void(json_object*) > h_syncdraw =
299       [this](json_object* object) {
300     AGL_DEBUG("Got Event_SyncDraw");
301     json_object* obj = json_object_new_object();
302     json_object_object_add(obj, this->m_wm->kKeyDrawingName,
303                            json_object_new_string(this->m_role.c_str()));
304     this->m_wm->endDraw(obj);
305   };
306
307   std::function< void(json_object*) > h_flushdraw= [](json_object* object) {
308     AGL_DEBUG("Got Event_FlushDraw");
309   };
310
311   m_wm->set_event_handler(LibWindowmanager::Event_Active, h_active);
312   m_wm->set_event_handler(LibWindowmanager::Event_Inactive, h_inactive);
313   m_wm->set_event_handler(LibWindowmanager::Event_Visible, h_visible);
314   m_wm->set_event_handler(LibWindowmanager::Event_Invisible, h_invisible);
315   m_wm->set_event_handler(LibWindowmanager::Event_SyncDraw, h_syncdraw);
316   m_wm->set_event_handler(LibWindowmanager::Event_FlushDraw, h_flushdraw);
317
318   return 0;
319 }
320
321 int RunXDG::init_hs (void)
322 {
323   m_hs = new LibHomeScreen();
324   if (m_hs->init(m_port, m_token.c_str())) {
325     AGL_DEBUG("cannot initialize homescreen");
326     return -1;
327   }
328
329   std::function< void(json_object*) > handler = [this] (json_object* object) {
330     json_object *val;
331
332     if (json_object_object_get_ex(object, "application_name", &val)) {
333       const char *name = json_object_get_string(val);
334
335       AGL_DEBUG("Event_TapShortcut <%s>", name);
336
337       if (strcmp(name, this->m_role.c_str()) == 0) {
338         // check app exist and re-launch if needed
339         AGL_DEBUG("Activesurface %s ", this->m_role.c_str());
340
341         json_object *obj = json_object_new_object();
342         json_object_object_add(obj, this->m_wm->kKeyDrawingName,
343                                json_object_new_string(this->m_role.c_str()));
344         json_object_object_add(obj, this->m_wm->kKeyDrawingArea,
345                                json_object_new_string("normal.full"));
346
347         this->m_wm->activateSurface(obj);
348       }
349     }
350   };
351   m_hs->set_event_handler(LibHomeScreen::Event_TapShortcut, handler);
352
353   std::function< void(json_object*) > h_default= [](json_object* object) {
354     const char *j_str = json_object_to_json_string(object);
355     AGL_DEBUG("Got event [%s]", j_str);
356   };
357   m_hs->set_event_handler(LibHomeScreen::Event_OnScreenMessage, h_default);
358
359   return 0;
360 }
361
362 int RunXDG::parse_config (const char *path_to_config)
363 {
364   auto config = cpptoml::parse_file(path_to_config);
365
366   if (config == nullptr) {
367     AGL_DEBUG("cannot parse %s", path_to_config);
368     return -1;
369   }
370
371   AGL_DEBUG("[%s] parsed", path_to_config);
372
373   auto app = config->get_table("application");
374   if (app == nullptr) {
375     AGL_DEBUG("cannto find [application]");
376     return -1;
377   }
378
379   m_role = *(app->get_as<std::string>("role"));
380   m_path = *(app->get_as<std::string>("path"));
381   if (m_role.empty() || m_path.empty()) {
382     AGL_FATAL("No name or path defined in config");
383   }
384
385   std::string method = *(app->get_as<std::string>("method"));
386   if (method.empty()) {
387     method = std::string("POSIX");
388   }
389
390   POSIXLauncher *pl;
391
392   /* Setup API of launcher */
393   if (method == "POSIX") {
394     pl = new POSIXLauncher();
395     m_launcher = pl;
396   } else if (method == "AFM_DBUS") {
397     m_launcher = new AFMDBusLauncher();
398     return 0;
399   } else if (method == "AFM_WEBSOCKET") {
400     m_launcher = new AFMWebSocketLauncher();
401     return 0;
402   } else {
403     AGL_FATAL("Unknown type of launcher");
404   }
405
406   // setup argv[0]
407   pl->m_args_v.push_back(m_path);
408
409   // setup argv[1..n]
410   auto params = app->get_array_of<std::string>("params");
411   for (const auto& param : *params)
412   {
413     pl->m_args_v.push_back(param);
414     AGL_DEBUG("params[%s]", param.c_str());
415   }
416
417   return 0;
418 }
419
420 RunXDG::RunXDG (int port, const char* token, const char* id)
421 {
422   m_id = std::string(id);
423   m_port = port;
424   m_token = std::string(token);
425
426
427   auto path = std::string(getenv("AFM_APP_INSTALL_DIR"));
428   path = path + "/" + RUNXDG_CONFIG;
429
430   // Parse config file of runxdg
431   if (parse_config(path.c_str())) {
432     AGL_FATAL("Error in config");
433   }
434
435   AGL_DEBUG("id=[%s], name=[%s], path=[%s], port=%lu, token=[%s]",
436             m_id.c_str(), m_role.c_str(), m_path.c_str(),
437             m_port, m_token.c_str());
438
439   // Setup HomeScreen/WindowManager API
440   if (init_wm())
441     AGL_FATAL("cannot setup wm API");
442
443   if (init_hs())
444     AGL_FATAL("cannot setup hs API");
445
446   // Setup ilmController API
447   m_ic = new ILMControl(notify_ivi_control_cb_static, this);
448
449   AGL_DEBUG("RunXDG created.");
450 }
451
452 void RunXDG::setup_surface (int id)
453 {
454   std::string sid = std::to_string(id);
455
456   // This surface is mine, register pair app_name and ivi id.
457   json_object *obj = json_object_new_object();
458   json_object_object_add(obj, m_wm->kKeyDrawingName,
459                          json_object_new_string(m_role.c_str()));
460   json_object_object_add(obj, m_wm->kKeyIviId,
461                          json_object_new_string(sid.c_str()));
462
463   AGL_DEBUG("requestSurfaceXDG(%s,%s)", m_role.c_str(), sid.c_str());
464   m_wm->requestSurfaceXDG(obj);
465
466   if (m_pending_create) {
467     // Recovering 1st time tap_shortcut is dropped because
468     // the application has not been run yet (1st time launch)
469     m_pending_create = false;
470
471     json_object *obj = json_object_new_object();
472     json_object_object_add(obj, m_wm->kKeyDrawingName,
473                            json_object_new_string(m_role.c_str()));
474     json_object_object_add(obj, m_wm->kKeyDrawingArea,
475                            json_object_new_string("normal.full"));
476     m_wm->activateSurface(obj);
477   }
478 }
479
480 void POSIXLauncher::register_surfpid (pid_t surf_pid)
481 {
482   if (surf_pid == m_rid) {
483     if (!std::count(m_pid_v.begin(), m_pid_v.end(), surf_pid)) {
484       AGL_DEBUG("surface creator(pid=%d) registered", surf_pid);
485       m_pid_v.push_back(surf_pid);
486       AGL_DEBUG("m_pid_v.count(%d) = %d", surf_pid,
487                 std::count(m_pid_v.begin(), m_pid_v.end(), surf_pid));
488     }
489   }
490 }
491
492 void POSIXLauncher::unregister_surfpid (pid_t surf_pid)
493 {
494   auto itr = m_pid_v.begin();
495   while (itr != m_pid_v.end()) {
496     if (*itr == surf_pid) {
497       m_pid_v.erase(itr++);
498     } else {
499       ++itr;
500     }
501   }
502 }
503
504 pid_t POSIXLauncher::find_surfpid_by_rid (pid_t rid)
505 {
506   AGL_DEBUG("find surfpid by rid(%d)", rid);
507   if (std::count(m_pid_v.begin(), m_pid_v.end(), rid)) {
508     AGL_DEBUG("found return(%d)", rid);
509     return rid;
510   }
511
512   return -1;
513 }
514
515 void AFMLauncher::register_surfpid (pid_t surf_pid)
516 {
517   pid_t pgid = 0;
518
519   pgid = getpgid(surf_pid);
520
521   if (pgid < 0) {
522     AGL_DEBUG("fail to get process group id");
523     return;
524   }
525
526   AGL_DEBUG("Surface creator is pid=%d, pgid=%d", surf_pid, pgid);
527
528   if (!m_pgids.count(pgid)) {
529     m_pgids[pgid] = surf_pid;
530   }
531 }
532
533 void AFMLauncher::unregister_surfpid (pid_t surf_pid)
534 {
535   auto itr = m_pgids.begin();
536   while (itr != m_pgids.end()) {
537     if (itr->second == surf_pid) {
538       m_pgids.erase(itr++);
539     } else {
540       ++itr;
541     }
542   }
543 }
544
545 pid_t AFMLauncher::find_surfpid_by_rid (pid_t rid)
546 {
547   auto itr = m_pgids.find(rid);
548   if (itr != m_pgids.end())
549     return itr->second;
550
551   return -1;
552 }
553
554 void RunXDG::start (void)
555 {
556   // Initialize SIGTERM handler
557   init_signal();
558
559   /* Launch XDG application */
560   m_launcher->m_rid = m_launcher->launch(m_id);
561   if (m_launcher->m_rid < 0) {
562     AGL_FATAL("cannot launch XDG app (%s)", m_id);
563   }
564
565   // take care 1st time launch
566   AGL_DEBUG("waiting for notification: surafce created");
567   m_pending_create = true;
568
569   // in case, target app has already run
570   if (m_launcher->m_rid) {
571     pid_t surf_pid = m_launcher->find_surfpid_by_rid(m_launcher->m_rid);
572     if (surf_pid > 0) {
573       AGL_DEBUG("match: surf:pid=%d, afm:rid=%d", surf_pid,
574                 m_launcher->m_rid);
575       auto itr = m_surfaces.find(surf_pid);
576       if (itr != m_surfaces.end()) {
577         int id = itr->second;
578         AGL_DEBUG("surface %d for <%s> already exists", id,
579                   m_role.c_str());
580         setup_surface(id);
581       }
582     }
583   }
584   m_launcher->loop(e_flag);
585 }
586
587 int main (int argc, const char* argv[])
588 {
589   // Set debug flags
590   // setenv("USE_HMI_DEBUG", "5", 1);
591   // setenv("WAYLAND_DEBUG", "1", 1);
592
593   // Parse args
594   int port;
595   const char *token;
596
597   if (argc < 3) {
598     AGL_FATAL("Missing port and token");
599   }
600
601   // Get app id
602   const char *afm_id = getenv("AFM_ID");
603   if (afm_id == NULL || !afm_id[0]) {
604     afm_id = argv[0];
605   }
606
607   try {
608     port = std::stol(argv[1]);
609     token = argv[2];
610   } catch (const std::invalid_argument& e) {
611     AGL_FATAL("Invalid argument");
612   } catch (const std::out_of_range& e) {
613     AGL_FATAL("Out of range");
614   }
615
616   RunXDG runxdg(port, token, afm_id);
617
618   runxdg.start();
619
620   return 0;
621 }