Bug Fix: Window Manager doesn't react after killing app process
[apps/agl-service-windowmanager-2017.git] / src / main.cpp
1 /*
2  * Copyright (c) 2017 TOYOTA MOTOR CORPORATION
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include <unistd.h>
18 #include <algorithm>
19 #include <mutex>
20 #include <json.h>
21 #include "../include/json.hpp"
22 #include "app.hpp"
23 #include "json_helper.hpp"
24 #include "wayland_ivi_wm.hpp"
25
26 extern "C"
27 {
28 #include <afb/afb-binding.h>
29 #include <systemd/sd-event.h>
30 }
31
32 typedef struct WMClientCtxt
33 {
34     std::string name;
35     std::string role;
36     WMClientCtxt(const char *appName, const char* appRole)
37     {
38         name = appName;
39         role = appRole;
40     }
41 } WMClientCtxt;
42
43 struct afb_instance
44 {
45     std::unique_ptr<wl::display> display;
46     wm::App app;
47
48     afb_instance() : display{new wl::display}, app{this->display.get()} {}
49
50     int init();
51 };
52
53 struct afb_instance *g_afb_instance;
54 std::mutex binding_m;
55
56 int afb_instance::init()
57 {
58     return this->app.init();
59 }
60
61 int display_event_callback(sd_event_source *evs, int /*fd*/, uint32_t events,
62                            void * /*data*/)
63 {
64     ST();
65
66     if ((events & EPOLLHUP) != 0)
67     {
68         HMI_ERROR("wm", "The compositor hung up, dying now.");
69         delete g_afb_instance;
70         g_afb_instance = nullptr;
71         goto error;
72     }
73
74     if ((events & EPOLLIN) != 0u)
75     {
76         {
77             STN(display_read_events);
78             g_afb_instance->app.display->read_events();
79             g_afb_instance->app.set_pending_events();
80         }
81         {
82             // We want do dispatch pending wayland events from within
83             // the API context
84             STN(winman_ping_api_call);
85             afb_service_call("windowmanager", "ping", json_object_new_object(),
86                             [](void *c, int st, json_object *j) {
87                                 STN(winman_ping_api_call_return);
88                             },
89                             nullptr);
90         }
91     }
92
93     return 0;
94
95 error:
96     sd_event_source_unref(evs);
97     if (getenv("WINMAN_EXIT_ON_HANGUP") != nullptr)
98     {
99         exit(1);
100     }
101     return -1;
102 }
103
104 int _binding_init()
105 {
106     HMI_NOTICE("wm", "WinMan ver. %s", WINMAN_VERSION_STRING);
107
108     if (g_afb_instance != nullptr)
109     {
110         HMI_ERROR("wm", "Wayland context already initialized?");
111         return 0;
112     }
113
114     if (getenv("XDG_RUNTIME_DIR") == nullptr)
115     {
116         HMI_ERROR("wm", "Environment variable XDG_RUNTIME_DIR not set");
117         goto error;
118     }
119
120     {
121         // wait until wayland compositor starts up.
122         int cnt = 0;
123         g_afb_instance = new afb_instance;
124         while (!g_afb_instance->display->ok())
125         {
126             cnt++;
127             if (20 <= cnt)
128             {
129                 HMI_ERROR("wm", "Could not connect to compositor");
130                 goto error;
131             }
132             HMI_ERROR("wm", "Wait to start weston ...");
133             sleep(1);
134             delete g_afb_instance;
135             g_afb_instance = new afb_instance;
136         }
137     }
138
139     if (g_afb_instance->init() == -1)
140     {
141         HMI_ERROR("wm", "Could not connect to compositor");
142         goto error;
143     }
144
145     {
146         int ret = sd_event_add_io(afb_daemon_get_event_loop(), nullptr,
147                                   g_afb_instance->display->get_fd(), EPOLLIN,
148                                   display_event_callback, g_afb_instance);
149         if (ret < 0)
150         {
151             HMI_ERROR("wm", "Could not initialize afb_instance event handler: %d", -ret);
152             goto error;
153         }
154     }
155
156     atexit([] { delete g_afb_instance; });
157
158     return 0;
159
160 error:
161     delete g_afb_instance;
162     g_afb_instance = nullptr;
163     return -1;
164 }
165
166 int binding_init() noexcept
167 {
168     try
169     {
170         return _binding_init();
171     }
172     catch (std::exception &e)
173     {
174         HMI_ERROR("wm", "Uncaught exception in binding_init(): %s", e.what());
175     }
176     return -1;
177 }
178
179 static bool checkFirstReq(afb_req req)
180 {
181     WMClientCtxt *ctxt = (WMClientCtxt *)afb_req_context_get(req);
182     return (ctxt) ? false : true;
183 }
184
185 static void cbRemoveClientCtxt(void *data)
186 {
187     WMClientCtxt *ctxt = (WMClientCtxt *)data;
188     if (ctxt == nullptr)
189     {
190         return;
191     }
192     HMI_DEBUG("wm", "remove app %s", ctxt->name.c_str());
193     // Lookup surfaceID and remove it because App is dead.
194     auto pSid = g_afb_instance->app.id_alloc.lookup(ctxt->role.c_str());
195     if (pSid)
196     {
197         auto sid = *pSid;
198         auto o_state = *g_afb_instance->app.layers.get_layout_state(sid);
199         if (o_state != nullptr)
200         {
201             if (o_state->main == sid)
202             {
203                 o_state->main = -1;
204             }
205             else if (o_state->sub == sid)
206             {
207                 o_state->sub = -1;
208             }
209         }
210         g_afb_instance->app.id_alloc.remove_id(sid);
211         g_afb_instance->app.layers.remove_surface(sid);
212         g_afb_instance->app.controller->sprops.erase(sid);
213         g_afb_instance->app.controller->surfaces.erase(sid);
214         HMI_DEBUG("wm", "delete surfaceID %d", sid);
215     }
216     delete ctxt;
217 }
218
219 void windowmanager_requestsurface(afb_req req) noexcept
220 {
221     std::lock_guard<std::mutex> guard(binding_m);
222 #ifdef ST
223     ST();
224 #endif
225     if (g_afb_instance == nullptr)
226     {
227         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
228         return;
229     }
230
231     try
232     {
233         const char *a_drawing_name = afb_req_value(req, "drawing_name");
234         if (!a_drawing_name)
235         {
236             afb_req_fail(req, "failed", "Need char const* argument drawing_name");
237             return;
238         }
239
240         /* Create Security Context */
241         bool isFirstReq = checkFirstReq(req);
242         if (!isFirstReq)
243         {
244             WMClientCtxt *ctxt = (WMClientCtxt *)afb_req_context_get(req);
245             HMI_DEBUG("wm", "You're %s.", ctxt->name.c_str());
246             if (ctxt->name != std::string(a_drawing_name))
247             {
248                 afb_req_fail_f(req, "failed", "Don't request with other name: %s for now", a_drawing_name);
249                 HMI_DEBUG("wm", "Don't request with other name: %s for now", a_drawing_name);
250                 return;
251             }
252         }
253
254         auto ret = g_afb_instance->app.api_request_surface(a_drawing_name);
255
256         if (isFirstReq)
257         {
258             WMClientCtxt *ctxt = new WMClientCtxt(afb_req_get_application_id(req), a_drawing_name);
259             HMI_DEBUG("wm", "create session for %s", ctxt->name.c_str());
260             afb_req_session_set_LOA(req, 1);
261             afb_req_context_set(req, ctxt, cbRemoveClientCtxt);
262         }
263         else
264         {
265             HMI_DEBUG("wm", "session already created for %s", a_drawing_name);
266         }
267
268         if (ret.is_err())
269         {
270             afb_req_fail(req, "failed", ret.unwrap_err());
271             return;
272         }
273
274         afb_req_success(req, json_object_new_int(ret.unwrap()), "success");
275     }
276     catch (std::exception &e)
277     {
278         afb_req_fail_f(req, "failed", "Uncaught exception while calling requestsurface: %s", e.what());
279         return;
280     }
281 }
282
283 void windowmanager_requestsurfacexdg(afb_req req) noexcept
284 {
285     std::lock_guard<std::mutex> guard(binding_m);
286 #ifdef ST
287     ST();
288 #endif
289     if (g_afb_instance == nullptr)
290     {
291         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
292         return;
293     }
294
295     try
296     {
297         json_object *jreq = afb_req_json(req);
298
299         json_object *j_drawing_name = nullptr;
300         if (!json_object_object_get_ex(jreq, "drawing_name", &j_drawing_name))
301         {
302             afb_req_fail(req, "failed", "Need char const* argument drawing_name");
303             return;
304         }
305         char const *a_drawing_name = json_object_get_string(j_drawing_name);
306
307         json_object *j_ivi_id = nullptr;
308         if (!json_object_object_get_ex(jreq, "ivi_id", &j_ivi_id))
309         {
310             afb_req_fail(req, "failed", "Need char const* argument ivi_id");
311             return;
312         }
313         char const *a_ivi_id = json_object_get_string(j_ivi_id);
314
315         auto ret = g_afb_instance->app.api_request_surface(a_drawing_name, a_ivi_id);
316         if (ret != nullptr)
317         {
318             afb_req_fail(req, "failed", ret);
319             return;
320         }
321
322         afb_req_success(req, NULL, "success");
323     }
324     catch (std::exception &e)
325     {
326         afb_req_fail_f(req, "failed", "Uncaught exception while calling requestsurfacexdg: %s", e.what());
327         return;
328     }
329 }
330
331 void windowmanager_activatesurface(afb_req req) noexcept
332 {
333     std::lock_guard<std::mutex> guard(binding_m);
334 #ifdef ST
335     ST();
336 #endif
337     if (g_afb_instance == nullptr)
338     {
339         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
340         return;
341     }
342
343     try
344     {
345         const char *a_drawing_name = afb_req_value(req, "drawing_name");
346         if (!a_drawing_name)
347         {
348             afb_req_fail(req, "failed", "Need char const* argument drawing_name");
349             return;
350         }
351
352         const char *a_drawing_area = afb_req_value(req, "drawing_area");
353         if (!a_drawing_area)
354         {
355             afb_req_fail(req, "failed", "Need char const* argument drawing_area");
356             return;
357         }
358
359         g_afb_instance->app.api_activate_surface(a_drawing_name, a_drawing_area,
360                     [&req](const char *errmsg) {
361                         if (errmsg != nullptr)
362                         {
363                             HMI_ERROR("wm", errmsg);
364                             afb_req_fail(req, "failed", errmsg);
365                             return;
366                         }
367                         afb_req_success(req, NULL, "success");
368                     });
369     }
370     catch (std::exception &e)
371     {
372         HMI_WARNING("wm", "failed", "Uncaught exception while calling activatesurface: %s", e.what());
373         return;
374     }
375 }
376
377 void windowmanager_deactivatesurface(afb_req req) noexcept
378 {
379     std::lock_guard<std::mutex> guard(binding_m);
380 #ifdef ST
381     ST();
382 #endif
383     if (g_afb_instance == nullptr)
384     {
385         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
386         return;
387     }
388
389     try
390     {
391         const char *a_drawing_name = afb_req_value(req, "drawing_name");
392         if (!a_drawing_name)
393         {
394             afb_req_fail(req, "failed", "Need char const* argument drawing_name");
395             return;
396         }
397
398         g_afb_instance->app.api_deactivate_surface(a_drawing_name,
399                     [&req](const char *errmsg) {
400                         if (errmsg != nullptr)
401                         {
402                             HMI_ERROR("wm", errmsg);
403                             afb_req_fail(req, "failed", errmsg);
404                             return;
405                         }
406                         afb_req_success(req, NULL, "success");
407                     });
408     }
409     catch (std::exception &e)
410     {
411         HMI_WARNING("wm", "Uncaught exception while calling deactivatesurface: %s", e.what());
412         return;
413     }
414 }
415
416 void windowmanager_enddraw(afb_req req) noexcept
417 {
418     std::lock_guard<std::mutex> guard(binding_m);
419 #ifdef ST
420     ST();
421 #endif
422     if (g_afb_instance == nullptr)
423     {
424         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
425         return;
426     }
427
428     try
429     {
430         const char *a_drawing_name = afb_req_value(req, "drawing_name");
431         if (!a_drawing_name)
432         {
433             afb_req_fail(req, "failed", "Need char const* argument drawing_name");
434             return;
435         }
436         afb_req_success(req, NULL, "success");
437
438         g_afb_instance->app.api_enddraw(a_drawing_name);
439     }
440     catch (std::exception &e)
441     {
442         HMI_WARNING("wm", "failed", "Uncaught exception while calling enddraw: %s", e.what());
443         return;
444     }
445 }
446
447 void windowmanager_getdisplayinfo_thunk(afb_req req) noexcept
448 {
449     std::lock_guard<std::mutex> guard(binding_m);
450 #ifdef ST
451     ST();
452 #endif
453     if (g_afb_instance == nullptr)
454     {
455         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
456         return;
457     }
458
459     try
460     {
461         auto ret = g_afb_instance->app.api_get_display_info();
462         if (ret.is_err())
463         {
464             afb_req_fail(req, "failed", ret.unwrap_err());
465             return;
466         }
467
468         afb_req_success(req, ret.unwrap(), "success");
469     }
470     catch (std::exception &e)
471     {
472         afb_req_fail_f(req, "failed", "Uncaught exception while calling getdisplayinfo: %s", e.what());
473         return;
474     }
475 }
476
477 void windowmanager_getareainfo_thunk(afb_req req) noexcept
478 {
479     std::lock_guard<std::mutex> guard(binding_m);
480 #ifdef ST
481     ST();
482 #endif
483     if (g_afb_instance == nullptr)
484     {
485         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
486         return;
487     }
488
489     try
490     {
491         json_object *jreq = afb_req_json(req);
492
493         json_object *j_drawing_name = nullptr;
494         if (!json_object_object_get_ex(jreq, "drawing_name", &j_drawing_name))
495         {
496             afb_req_fail(req, "failed", "Need char const* argument drawing_name");
497             return;
498         }
499         char const *a_drawing_name = json_object_get_string(j_drawing_name);
500
501         auto ret = g_afb_instance->app.api_get_area_info(a_drawing_name);
502         if (ret.is_err())
503         {
504             afb_req_fail(req, "failed", ret.unwrap_err());
505             return;
506         }
507
508         afb_req_success(req, ret.unwrap(), "success");
509     }
510     catch (std::exception &e)
511     {
512         afb_req_fail_f(req, "failed", "Uncaught exception while calling getareainfo: %s", e.what());
513         return;
514     }
515 }
516
517 void windowmanager_wm_subscribe(afb_req req) noexcept
518 {
519     std::lock_guard<std::mutex> guard(binding_m);
520 #ifdef ST
521     ST();
522 #endif
523     if (g_afb_instance == nullptr)
524     {
525         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
526         return;
527     }
528
529     try
530     {
531         json_object *jreq = afb_req_json(req);
532         json_object *j = nullptr;
533         if (!json_object_object_get_ex(jreq, "event", &j))
534         {
535             afb_req_fail(req, "failed", "Need char const* argument event");
536             return;
537         }
538         int event_type = json_object_get_int(j);
539         const char *event_name = g_afb_instance->app.kListEventName[event_type];
540         struct afb_event event = g_afb_instance->app.map_afb_event[event_name];
541         int ret = afb_req_subscribe(req, event);
542         if (ret)
543         {
544             afb_req_fail(req, "failed", "Error: afb_req_subscribe()");
545             return;
546         }
547         afb_req_success(req, NULL, "success");
548     }
549     catch (std::exception &e)
550     {
551         afb_req_fail_f(req, "failed", "Uncaught exception while calling wm_subscribe: %s", e.what());
552         return;
553     }
554 }
555
556 void windowmanager_list_drawing_names(afb_req req) noexcept
557 {
558     std::lock_guard<std::mutex> guard(binding_m);
559 #ifdef ST
560     ST();
561 #endif
562     if (g_afb_instance == nullptr)
563     {
564         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
565         return;
566     }
567
568     try
569     {
570
571         nlohmann::json j = g_afb_instance->app.id_alloc.name2id;
572         auto ret = wm::Ok(json_tokener_parse(j.dump().c_str()));
573         if (ret.is_err())
574         {
575             afb_req_fail(req, "failed", ret.unwrap_err());
576             return;
577         }
578
579         afb_req_success(req, ret.unwrap(), "success");
580     }
581     catch (std::exception &e)
582     {
583         afb_req_fail_f(req, "failed", "Uncaught exception while calling list_drawing_names: %s", e.what());
584         return;
585     }
586 }
587
588 void windowmanager_ping(afb_req req) noexcept
589 {
590     std::lock_guard<std::mutex> guard(binding_m);
591 #ifdef ST
592     ST();
593 #endif
594     if (g_afb_instance == nullptr)
595     {
596         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
597         return;
598     }
599
600     try
601     {
602
603         g_afb_instance->app.api_ping();
604
605         afb_req_success(req, NULL, "success");
606     }
607     catch (std::exception &e)
608     {
609         afb_req_fail_f(req, "failed", "Uncaught exception while calling ping: %s", e.what());
610         return;
611     }
612 }
613
614 void windowmanager_debug_status(afb_req req) noexcept
615 {
616     std::lock_guard<std::mutex> guard(binding_m);
617 #ifdef ST
618     ST();
619 #endif
620     if (g_afb_instance == nullptr)
621     {
622         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
623         return;
624     }
625
626     try
627     {
628
629         json_object *jr = json_object_new_object();
630         json_object_object_add(jr, "surfaces",
631                                to_json(g_afb_instance->app.controller->sprops));
632         json_object_object_add(jr, "layers", to_json(g_afb_instance->app.controller->lprops));
633
634         afb_req_success(req, jr, "success");
635     }
636     catch (std::exception &e)
637     {
638         afb_req_fail_f(req, "failed", "Uncaught exception while calling debug_status: %s", e.what());
639         return;
640     }
641 }
642
643 void windowmanager_debug_layers(afb_req req) noexcept
644 {
645     std::lock_guard<std::mutex> guard(binding_m);
646 #ifdef ST
647     ST();
648 #endif
649     if (g_afb_instance == nullptr)
650     {
651         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
652         return;
653     }
654
655     try
656     {
657         auto ret = wm::Ok(json_tokener_parse(g_afb_instance->app.layers.to_json().dump().c_str()));
658
659         afb_req_success(req, ret, "success");
660     }
661     catch (std::exception &e)
662     {
663         afb_req_fail_f(req, "failed", "Uncaught exception while calling debug_layers: %s", e.what());
664         return;
665     }
666 }
667
668 void windowmanager_debug_surfaces(afb_req req) noexcept
669 {
670     std::lock_guard<std::mutex> guard(binding_m);
671 #ifdef ST
672     ST();
673 #endif
674     if (g_afb_instance == nullptr)
675     {
676         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
677         return;
678     }
679
680     try
681     {
682
683         auto ret = wm::Ok(to_json(g_afb_instance->app.controller->sprops));
684         if (ret.is_err())
685         {
686             afb_req_fail(req, "failed", ret.unwrap_err());
687             return;
688         }
689
690         afb_req_success(req, ret.unwrap(), "success");
691     }
692     catch (std::exception &e)
693     {
694         afb_req_fail_f(req, "failed", "Uncaught exception while calling debug_surfaces: %s", e.what());
695         return;
696     }
697 }
698
699 void windowmanager_debug_terminate(afb_req req) noexcept
700 {
701     std::lock_guard<std::mutex> guard(binding_m);
702 #ifdef ST
703     ST();
704 #endif
705     if (g_afb_instance == nullptr)
706     {
707         afb_req_fail(req, "failed", "Binding not initialized, did the compositor die?");
708         return;
709     }
710
711     try
712     {
713
714         if (getenv("WINMAN_DEBUG_TERMINATE") != nullptr)
715         {
716             raise(SIGKILL); // afb-daemon kills it's pgroup using TERM, which
717                             // doesn't play well with perf
718         }
719
720         afb_req_success(req, NULL, "success");
721     }
722     catch (std::exception &e)
723     {
724         afb_req_fail_f(req, "failed", "Uncaught exception while calling debug_terminate: %s", e.what());
725         return;
726     }
727 }
728
729 const struct afb_verb_v2 windowmanager_verbs[] = {
730     {"requestsurface", windowmanager_requestsurface, nullptr, nullptr, AFB_SESSION_NONE},
731     {"requestsurfacexdg", windowmanager_requestsurfacexdg, nullptr, nullptr, AFB_SESSION_NONE},
732     {"activatesurface", windowmanager_activatesurface, nullptr, nullptr, AFB_SESSION_NONE},
733     {"deactivatesurface", windowmanager_deactivatesurface, nullptr, nullptr, AFB_SESSION_NONE},
734     {"enddraw", windowmanager_enddraw, nullptr, nullptr, AFB_SESSION_NONE},
735     {"getdisplayinfo", windowmanager_getdisplayinfo_thunk, nullptr, nullptr, AFB_SESSION_NONE},
736     {"getareainfo", windowmanager_getareainfo_thunk, nullptr, nullptr, AFB_SESSION_NONE},
737     {"wm_subscribe", windowmanager_wm_subscribe, nullptr, nullptr, AFB_SESSION_NONE},
738     {"list_drawing_names", windowmanager_list_drawing_names, nullptr, nullptr, AFB_SESSION_NONE},
739     {"ping", windowmanager_ping, nullptr, nullptr, AFB_SESSION_NONE},
740     {"debug_status", windowmanager_debug_status, nullptr, nullptr, AFB_SESSION_NONE},
741     {"debug_layers", windowmanager_debug_layers, nullptr, nullptr, AFB_SESSION_NONE},
742     {"debug_surfaces", windowmanager_debug_surfaces, nullptr, nullptr, AFB_SESSION_NONE},
743     {"debug_terminate", windowmanager_debug_terminate, nullptr, nullptr, AFB_SESSION_NONE},
744     {}};
745
746 extern "C" const struct afb_binding_v2 afbBindingV2 = {
747     "windowmanager", nullptr, nullptr, windowmanager_verbs, nullptr, binding_init, nullptr, 0};