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