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