2 * Copyright (c) 2017 Panasonic Corporation
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:
11 * The above copyright notice and this permission notice shall be included in all
12 * copies or substantial portions of the Software.
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
33 #include "cpptoml/cpptoml.h"
37 #define RUNXDG_CONFIG "runxdg.toml"
38 #define AREA_NORMAL_FULL "normal.full"
40 void fatal(const char* format, ...)
43 va_start(va_args, format);
44 vfprintf(stderr, format, va_args);
50 void warn(const char* format, ...)
53 va_start(va_args, format);
54 vfprintf(stderr, format, va_args);
58 void debug(const char* format, ...)
61 va_start(va_args, format);
62 vfprintf(stderr, format, va_args);
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));
71 void RunXDG::notify_ivi_control_cb (ilmObjectType object, t_ilm_uint id,
74 if (object == ILM_SURFACE) {
75 struct ilmSurfaceProperties surf_props;
77 ilm_getPropertiesOfSurface(id, &surf_props);
78 pid_t surf_pid = surf_props.creatorPid;
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);
87 AGL_DEBUG("ivi surface (id=%d, pid=%d) is created.", id, surf_pid);
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)) {
95 m_surfaces[surf_pid] = id;
96 } else if (object == ILM_LAYER) {
98 AGL_DEBUG("ivi layer: %d created.", id);
100 AGL_DEBUG("ivi layer: %d destroyed.", id);
103 //static std::function<int(int)> reply = _reply;
104 AGL_DEBUG("REPLY: %p, %p", _activity_reply, &_activity_reply);
106 ret = this->m_lc->getActivityStatus("hvac", &_activity_reply);
108 AGL_DEBUG("Success!! maybe reply late!!!");
110 AGL_DEBUG("Error!! getActivityStatus(hvac)");
114 //Test for AGL LifeCycle Management
117 ret = this->m_wm->getActivityStatus("hvac", &state);
119 AGL_DEBUG("Check HVAC state (%s)", state);
121 AGL_DEBUG("Error!! getActivityStatus(hvac)");
123 ret = this->m_wm->getActivityStatus("dashboard", &state);
125 AGL_DEBUG("Check Dashboard state (%s)", state);
127 AGL_DEBUG("Error!! getActivityStatus(dashboard)");
129 ret = this->m_wm->getActivityStatus("mediaplayer", &state);
131 AGL_DEBUG("Check MediaPlayer state (%s)", state);
133 AGL_DEBUG("Error!! getActivityStatus(mediaplayer)");
135 ret = this->m_wm->getActivityStatus("setting", &state);
137 AGL_DEBUG("Check Settting state (%s)", state);
139 AGL_DEBUG("Error!! getActivityStatus(setting)");
145 void RunXDG::notify_ivi_control_cb_static (ilmObjectType object, t_ilm_uint id,
146 t_ilm_bool created, void *user_data)
148 RunXDG *runxdg = static_cast<RunXDG*>(user_data);
149 runxdg->notify_ivi_control_cb(object, id, created);
152 int POSIXLauncher::launch (std::string& name)
158 AGL_DEBUG("cannot fork()");
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();
168 argv[m_args_v.size()] = NULL;
170 execv(argv[0], (char **)argv);
172 AGL_FATAL("fail to execve(%s)", argv[0]);
179 void POSIXLauncher::loop (volatile sig_atomic_t& e_flag)
185 ret = waitpid(m_rid, &status, 0);
187 if (errno == EINTR) {
188 AGL_DEBUG("catch EINTR while waitpid()");
196 if (WIFEXITED(status)) {
197 AGL_DEBUG("%s terminated, return %d", m_args_v[0].c_str(),
198 WEXITSTATUS(status));
200 if (WIFSIGNALED(status)) {
201 AGL_DEBUG("%s terminated by signal %d", m_args_v[0].c_str(),
207 /* parent killed by someone, so need to kill children */
208 AGL_DEBUG("killpg(0, SIGTERM)");
213 int AFMDBusLauncher::get_dbus_message_bus (GBusType bus_type,
214 GDBusConnection * &conn)
218 conn = g_bus_get_sync(bus_type, NULL, &err);
220 AGL_WARN("Failed to get session bus: %s", err->message);
221 g_clear_error (&err);
228 int AFMDBusLauncher::launch (std::string &name)
232 GDBusConnection* conn;
236 const char* xdg_app = name.c_str();
238 if (get_dbus_message_bus(G_BUS_TYPE_SESSION, conn)) {
242 msg = g_dbus_message_new_method_call (
249 AGL_WARN("Failed to allocate the dbus message");
250 g_object_unref(conn);
254 g_dbus_message_set_body(msg, g_variant_new("(s)", xdg_app));
256 re = g_dbus_connection_send_message_with_reply_sync (
257 conn, msg, G_DBUS_SEND_MESSAGE_FLAGS_NONE, -1, NULL, NULL, &err);
260 AGL_WARN("unable to send message: %s", err->message);
262 g_object_unref(conn);
267 g_dbus_connection_flush_sync(conn, NULL, &err);
269 AGL_WARN("unable to flush message queue: %s", err->message);
270 g_object_unref(conn);
276 body = g_dbus_message_get_body(re);
277 g_variant_get(body, "(&s)", &val);
279 AGL_DEBUG("dbus message get (%s)", val);
281 pid_t rid = std::stol(std::string(val));
282 AGL_DEBUG("RID = %d", rid);
284 g_object_unref(conn);
291 volatile sig_atomic_t e_flag = 0;
293 static void sigterm_handler (int signum)
298 static void init_signal (void)
300 struct sigaction act, info;
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.");
311 act.sa_handler = &sigterm_handler;
312 if (sigemptyset(&act.sa_mask) != 0) {
313 AGL_FATAL("Cannot initialize sigaction");
317 if (sigaction(SIGTERM, &act, &info) != 0) {
318 AGL_FATAL("Cannot register signal handler for SIGTERM");
322 int RunXDG::init_wm (void)
324 m_wm = new LibWindowmanager();
325 if (m_wm->init(m_port, m_token.c_str())) {
326 AGL_DEBUG("cannot initialize windowmanager");
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);
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);
342 std::function< void(json_object*) > h_visible = [](json_object* object) {
343 AGL_DEBUG("Got Event_Visible");
346 std::function< void(json_object*) > h_invisible = [](json_object* object) {
347 AGL_DEBUG("Got Event_Invisible");
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());
356 std::function< void(json_object*) > h_flushdraw= [](json_object* object) {
357 AGL_DEBUG("Got Event_FlushDraw");
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);
367 // Test code of lifecycle
368 // Register lifecycle observer for 'HVAC' & 'Dashboard'
369 this->m_lc->registerActivityObserver("hvac");
370 this->m_lc->registerActivityObserver("dashboard");
372 std::function< void(json_object*) > h_statusChanged =
373 [this](json_object* object)
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");
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);
387 m_lc->set_event_handler(h_statusChanged);
391 int RunXDG::init_hs (void)
393 m_hs = new LibHomeScreen();
394 if (m_hs->init(m_port, m_token.c_str())) {
395 AGL_DEBUG("cannot initialize homescreen");
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);
403 m_hs->set_event_handler(LibHomeScreen::Event_TapShortcut, handler);
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);
409 m_hs->set_event_handler(LibHomeScreen::Event_OnScreenMessage, h_default);
414 int RunXDG::parse_config (const char *path_to_config)
416 auto config = cpptoml::parse_file(path_to_config);
418 if (config == nullptr) {
419 AGL_DEBUG("cannot parse %s", path_to_config);
423 AGL_DEBUG("[%s] parsed", path_to_config);
425 auto app = config->get_table("application");
426 if (app == nullptr) {
427 AGL_DEBUG("cannto find [application]");
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");
437 std::string method = *(app->get_as<std::string>("method"));
438 if (method.empty()) {
439 method = std::string("POSIX");
444 /* Setup API of launcher */
445 if (method == "POSIX") {
446 pl = new POSIXLauncher();
448 } else if (method == "AFM_DBUS") {
449 m_launcher = new AFMDBusLauncher();
451 } else if (method == "AFM_WEBSOCKET") {
452 m_launcher = new AFMWebSocketLauncher();
455 AGL_FATAL("Unknown type of launcher");
459 pl->m_args_v.push_back(m_path);
462 auto params = app->get_array_of<std::string>("params");
463 for (const auto& param : *params)
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());
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());
486 pl->m_args_v.push_back(param);
488 AGL_DEBUG("params[%s]", param.c_str());
494 RunXDG::RunXDG (int port, const char* token, const char* id)
496 m_id = std::string(id);
498 m_token = std::string(token);
501 auto path = std::string(getenv("AFM_APP_INSTALL_DIR"));
502 path = path + "/" + RUNXDG_CONFIG;
504 // Parse config file of runxdg
505 if (parse_config(path.c_str())) {
506 AGL_FATAL("Error in config");
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());
513 // Setup HomeScreen/WindowManager API
515 AGL_FATAL("cannot setup wm API");
518 AGL_FATAL("cannot setup hs API");
520 // Setup ilmController API
521 m_ic = new ILMControl(notify_ivi_control_cb_static, this);
523 m_lc = new LifeCycleObserver;
525 AGL_DEBUG("RunXDG created.");
528 void RunXDG::setup_surface (void)
530 std::string sid = std::to_string(m_ivi_id);
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);
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);
544 void POSIXLauncher::register_surfpid (pid_t surf_pid)
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));
556 void POSIXLauncher::unregister_surfpid (pid_t surf_pid)
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++);
568 pid_t POSIXLauncher::find_surfpid_by_rid (pid_t rid)
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);
579 void AFMLauncher::register_surfpid (pid_t surf_pid)
583 pgid = getpgid(surf_pid);
586 AGL_DEBUG("fail to get process group id");
590 AGL_DEBUG("Surface creator is pid=%d, pgid=%d", surf_pid, pgid);
592 if (!m_pgids.count(pgid)) {
593 m_pgids[pgid] = surf_pid;
597 void AFMLauncher::unregister_surfpid (pid_t surf_pid)
599 auto itr = m_pgids.begin();
600 while (itr != m_pgids.end()) {
601 if (itr->second == surf_pid) {
602 m_pgids.erase(itr++);
609 pid_t AFMLauncher::find_surfpid_by_rid (pid_t rid)
611 auto itr = m_pgids.find(rid);
612 if (itr != m_pgids.end())
618 void RunXDG::start (void)
620 // Initialize SIGTERM handler
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);
629 // take care 1st time launch
630 AGL_DEBUG("waiting for notification: surafce created");
631 m_pending_create = true;
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);
639 AGL_DEBUG("match: surf:pid=%d, afm:rid=%d", surf_pid,
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,
653 m_launcher->loop(e_flag);
656 int main (int argc, const char* argv[])
659 // setenv("USE_HMI_DEBUG", "5", 1);
660 // setenv("WAYLAND_DEBUG", "1", 1);
667 AGL_FATAL("Missing port and token");
671 const char *afm_id = getenv("AFM_ID");
672 if (afm_id == NULL || !afm_id[0]) {
677 port = std::stol(argv[1]);
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");
685 RunXDG runxdg(port, token, afm_id);