2 * Copyright (C) 2015-2019 "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);
415 wrap_json_pack(&resp, "i", runid);
417 afb_req_success(req, resp, NULL);
423 static void once(afb_req_t req)
426 struct json_object *appli, *resp;
429 /* scan the request */
430 if (!onappid(req, _once_, &appid))
433 /* get the application */
434 appli = afm_udb_get_application_private(afudb, appid, afb_req_get_uid(req));
440 /* launch the application */
441 runid = afm_urun_once(appli, afb_req_get_uid(req));
447 /* returns the state */
448 resp = afm_urun_state(afudb, runid, afb_req_get_uid(req));
449 afb_req_success(req, resp, NULL);
455 static void pause(afb_req_t req)
458 if (onrunid(req, "pause", &runid)) {
459 status = afm_urun_pause(runid, afb_req_get_uid(req));
460 reply_status(req, status);
465 * On query "resume" from 'smsg' with parameters of 'obj'.
467 static void resume(afb_req_t req)
470 if (onrunid(req, "resume", &runid)) {
471 status = afm_urun_resume(runid, afb_req_get_uid(req));
472 reply_status(req, status);
477 * On query "terminate"
479 static void terminate(afb_req_t req)
482 if (onrunid(req, "terminate", &runid)) {
483 status = afm_urun_terminate(runid, afb_req_get_uid(req));
484 reply_status(req, status);
491 static void runners(afb_req_t req)
494 struct json_object *resp;
496 resp = afm_urun_list(afudb, all, afb_req_get_uid(req));
497 afb_req_success(req, resp, NULL);
503 static void state(afb_req_t req)
506 struct json_object *resp;
507 if (onrunid(req, "state", &runid)) {
508 resp = afm_urun_state(afudb, runid, afb_req_get_uid(req));
514 * On querying installation of widget(s)
516 static void install(afb_req_t req)
522 struct wgt_info *ifo;
523 struct json_object *json;
524 struct json_object *resp;
526 /* default settings */
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}",
538 "reload", &reload)) {
539 return bad_request(req);
542 /* install the widget */
543 ifo = install_widget(wgtfile, root, force);
545 afb_req_fail_f(req, "failed", "installation failed: %m");
547 afm_udb_update(afudb);
548 /* reload if needed */
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);
563 * On querying uninstallation of widget(s)
565 static void uninstall(afb_req_t req)
569 struct json_object *json;
572 /* default settings */
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}",
581 return bad_request(req);
584 /* install the widget */
585 rc = uninstall_widget(idaver, root);
587 afb_req_fail_f(req, "failed", "uninstallation failed: %m");
589 afm_udb_update(afudb);
590 afb_req_success(req, NULL, NULL);
591 application_list_changed(_uninstall_, idaver);
595 static void onsighup(int signal)
597 afm_udb_update(afudb);
598 application_list_changed(_update_, _update_);
601 static int init(afb_api_t api)
604 json_true = json_object_new_boolean(1);
607 afudb = afm_udb_create(1, 0, "afm-");
609 ERROR("afm_udb_create failed");
613 signal(SIGHUP, onsighup);
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);
620 static const afb_verb_t verbs[] =
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 },
636 const afb_binding_t afbBindingExport = {
638 .specification = NULL,
639 .info = "Application Framework Master Service",
644 .noconcurrency = 1 /* relies on binder for serialization of requests */