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