55010a2deb57f1e0da7678f4be6d6640c0275cc4
[src/app-framework-main.git] / src / afm-binding.c
1 /*
2  * Copyright (C) 2015-2020 "IoT.bzh"
3  * Author José Bollo <jose.bollo@iot.bzh>
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *   http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17
18 #define _GNU_SOURCE         /* See feature_test_macros(7) */
19 #include <stdlib.h>
20 #include <stdio.h>
21 #include <string.h>
22 #include <assert.h>
23 #include <signal.h>
24 #include <errno.h>
25
26 #include <json-c/json.h>
27
28 #define AFB_BINDING_VERSION 3
29 #include <afb/afb-binding.h>
30
31 #include "verbose.h"
32 #include "utils-systemd.h"
33 #include "afm-udb.h"
34 #include "afm-urun.h"
35 #include "wgt-info.h"
36 #include "wgtpkg-install.h"
37 #include "wgtpkg-uninstall.h"
38 #include "wrap-json.h"
39
40 /*
41  * constant strings
42  */
43 static const char _added_[]     = "added";
44 static const char _all_[]       = "all";
45 static const char _a_l_c_[]     = "application-list-changed";
46 static const char _bad_request_[] = "bad-request";
47 static const char _cannot_start_[] = "cannot-start";
48 static const char _detail_[]    = "detail";
49 static const char _id_[]        = "id";
50 static const char _install_[]   = "install";
51 static const char _lang_[]      = "lang";
52 static const char _not_found_[] = "not-found";
53 static const char _not_running_[] = "not-running";
54 static const char _once_[]      = "once";
55 static const char _pause_[]     = "pause";
56 static const char _resume_[]    = "resume";
57 static const char _runid_[]     = "runid";
58 static const char _runnables_[] = "runnables";
59 static const char _runners_[]   = "runners";
60 static const char _start_[]     = "start";
61 static const char _state_[]     = "state";
62 static const char _terminate_[] = "terminate";
63 static const char _uninstall_[] = "uninstall";
64 static const char _update_[]    = "update";
65
66 /*
67  * the permissions
68  */
69 static const struct afb_auth
70         auth_perm_widget = {
71                 .type = afb_auth_Permission,
72                 .text = FWK_PREFIX"permission:afm:system:widget"
73         },
74         auth_perm_widget_install = {
75                 .type = afb_auth_Permission,
76                 .text = FWK_PREFIX"permission:afm:system:widget:install"
77         },
78         auth_perm_widget_uninstall = {
79                 .type = afb_auth_Permission,
80                 .text = FWK_PREFIX"permission:afm:system:widget:uninstall"
81         },
82         auth_perm_widget_preinstall = {
83                 .type = afb_auth_Permission,
84                 .text = FWK_PREFIX"permission:afm:system:widget:preinstall"
85         },
86         auth_perm_widget_detail = {
87                 .type = afb_auth_Permission,
88                 .text = FWK_PREFIX"permission:afm:system:widget:detail"
89         },
90         auth_perm_widget_start = {
91                 .type = afb_auth_Permission,
92                 .text = FWK_PREFIX"permission:afm:system:widget:start"
93         },
94         auth_perm_widget_view_all = {
95                 .type = afb_auth_Permission,
96                 .text = FWK_PREFIX"permission:afm:system:widget:view-all"
97         },
98         auth_perm_runner = {
99                 .type = afb_auth_Permission,
100                 .text = FWK_PREFIX"permission:afm:system:runner"
101         },
102         auth_perm_runner_state = {
103                 .type = afb_auth_Permission,
104                 .text = FWK_PREFIX"permission:afm:system:runner:state"
105         },
106         auth_perm_runner_kill = {
107                 .type = afb_auth_Permission,
108                 .text = FWK_PREFIX"permission:afm:system:runner:kill"
109         },
110
111         auth_install = {
112                 .type = afb_auth_Or,
113                 .first = &auth_perm_widget,
114                 .next = &auth_perm_widget_install
115         },
116         auth_uninstall = {
117                 .type = afb_auth_Or,
118                 .first = &auth_perm_widget,
119                 .next = &auth_perm_widget_uninstall
120         },
121         auth_preinstall = {
122                 .type = afb_auth_Or,
123                 .first = &auth_perm_widget,
124                 .next = &auth_perm_widget_preinstall
125         },
126         auth_detail = {
127                 .type = afb_auth_Or,
128                 .first = &auth_perm_widget,
129                 .next = &auth_perm_widget_detail
130         },
131         auth_start = {
132                 .type = afb_auth_Or,
133                 .first = &auth_perm_widget,
134                 .next = &auth_perm_widget_start
135         },
136         auth_view_all = {
137                 .type = afb_auth_Or,
138                 .first = &auth_perm_widget,
139                 .next = &auth_perm_widget_view_all
140         },
141         auth_state = {
142                 .type = afb_auth_Or,
143                 .first = &auth_perm_runner,
144                 .next = &auth_perm_runner_state
145         },
146         auth_kill = {
147                 .type = afb_auth_Or,
148                 .first = &auth_perm_runner,
149                 .next = &auth_perm_runner_kill
150         }
151 ;
152
153 /*
154  * default root
155  */
156 static const char *rootdir = FWK_APP_DIR;
157
158 /*
159  * the internal application database
160  */
161 static struct afm_udb *afudb;
162
163 /*
164  * the event signaling that application list changed
165  */
166 static afb_event_t applist_changed_event;
167
168 /*
169  * the preallocated true json_object
170  */
171 static struct json_object *json_true;
172
173 /* enforce daemon reload */
174 static void do_reloads()
175 {
176         systemd_daemon_reload(0);
177         systemd_unit_restart_name(0, "sockets.target");
178 }
179
180 /* common bad request reply */
181 static void bad_request(afb_req_t req)
182 {
183         afb_req_fail(req, _bad_request_, NULL);
184 }
185
186 /* common not found reply */
187 static void not_found(afb_req_t req)
188 {
189         afb_req_fail(req, _not_found_, NULL);
190 }
191
192 /* common not running reply */
193 static void not_running(afb_req_t req)
194 {
195         afb_req_fail(req, _not_running_, NULL);
196 }
197
198 /* common can't start reply */
199 static void cant_start(afb_req_t req)
200 {
201         afb_req_fail(req, _cannot_start_, NULL);
202 }
203
204 /*
205  * Broadcast the event "application-list-changed".
206  * This event is sent was the event "changed" is received from dbus.
207  */
208 static void application_list_changed(const char *operation, const char *data)
209 {
210         struct json_object *e = NULL;
211         wrap_json_pack(&e, "{ss ss}", "operation", operation, "data", data);
212         afb_event_broadcast(applist_changed_event, e);
213 }
214
215 /*
216  * Retrieve the required language from 'req'.
217  */
218 static const char *get_lang(afb_req_t req)
219 {
220         const char *lang;
221
222         /* get the optional language */
223         lang = afb_req_value(req, _lang_);
224
225         /* TODO use the req to get the lang of the session (if any) */
226
227         return lang;
228 }
229
230 /*
231  * Retrieve whether all is required from 'req'.
232  */
233 static int get_all(afb_req_t req)
234 {
235         struct json_object *val;
236
237         /* get the optional language */
238         return json_object_object_get_ex(afb_req_json(req), _all_, &val)
239                 && json_object_get_boolean(val);
240 }
241
242
243 /*
244  * retrieves the 'appid' in parameters received with the
245  * request 'req' for the 'method'.
246  *
247  * Returns 1 in case of success.
248  * Otherwise, if the 'appid' can't be retrieved, an error stating
249  * the bad request is replied for 'req' and 0 is returned.
250  */
251 static int onappid(afb_req_t req, const char *method, const char **appid)
252 {
253         struct json_object *json;
254
255         /* get the paramaters of the request */
256         json = afb_req_json(req);
257
258         /* get the appid if any */
259         if (!wrap_json_unpack(json, "s", appid)
260          || !wrap_json_unpack(json, "{ss}", _id_, appid)) {
261                 /* found */
262                 INFO("method %s called for %s", method, *appid);
263                 return 1;
264         }
265
266         /* nothing appropriate */
267         INFO("bad request method %s: %s", method,
268                                         json_object_to_json_string(json));
269         bad_request(req);
270         return 0;
271 }
272
273 /*
274  * retrieves the 'runid' in parameters received with the
275  * request 'req' for the 'method'.
276  *
277  * Returns 1 in case of success.
278  * Otherwise, if the 'runid' can't be retrieved, an error stating
279  * the bad request is replied for 'req' and 0 is returned.
280  */
281 static int onrunid(afb_req_t req, const char *method, int *runid)
282 {
283         struct json_object *json;
284         const char *appid;
285
286         /* get the paramaters of the request */
287         json = afb_req_json(req);
288
289         /* get the runid if any */
290         if (!wrap_json_unpack(json, "i", runid)
291          || !wrap_json_unpack(json, "{si}", _runid_, runid)) {
292                 INFO("method %s called for %d", method, *runid);
293                 return 1;
294         }
295
296         /* get the appid if any */
297         if (!onappid(req, method, &appid))
298                 return 0;
299
300         /* search the runid of the appid */
301         *runid = afm_urun_search_runid(afudb, appid, afb_req_get_uid(req));
302         if (*runid < 0) {
303                 /* nothing appropriate */
304                 INFO("method %s can't get runid for %s: %m", method,
305                                                         appid);
306                 if (errno == ESRCH)
307                         not_running(req);
308                 else
309                         not_found(req);
310                 return 0;
311         }
312
313         /* found */
314         INFO("method %s called for %s -> %d", method, appid, *runid);
315         return 1;
316 }
317
318 /*
319  * Sends the reply 'resp' to the request 'req' if 'resp' is not NULLzero.
320  * Otherwise, when 'resp' is NULL replies the error string 'errstr'.
321  */
322 static void reply(afb_req_t req, struct json_object *resp)
323 {
324         if (resp)
325                 afb_req_reply(req, resp, NULL, NULL);
326         else
327                 afb_req_reply(req, NULL, "failed", strerror(errno));
328 }
329
330 /*
331  * Sends the reply "true" to the request 'req' if 'status' is zero.
332  * Otherwise, when 'status' is not zero replies the error string 'errstr'.
333  */
334 static void reply_status(afb_req_t req, int status)
335 {
336         reply(req, status ? NULL : json_object_get(json_true));
337 }
338
339 /*
340  * On query "runnables"
341  */
342 static void runnables(afb_req_t req)
343 {
344         int all;
345         const char *lang;
346         struct json_object *resp;
347
348         /* get the language */
349         lang = get_lang(req);
350
351         /* get the all */
352         all = get_all(req);
353
354         /* get the details */
355         resp = afm_udb_applications_public(afudb, all, afb_req_get_uid(req), lang);
356         afb_req_success(req, resp, NULL);
357 }
358
359 /*
360  * On query "detail"
361  */
362 static void detail(afb_req_t req)
363 {
364         const char *lang;
365         const char *appid;
366         struct json_object *resp;
367
368         /* scan the request */
369         if (!onappid(req, _detail_, &appid))
370                 return;
371
372         /* get the language */
373         lang = get_lang(req);
374
375         /* wants details for appid */
376         resp = afm_udb_get_application_public(afudb, appid, afb_req_get_uid(req), lang);
377         if (resp)
378                 afb_req_success(req, resp, NULL);
379         else
380                 not_found(req);
381 }
382
383 /*
384  * On query "start"
385  */
386 static void start(afb_req_t req)
387 {
388         const char *appid;
389         struct json_object *appli, *resp;
390         int runid;
391
392         /* scan the request */
393         if (!onappid(req, _start_, &appid))
394                 return;
395
396         /* get the application */
397         appli = afm_udb_get_application_private(afudb, appid, afb_req_get_uid(req));
398         if (appli == NULL) {
399                 not_found(req);
400                 return;
401         }
402
403         /* launch the application */
404         runid = afm_urun_start(appli, afb_req_get_uid(req));
405         if (runid <= 0) {
406                 cant_start(req);
407                 return;
408         }
409
410         /* returns */
411         resp = NULL;
412 #if 0
413         wrap_json_pack(&resp, "{si}", _runid_, runid);
414 #else
415         wrap_json_pack(&resp, "i", runid);
416 #endif
417         afb_req_success(req, resp, NULL);
418 }
419
420 /*
421  * On query "once"
422  */
423 static void once(afb_req_t req)
424 {
425         const char *appid;
426         struct json_object *appli, *resp;
427         int runid;
428
429         /* scan the request */
430         if (!onappid(req, _once_, &appid))
431                 return;
432
433         /* get the application */
434         appli = afm_udb_get_application_private(afudb, appid, afb_req_get_uid(req));
435         if (appli == NULL) {
436                 not_found(req);
437                 return;
438         }
439
440         /* launch the application */
441         runid = afm_urun_once(appli, afb_req_get_uid(req));
442         if (runid <= 0) {
443                 cant_start(req);
444                 return;
445         }
446
447         /* returns the state */
448         resp = afm_urun_state(afudb, runid, afb_req_get_uid(req));
449         afb_req_success(req, resp, NULL);
450 }
451
452 /*
453  * On query "pause"
454  */
455 static void pause(afb_req_t req)
456 {
457         int runid, status;
458         if (onrunid(req, "pause", &runid)) {
459                 status = afm_urun_pause(runid, afb_req_get_uid(req));
460                 reply_status(req, status);
461         }
462 }
463
464 /*
465  * On query "resume" from 'smsg' with parameters of 'obj'.
466  */
467 static void resume(afb_req_t req)
468 {
469         int runid, status;
470         if (onrunid(req, "resume", &runid)) {
471                 status = afm_urun_resume(runid, afb_req_get_uid(req));
472                 reply_status(req, status);
473         }
474 }
475
476 /*
477  * On query "terminate"
478  */
479 static void terminate(afb_req_t req)
480 {
481         int runid, status;
482         if (onrunid(req, "terminate", &runid)) {
483                 status = afm_urun_terminate(runid, afb_req_get_uid(req));
484                 reply_status(req, status);
485         }
486 }
487
488 /*
489  * On query "runners"
490  */
491 static void runners(afb_req_t req)
492 {
493         int all;
494         struct json_object *resp;
495         all = get_all(req);
496         resp = afm_urun_list(afudb, all, afb_req_get_uid(req));
497         afb_req_success(req, resp, NULL);
498 }
499
500 /*
501  * On query "state"
502  */
503 static void state(afb_req_t req)
504 {
505         int runid;
506         struct json_object *resp;
507         if (onrunid(req, "state", &runid)) {
508                 resp = afm_urun_state(afudb, runid, afb_req_get_uid(req));
509                 reply(req, resp);
510         }
511 }
512
513 /*
514  * On querying installation of widget(s)
515  */
516 static void install(afb_req_t req)
517 {
518         const char *wgtfile;
519         const char *root;
520         int force;
521         int reload;
522         struct wgt_info *ifo;
523         struct json_object *json;
524         struct json_object *resp;
525
526         /* default settings */
527         root = rootdir;
528         force = 0;
529         reload = 1;
530
531         /* scan the request */
532         json = afb_req_json(req);
533         if (wrap_json_unpack(json, "s", &wgtfile)
534                 && wrap_json_unpack(json, "{ss s?s s?b s?b}",
535                                 "wgt", &wgtfile,
536                                 "root", &root,
537                                 "force", &force,
538                                 "reload", &reload)) {
539                 return bad_request(req);
540         }
541
542         /* install the widget */
543         ifo = install_widget(wgtfile, root, force);
544         if (ifo == NULL)
545                 afb_req_fail_f(req, "failed", "installation failed: %m");
546         else {
547                 afm_udb_update(afudb);
548                 /* reload if needed */
549                 if (reload)
550                         do_reloads();
551
552                 /* build the response */
553                 wrap_json_pack(&resp, "{ss}", _added_, wgt_info_desc(ifo)->idaver);
554                 afb_req_success(req, resp, NULL);
555                 application_list_changed(_install_, wgt_info_desc(ifo)->idaver);
556
557                 /* clean-up */
558                 wgt_info_unref(ifo);
559         }
560 }
561
562 /*
563  * On querying uninstallation of widget(s)
564  */
565 static void uninstall(afb_req_t req)
566 {
567         const char *idaver;
568         const char *root;
569         struct json_object *json;
570         int rc;
571
572         /* default settings */
573         root = rootdir;
574
575         /* scan the request */
576         json = afb_req_json(req);
577         if (wrap_json_unpack(json, "s", &idaver)
578                 && wrap_json_unpack(json, "{ss s?s}",
579                                 _id_, &idaver,
580                                 "root", &root)) {
581                 return bad_request(req);
582         }
583
584         /* install the widget */
585         rc = uninstall_widget(idaver, root);
586         if (rc)
587                 afb_req_fail_f(req, "failed", "uninstallation failed: %m");
588         else {
589                 afm_udb_update(afudb);
590                 afb_req_success(req, NULL, NULL);
591                 application_list_changed(_uninstall_, idaver);
592         }
593 }
594
595 static void onsighup(int signal)
596 {
597         afm_udb_update(afudb);
598         application_list_changed(_update_, _update_);
599 }
600
601 static int init(afb_api_t api)
602 {
603         /* create TRUE */
604         json_true = json_object_new_boolean(1);
605
606         /* init database */
607         afudb = afm_udb_create(1, 0, "afm-");
608         if (!afudb) {
609                 ERROR("afm_udb_create failed");
610                 return -1;
611         }
612
613         signal(SIGHUP, onsighup);
614
615         /* create the event */
616         applist_changed_event = afb_api_make_event(api, _a_l_c_);
617         return -!afb_event_is_valid(applist_changed_event);
618 }
619
620 static const afb_verb_t verbs[] =
621 {
622         {.verb=_runnables_, .callback=runnables, .auth=&auth_detail,    .info="Get list of runnable applications",          .session=AFB_SESSION_CHECK },
623         {.verb=_detail_   , .callback=detail,    .auth=&auth_detail,    .info="Get the details for one application",        .session=AFB_SESSION_CHECK },
624         {.verb=_start_    , .callback=start,     .auth=&auth_start,     .info="Start an application",                       .session=AFB_SESSION_CHECK },
625         {.verb=_once_     , .callback=once,      .auth=&auth_start,     .info="Start once an application",                  .session=AFB_SESSION_CHECK },
626         {.verb=_terminate_, .callback=terminate, .auth=&auth_kill,      .info="Terminate a running application",            .session=AFB_SESSION_CHECK },
627         {.verb=_pause_    , .callback=pause,     .auth=&auth_kill,      .info="Pause a running application",                .session=AFB_SESSION_CHECK },
628         {.verb=_resume_   , .callback=resume,    .auth=&auth_kill,      .info="Resume a paused application",                .session=AFB_SESSION_CHECK },
629         {.verb=_runners_  , .callback=runners,   .auth=&auth_state,     .info="Get the list of running applications",       .session=AFB_SESSION_CHECK },
630         {.verb=_state_    , .callback=state,     .auth=&auth_state,     .info="Get the state of a running application",     .session=AFB_SESSION_CHECK },
631         {.verb=_install_  , .callback=install,   .auth=&auth_install,   .info="Install an application using a widget file", .session=AFB_SESSION_CHECK },
632         {.verb=_uninstall_, .callback=uninstall, .auth=&auth_uninstall, .info="Uninstall an application",                   .session=AFB_SESSION_CHECK },
633         {.verb=NULL }
634 };
635
636 const afb_binding_t afbBindingExport = {
637         .api = "afm-main",
638         .specification = NULL,
639         .info = "Application Framework Master Service",
640         .verbs = verbs,
641         .preinit = NULL,
642         .init = init,
643         .onevent = NULL,
644         .noconcurrency = 1 /* relies on binder for serialization of requests */
645 };
646