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"
39 void fatal(const char* format, ...)
42 va_start(va_args, format);
43 vfprintf(stderr, format, va_args);
49 void warn(const char* format, ...)
52 va_start(va_args, format);
53 vfprintf(stderr, format, va_args);
57 void debug(const char* format, ...)
60 va_start(va_args, format);
61 vfprintf(stderr, format, va_args);
65 void RunXDG::notify_ivi_control_cb (ilmObjectType object, t_ilm_uint id,
68 if (object == ILM_SURFACE) {
69 struct ilmSurfaceProperties surf_props;
71 ilm_getPropertiesOfSurface(id, &surf_props);
72 pid_t surf_pid = surf_props.creatorPid;
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);
81 AGL_DEBUG("ivi surface (id=%d, pid=%d) is created.", id, surf_pid);
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)) {
88 m_surfaces[surf_pid] = id;
89 } else if (object == ILM_LAYER) {
91 AGL_DEBUG("ivi layer: %d created.", id);
93 AGL_DEBUG("ivi layer: %d destroyed.", id);
97 void RunXDG::notify_ivi_control_cb_static (ilmObjectType object, t_ilm_uint id,
98 t_ilm_bool created, void *user_data)
100 RunXDG *runxdg = static_cast<RunXDG*>(user_data);
101 runxdg->notify_ivi_control_cb(object, id, created);
104 int POSIXLauncher::launch (std::string& name)
110 AGL_DEBUG("cannot fork()");
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();
120 argv[m_args_v.size()] = NULL;
122 execv(argv[0], (char **)argv);
124 AGL_FATAL("fail to execve(%s)", argv[0]);
131 void POSIXLauncher::loop (volatile sig_atomic_t& e_flag)
137 ret = waitpid(m_rid, &status, 0);
139 if (errno == EINTR) {
140 AGL_DEBUG("catch EINTR while waitpid()");
148 if (WIFEXITED(status)) {
149 AGL_DEBUG("%s terminated, return %d", m_args_v[0].c_str(),
150 WEXITSTATUS(status));
152 if (WIFSIGNALED(status)) {
153 AGL_DEBUG("%s terminated by signal %d", m_args_v[0].c_str(),
159 /* parent killed by someone, so need to kill children */
160 AGL_DEBUG("killpg(0, SIGTERM)");
165 int AFMDBusLauncher::get_dbus_message_bus (GBusType bus_type,
166 GDBusConnection * &conn)
170 conn = g_bus_get_sync(bus_type, NULL, &err);
172 AGL_WARN("Failed to get session bus: %s", err->message);
173 g_clear_error (&err);
180 int AFMDBusLauncher::launch (std::string &name)
184 GDBusConnection* conn;
188 const char* xdg_app = name.c_str();
190 if (get_dbus_message_bus(G_BUS_TYPE_SESSION, conn)) {
194 msg = g_dbus_message_new_method_call (
201 AGL_WARN("Failed to allocate the dbus message");
202 g_object_unref(conn);
206 g_dbus_message_set_body(msg, g_variant_new("(s)", xdg_app));
208 re = g_dbus_connection_send_message_with_reply_sync (
209 conn, msg, G_DBUS_SEND_MESSAGE_FLAGS_NONE, -1, NULL, NULL, &err);
212 AGL_WARN("unable to send message: %s", err->message);
214 g_object_unref(conn);
219 g_dbus_connection_flush_sync(conn, NULL, &err);
221 AGL_WARN("unable to flush message queue: %s", err->message);
222 g_object_unref(conn);
228 body = g_dbus_message_get_body(re);
229 g_variant_get(body, "(&s)", &val);
231 AGL_DEBUG("dbus message get (%s)", val);
233 pid_t rid = std::stol(std::string(val));
234 AGL_DEBUG("RID = %d", rid);
236 g_object_unref(conn);
243 volatile sig_atomic_t e_flag = 0;
245 static void sigterm_handler (int signum)
250 static void init_signal (void)
252 struct sigaction act, info;
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.");
263 act.sa_handler = &sigterm_handler;
264 if (sigemptyset(&act.sa_mask) != 0) {
265 AGL_FATAL("Cannot initialize sigaction");
269 if (sigaction(SIGTERM, &act, &info) != 0) {
270 AGL_FATAL("Cannot register signal handler for SIGTERM");
274 int RunXDG::init_wm (void)
276 m_wm = new LibWindowmanager();
277 if (m_wm->init(m_port, m_token.c_str())) {
278 AGL_DEBUG("cannot initialize windowmanager");
282 std::function< void(json_object*) > h_active = [](json_object* object) {
283 AGL_DEBUG("Got Event_Active");
286 std::function< void(json_object*) > h_inactive = [](json_object* object) {
287 AGL_DEBUG("Got Event_Inactive");
290 std::function< void(json_object*) > h_visible = [](json_object* object) {
291 AGL_DEBUG("Got Event_Visible");
294 std::function< void(json_object*) > h_invisible = [](json_object* object) {
295 AGL_DEBUG("Got Event_Invisible");
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);
307 std::function< void(json_object*) > h_flushdraw= [](json_object* object) {
308 AGL_DEBUG("Got Event_FlushDraw");
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);
321 int RunXDG::init_hs (void)
323 m_hs = new LibHomeScreen();
324 if (m_hs->init(m_port, m_token.c_str())) {
325 AGL_DEBUG("cannot initialize homescreen");
329 std::function< void(json_object*) > handler = [this] (json_object* object) {
332 if (json_object_object_get_ex(object, "application_name", &val)) {
333 const char *name = json_object_get_string(val);
335 AGL_DEBUG("Event_TapShortcut <%s>", name);
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());
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"));
347 this->m_wm->activateSurface(obj);
351 m_hs->set_event_handler(LibHomeScreen::Event_TapShortcut, handler);
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);
357 m_hs->set_event_handler(LibHomeScreen::Event_OnScreenMessage, h_default);
362 int RunXDG::parse_config (const char *path_to_config)
364 auto config = cpptoml::parse_file(path_to_config);
366 if (config == nullptr) {
367 AGL_DEBUG("cannot parse %s", path_to_config);
371 AGL_DEBUG("[%s] parsed", path_to_config);
373 auto app = config->get_table("application");
374 if (app == nullptr) {
375 AGL_DEBUG("cannto find [application]");
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");
385 std::string method = *(app->get_as<std::string>("method"));
386 if (method.empty()) {
387 method = std::string("POSIX");
392 /* Setup API of launcher */
393 if (method == "POSIX") {
394 pl = new POSIXLauncher();
396 } else if (method == "AFM_DBUS") {
397 m_launcher = new AFMDBusLauncher();
399 } else if (method == "AFM_WEBSOCKET") {
400 m_launcher = new AFMWebSocketLauncher();
403 AGL_FATAL("Unknown type of launcher");
407 pl->m_args_v.push_back(m_path);
410 auto params = app->get_array_of<std::string>("params");
411 for (const auto& param : *params)
413 pl->m_args_v.push_back(param);
414 AGL_DEBUG("params[%s]", param.c_str());
420 RunXDG::RunXDG (int port, const char* token, const char* id)
422 m_id = std::string(id);
424 m_token = std::string(token);
427 auto path = std::string(getenv("AFM_APP_INSTALL_DIR"));
428 path = path + "/" + RUNXDG_CONFIG;
430 // Parse config file of runxdg
431 if (parse_config(path.c_str())) {
432 AGL_FATAL("Error in config");
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());
439 // Setup HomeScreen/WindowManager API
441 AGL_FATAL("cannot setup wm API");
444 AGL_FATAL("cannot setup hs API");
446 // Setup ilmController API
447 m_ic = new ILMControl(notify_ivi_control_cb_static, this);
449 AGL_DEBUG("RunXDG created.");
452 void RunXDG::setup_surface (int id)
454 std::string sid = std::to_string(id);
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()));
463 AGL_DEBUG("requestSurfaceXDG(%s,%s)", m_role.c_str(), sid.c_str());
464 m_wm->requestSurfaceXDG(obj);
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;
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);
480 void POSIXLauncher::register_surfpid (pid_t surf_pid)
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));
492 void POSIXLauncher::unregister_surfpid (pid_t surf_pid)
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++);
504 pid_t POSIXLauncher::find_surfpid_by_rid (pid_t rid)
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);
515 void AFMLauncher::register_surfpid (pid_t surf_pid)
519 pgid = getpgid(surf_pid);
522 AGL_DEBUG("fail to get process group id");
526 AGL_DEBUG("Surface creator is pid=%d, pgid=%d", surf_pid, pgid);
528 if (!m_pgids.count(pgid)) {
529 m_pgids[pgid] = surf_pid;
533 void AFMLauncher::unregister_surfpid (pid_t surf_pid)
535 auto itr = m_pgids.begin();
536 while (itr != m_pgids.end()) {
537 if (itr->second == surf_pid) {
538 m_pgids.erase(itr++);
545 pid_t AFMLauncher::find_surfpid_by_rid (pid_t rid)
547 auto itr = m_pgids.find(rid);
548 if (itr != m_pgids.end())
554 void RunXDG::start (void)
556 // Initialize SIGTERM handler
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);
565 // take care 1st time launch
566 AGL_DEBUG("waiting for notification: surafce created");
567 m_pending_create = true;
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);
573 AGL_DEBUG("match: surf:pid=%d, afm:rid=%d", surf_pid,
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,
584 m_launcher->loop(e_flag);
587 int main (int argc, const char* argv[])
590 // setenv("USE_HMI_DEBUG", "5", 1);
591 // setenv("WAYLAND_DEBUG", "1", 1);
598 AGL_FATAL("Missing port and token");
602 const char *afm_id = getenv("AFM_ID");
603 if (afm_id == NULL || !afm_id[0]) {
608 port = std::stol(argv[1]);
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");
616 RunXDG runxdg(port, token, afm_id);