2 * Copyright (C) 2015-2020 "IoT.bzh"
3 * Author José Bollo <jose.bollo@iot.bzh>
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
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 #define _GNU_SOURCE /* See feature_test_macros(7) */
26 #include <json-c/json.h>
28 #define AFB_BINDING_VERSION 3
29 #include <afb/afb-binding.h>
32 #include "utils-systemd.h"
36 #include "wgtpkg-install.h"
37 #include "wgtpkg-uninstall.h"
38 #include "wrap-json.h"
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";
69 static const struct afb_auth
71 .type = afb_auth_Permission,
72 .text = FWK_PREFIX"permission:afm:system:widget"
74 auth_perm_widget_install = {
75 .type = afb_auth_Permission,
76 .text = FWK_PREFIX"permission:afm:system:widget:install"
78 auth_perm_widget_uninstall = {
79 .type = afb_auth_Permission,
80 .text = FWK_PREFIX"permission:afm:system:widget:uninstall"
82 auth_perm_widget_preinstall = {
83 .type = afb_auth_Permission,
84 .text = FWK_PREFIX"permission:afm:system:widget:preinstall"
86 auth_perm_widget_detail = {
87 .type = afb_auth_Permission,
88 .text = FWK_PREFIX"permission:afm:system:widget:detail"
90 auth_perm_widget_start = {
91 .type = afb_auth_Permission,
92 .text = FWK_PREFIX"permission:afm:system:widget:start"
94 auth_perm_widget_view_all = {
95 .type = afb_auth_Permission,
96 .text = FWK_PREFIX"permission:afm:system:widget:view-all"
99 .type = afb_auth_Permission,
100 .text = FWK_PREFIX"permission:afm:system:runner"
102 auth_perm_runner_state = {
103 .type = afb_auth_Permission,
104 .text = FWK_PREFIX"permission:afm:system:runner:state"
106 auth_perm_runner_kill = {
107 .type = afb_auth_Permission,
108 .text = FWK_PREFIX"permission:afm:system:runner:kill"
113 .first = &auth_perm_widget,
114 .next = &auth_perm_widget_install
118 .first = &auth_perm_widget,
119 .next = &auth_perm_widget_uninstall
123 .first = &auth_perm_widget,
124 .next = &auth_perm_widget_preinstall
128 .first = &auth_perm_widget,
129 .next = &auth_perm_widget_detail
133 .first = &auth_perm_widget,
134 .next = &auth_perm_widget_start
138 .first = &auth_perm_widget,
139 .next = &auth_perm_widget_view_all
143 .first = &auth_perm_runner,
144 .next = &auth_perm_runner_state
148 .first = &auth_perm_runner,
149 .next = &auth_perm_runner_kill
156 static const char *rootdir = FWK_APP_DIR;
159 * the internal application database
161 static struct afm_udb *afudb;
164 * the event signaling that application list changed
166 static afb_event_t applist_changed_event;
169 * the preallocated true json_object
171 static struct json_object *json_true;
173 /* enforce daemon reload */
174 static void do_reloads()
176 systemd_daemon_reload(0);
177 systemd_unit_restart_name(0, "sockets.target");
180 /* common bad request reply */
181 static void bad_request(afb_req_t req)
183 afb_req_fail(req, _bad_request_, NULL);
186 /* common not found reply */
187 static void not_found(afb_req_t req)
189 afb_req_fail(req, _not_found_, NULL);
192 /* common not running reply */
193 static void not_running(afb_req_t req)
195 afb_req_fail(req, _not_running_, NULL);
198 /* common can't start reply */
199 static void cant_start(afb_req_t req)
201 afb_req_fail(req, _cannot_start_, NULL);
205 * Broadcast the event "application-list-changed".
206 * This event is sent was the event "changed" is received from dbus.
208 static void application_list_changed(const char *operation, const char *data)
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);
216 * Retrieve the required language from 'req'.
218 static const char *get_lang(afb_req_t req)
222 /* get the optional language */
223 lang = afb_req_value(req, _lang_);
225 /* TODO use the req to get the lang of the session (if any) */
231 * Retrieve whether all is required from 'req'.
233 static int get_all(afb_req_t req)
235 struct json_object *val;
237 /* get the optional language */
238 return json_object_object_get_ex(afb_req_json(req), _all_, &val)
239 && json_object_get_boolean(val);
244 * retrieves the 'appid' in parameters received with the
245 * request 'req' for the 'method'.
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.
251 static int onappid(afb_req_t req, const char *method, const char **appid)
253 struct json_object *json;
255 /* get the paramaters of the request */
256 json = afb_req_json(req);
258 /* get the appid if any */
259 if (!wrap_json_unpack(json, "s", appid)
260 || !wrap_json_unpack(json, "{ss}", _id_, appid)) {
262 INFO("method %s called for %s", method, *appid);
266 /* nothing appropriate */
267 INFO("bad request method %s: %s", method,
268 json_object_to_json_string(json));
274 * retrieves the 'runid' in parameters received with the
275 * request 'req' for the 'method'.
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.
281 static int onrunid(afb_req_t req, const char *method, int *runid)
283 struct json_object *json;
286 /* get the paramaters of the request */
287 json = afb_req_json(req);
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);
296 /* get the appid if any */
297 if (!onappid(req, method, &appid))
300 /* search the runid of the appid */
301 *runid = afm_urun_search_runid(afudb, appid, afb_req_get_uid(req));
303 /* nothing appropriate */
304 INFO("method %s can't get runid for %s: %m", method,
314 INFO("method %s called for %s -> %d", method, appid, *runid);
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'.
322 static void reply(afb_req_t req, struct json_object *resp)
325 afb_req_reply(req, resp, NULL, NULL);
327 afb_req_reply(req, NULL, "failed", strerror(errno));
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'.
334 static void reply_status(afb_req_t req, int status)
336 reply(req, status ? NULL : json_object_get(json_true));
340 * On query "runnables"
342 static void runnables(afb_req_t req)
346 struct json_object *resp;
348 /* get the language */
349 lang = get_lang(req);
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);
362 static void detail(afb_req_t req)
366 struct json_object *resp;
368 /* scan the request */
369 if (!onappid(req, _detail_, &appid))
372 /* get the language */
373 lang = get_lang(req);
375 /* wants details for appid */
376 resp = afm_udb_get_application_public(afudb, appid, afb_req_get_uid(req), lang);
378 afb_req_success(req, resp, NULL);
386 static void start(afb_req_t req)
389 struct json_object *appli, *resp;
392 /* scan the request */
393 if (!onappid(req, _start_, &appid))
396 /* get the application */
397 appli = afm_udb_get_application_private(afudb, appid, afb_req_get_uid(req));
403 /* launch the application */
404 runid = afm_urun_start(appli, afb_req_get_uid(req));
413 wrap_json_pack(&resp, "{si}", _runid_, runid);
416 wrap_json_pack(&resp, "i", runid);
418 afb_req_success(req, resp, NULL);
424 static void once(afb_req_t req)
427 struct json_object *appli, *resp;
430 /* scan the request */
431 if (!onappid(req, _once_, &appid))
434 /* get the application */
435 appli = afm_udb_get_application_private(afudb, appid, afb_req_get_uid(req));
441 /* launch the application */
442 runid = afm_urun_once(appli, afb_req_get_uid(req));
448 /* returns the state */
449 resp = runid ? afm_urun_state(afudb, runid, afb_req_get_uid(req)) : NULL;
450 afb_req_success(req, resp, NULL);
456 static void pause(afb_req_t req)
459 if (onrunid(req, "pause", &runid)) {
460 status = afm_urun_pause(runid, afb_req_get_uid(req));
461 reply_status(req, status);
466 * On query "resume" from 'smsg' with parameters of 'obj'.
468 static void resume(afb_req_t req)
471 if (onrunid(req, "resume", &runid)) {
472 status = afm_urun_resume(runid, afb_req_get_uid(req));
473 reply_status(req, status);
478 * On query "terminate"
480 static void terminate(afb_req_t req)
483 if (onrunid(req, "terminate", &runid)) {
484 status = afm_urun_terminate(runid, afb_req_get_uid(req));
485 reply_status(req, status);
492 static void runners(afb_req_t req)
495 struct json_object *resp;
497 resp = afm_urun_list(afudb, all, afb_req_get_uid(req));
498 afb_req_success(req, resp, NULL);
504 static void state(afb_req_t req)
507 struct json_object *resp;
508 if (onrunid(req, "state", &runid)) {
509 resp = afm_urun_state(afudb, runid, afb_req_get_uid(req));
515 * On querying installation of widget(s)
517 static void install(afb_req_t req)
523 struct wgt_info *ifo;
524 struct json_object *json;
525 struct json_object *resp;
527 /* default settings */
532 /* scan the request */
533 json = afb_req_json(req);
534 if (wrap_json_unpack(json, "s", &wgtfile)
535 && wrap_json_unpack(json, "{ss s?s s?b s?b}",
539 "reload", &reload)) {
540 return bad_request(req);
543 /* install the widget */
544 ifo = install_widget(wgtfile, root, force);
546 afb_req_fail_f(req, "failed", "installation failed: %m");
548 afm_udb_update(afudb);
549 /* reload if needed */
553 /* build the response */
554 wrap_json_pack(&resp, "{ss}", _added_, wgt_info_desc(ifo)->idaver);
555 afb_req_success(req, resp, NULL);
556 application_list_changed(_install_, wgt_info_desc(ifo)->idaver);
564 * On querying uninstallation of widget(s)
566 static void uninstall(afb_req_t req)
570 struct json_object *json;
573 /* default settings */
576 /* scan the request */
577 json = afb_req_json(req);
578 if (wrap_json_unpack(json, "s", &idaver)
579 && wrap_json_unpack(json, "{ss s?s}",
582 return bad_request(req);
585 /* install the widget */
586 rc = uninstall_widget(idaver, root);
588 afb_req_fail_f(req, "failed", "uninstallation failed: %m");
590 afm_udb_update(afudb);
591 afb_req_success(req, NULL, NULL);
592 application_list_changed(_uninstall_, idaver);
596 static void onsighup(int signal)
598 afm_udb_update(afudb);
599 application_list_changed(_update_, _update_);
602 static int init(afb_api_t api)
605 json_true = json_object_new_boolean(1);
608 afudb = afm_udb_create(1, 0, "afm-");
610 ERROR("afm_udb_create failed");
614 signal(SIGHUP, onsighup);
616 /* create the event */
617 applist_changed_event = afb_api_make_event(api, _a_l_c_);
618 return -!afb_event_is_valid(applist_changed_event);
621 static const afb_verb_t verbs[] =
623 {.verb=_runnables_, .callback=runnables, .auth=&auth_detail, .info="Get list of runnable applications", .session=AFB_SESSION_CHECK },
624 {.verb=_detail_ , .callback=detail, .auth=&auth_detail, .info="Get the details for one application", .session=AFB_SESSION_CHECK },
625 {.verb=_start_ , .callback=start, .auth=&auth_start, .info="Start an application", .session=AFB_SESSION_CHECK },
626 {.verb=_once_ , .callback=once, .auth=&auth_start, .info="Start once an application", .session=AFB_SESSION_CHECK },
627 {.verb=_terminate_, .callback=terminate, .auth=&auth_kill, .info="Terminate a running application", .session=AFB_SESSION_CHECK },
628 {.verb=_pause_ , .callback=pause, .auth=&auth_kill, .info="Pause a running application", .session=AFB_SESSION_CHECK },
629 {.verb=_resume_ , .callback=resume, .auth=&auth_kill, .info="Resume a paused application", .session=AFB_SESSION_CHECK },
630 {.verb=_runners_ , .callback=runners, .auth=&auth_state, .info="Get the list of running applications", .session=AFB_SESSION_CHECK },
631 {.verb=_state_ , .callback=state, .auth=&auth_state, .info="Get the state of a running application", .session=AFB_SESSION_CHECK },
632 {.verb=_install_ , .callback=install, .auth=&auth_install, .info="Install an application using a widget file", .session=AFB_SESSION_CHECK },
633 {.verb=_uninstall_, .callback=uninstall, .auth=&auth_uninstall, .info="Uninstall an application", .session=AFB_SESSION_CHECK },
637 const afb_binding_t afbBindingExport = {
639 .specification = NULL,
640 .info = "Application Framework Master Service",
645 .noconcurrency = 1 /* relies on binder for serialization of requests */