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 _forbidden_[] = "forbidden";
50 static const char _force_[] = "force";
51 static const char _id_[] = "id";
52 static const char _install_[] = "install";
53 static const char _lang_[] = "lang";
54 static const char _not_found_[] = "not-found";
55 static const char _not_running_[] = "not-running";
56 static const char _once_[] = "once";
57 static const char _pause_[] = "pause";
58 static const char _reload_[] = "reload";
59 static const char _resume_[] = "resume";
60 static const char _root_[] = "root";
61 static const char _runid_[] = "runid";
62 static const char _runnables_[] = "runnables";
63 static const char _runners_[] = "runners";
64 static const char _start_[] = "start";
65 static const char _state_[] = "state";
66 static const char _terminate_[] = "terminate";
67 static const char _uid_[] = "uid";
68 static const char _uninstall_[] = "uninstall";
69 static const char _update_[] = "update";
70 static const char _wgt_[] = "wgt";
75 static const struct afb_auth
77 .type = afb_auth_Permission,
78 .text = FWK_PREFIX"permission:afm:system:widget"
80 auth_perm_widget_install = {
81 .type = afb_auth_Permission,
82 .text = FWK_PREFIX"permission:afm:system:widget:install"
84 auth_perm_widget_uninstall = {
85 .type = afb_auth_Permission,
86 .text = FWK_PREFIX"permission:afm:system:widget:uninstall"
88 auth_perm_widget_detail = {
89 .type = afb_auth_Permission,
90 .text = FWK_PREFIX"permission:afm:system:widget:detail"
92 auth_perm_widget_start = {
93 .type = afb_auth_Permission,
94 .text = FWK_PREFIX"permission:afm:system:widget:start"
96 auth_perm_widget_view_all = {
97 .type = afb_auth_Permission,
98 .text = FWK_PREFIX"permission:afm:system:widget:view-all"
101 .type = afb_auth_Permission,
102 .text = FWK_PREFIX"permission:afm:system:runner"
104 auth_perm_runner_state = {
105 .type = afb_auth_Permission,
106 .text = FWK_PREFIX"permission:afm:system:runner:state"
108 auth_perm_runner_kill = {
109 .type = afb_auth_Permission,
110 .text = FWK_PREFIX"permission:afm:system:runner:kill"
112 auth_perm_set_uid = {
113 .type = afb_auth_Permission,
114 .text = FWK_PREFIX"permission:afm:system:set-uid"
119 .first = &auth_perm_widget,
120 .next = &auth_perm_widget_install
124 .first = &auth_perm_widget,
125 .next = &auth_perm_widget_uninstall
129 .first = &auth_perm_widget,
130 .next = &auth_perm_widget_detail
134 .first = &auth_perm_widget,
135 .next = &auth_perm_widget_start
139 .first = &auth_perm_widget,
140 .next = &auth_perm_widget_view_all
144 .first = &auth_perm_runner,
145 .next = &auth_perm_runner_state
149 .first = &auth_perm_runner,
150 .next = &auth_perm_runner_kill
155 * Enumerate the possible arguments
156 * This is intended to be used as a mask of bits
157 * telling what parameter is expected, optional,
172 * Records the parameters of verb queries
175 /** bit mask of the given param */
177 /** value of param 'all' if set */
179 /** value of param 'force' if set */
181 /** value of param 'reload' if set */
183 /** value of param 'uid' if set */
185 /** value of param 'runid' if set */
187 /** value of param 'lang' if set */
189 /** value of param 'id' if set */
191 /** value of param 'wgt' if set */
193 /** value of param 'root' if set */
195 /** object value of parameters */
196 struct json_object *args;
202 static const char rootdir[] = FWK_APP_DIR;
205 * the internal application database
207 static struct afm_udb *afudb;
210 * the event signaling that application list changed
212 static afb_event_t applist_changed_event;
215 * the preallocated true json_object
217 static struct json_object *json_true;
219 /* enforce daemon reload */
220 static void do_reloads()
222 systemd_daemon_reload(0);
223 systemd_unit_restart_name(0, "sockets.target");
226 /* common bad request reply */
227 static void bad_request(afb_req_t req)
229 INFO("bad request verb %s: %s",
230 afb_req_get_called_verb(req),
231 json_object_to_json_string(afb_req_json(req)));
232 afb_req_fail(req, _bad_request_, NULL);
235 /* forbidden request reply */
236 static void forbidden_request(afb_req_t req)
238 INFO("forbidden request verb %s: %s",
239 afb_req_get_called_verb(req),
240 json_object_to_json_string(afb_req_json(req)));
241 afb_req_fail(req, _forbidden_, NULL);
244 /* common not found reply */
245 static void not_found(afb_req_t req)
247 afb_req_fail(req, _not_found_, NULL);
250 /* common not running reply */
251 static void not_running(afb_req_t req)
253 afb_req_fail(req, _not_running_, NULL);
256 /* common can't start reply */
257 static void cant_start(afb_req_t req)
259 afb_req_fail(req, _cannot_start_, NULL);
262 /* emulate missing function */
263 static int has_auth(afb_req_t req, const struct afb_auth *auth)
265 switch (auth->type) {
266 case afb_auth_Permission:
267 return afb_req_has_permission(req, auth->text);
269 return has_auth(req, auth->first) || has_auth(req, auth->next);
271 return has_auth(req, auth->first) && has_auth(req, auth->next);
273 return !has_auth(req, auth->first);
285 * Broadcast the event "application-list-changed".
286 * This event is sent was the event "changed" is received from dbus.
288 static void application_list_changed(const char *operation, const char *data)
290 struct json_object *e = NULL;
291 wrap_json_pack(&e, "{ss ss}", "operation", operation, "data", data);
292 afb_event_broadcast(applist_changed_event, e);
296 * common routine for getting parameters
298 static int get_params(afb_req_t req, unsigned mandatory, unsigned optional, struct params *params)
302 error_bad_request = 1,
304 error_not_running = 3,
309 struct json_object *args, *obj;
310 unsigned found, expected;
313 expected = optional|mandatory;
314 memset(params, 0, sizeof *params);
317 params->uid = afb_req_get_uid(req);
318 args = afb_req_json(req);
320 /* args is a numeric value: a run id */
321 if (json_object_is_type(args, json_type_int)) {
322 if (expected & Param_RunId) {
323 params->runid = json_object_get_int(args);
324 found |= Param_RunId;
328 /* args is a string value: either an ID or a widget path */
329 else if (json_object_is_type(args, json_type_string)) {
330 if (expected & (Param_Id | Param_RunId)) {
331 params->id = json_object_get_string(args);
334 else if (expected & Param_WGT) {
335 params->wgt = json_object_get_string(args);
340 /* args is a object value: inspect it */
341 else if (json_object_is_type(args, json_type_object)) {
343 if (json_object_object_get_ex(args, _uid_, &obj)) {
344 if (!json_object_is_type(obj, json_type_int))
347 id = json_object_get_int(obj);
349 error = error_bad_request;
350 else if (params->uid != id) {
351 if (!afb_req_has_permission(req, auth_perm_set_uid.text))
352 error = error_forbidden;
361 if ((expected & Param_All)
362 && json_object_object_get_ex(args, _all_, &obj)) {
363 params->all = json_object_get_boolean(obj);
364 if (params->all && !has_auth(req, &auth_view_all))
365 error = error_forbidden;
371 if ((expected & Param_Force)
372 && json_object_object_get_ex(args, _force_, &obj)) {
373 params->force = json_object_get_boolean(obj);
374 found |= Param_Force;
378 if ((expected & Param_Reload)
379 && json_object_object_get_ex(args, _reload_, &obj)) {
380 params->reload = json_object_get_boolean(obj);
381 found |= Param_Reload;
385 if ((expected & Param_Lang)
386 && json_object_object_get_ex(args, _lang_, &obj)) {
387 params->lang = json_object_get_string(obj);
392 if ((expected & Param_Root)
393 && json_object_object_get_ex(args, _root_, &obj)) {
394 params->root = json_object_get_string(obj);
399 if (expected & Param_WGT) {
400 if (json_object_object_get_ex(args, _wgt_, &obj)) {
401 params->wgt = json_object_get_string(obj);
407 if (expected & (Param_Id | Param_RunId)) {
408 if (json_object_object_get_ex(args, _id_, &obj)) {
409 params->id = json_object_get_string(obj);
415 if (expected & Param_RunId) {
416 if (json_object_object_get_ex(args, _runid_, &obj)) {
417 if (json_object_is_type(obj, json_type_int))
418 error = error_bad_request;
420 params->runid = json_object_get_int(obj);
421 found |= Param_RunId;
427 /* deduce the runid from the uid on need */
428 if ((mandatory & Param_RunId) && !(found & Param_RunId) && (found & Param_Id)) {
429 id = afm_urun_search_runid(afudb, params->id, params->uid);
432 found |= Param_RunId;
434 else if (errno == ESRCH)
435 error = error_not_running;
437 error = error_not_found;
440 /* check all mandatory are here */
441 if (error != no_error || (mandatory & found) != mandatory) {
443 case error_not_found:
446 case error_not_running:
449 case error_forbidden:
450 forbidden_request(req);
452 case error_bad_request:
461 params->found = found;
466 * Sends the reply 'resp' to the request 'req' if 'resp' is not NULLzero.
467 * Otherwise, when 'resp' is NULL replies the error string 'errstr'.
469 static void reply(afb_req_t req, struct json_object *resp)
472 afb_req_reply(req, resp, NULL, NULL);
474 afb_req_reply(req, NULL, "failed", strerror(errno));
478 * Sends the reply "true" to the request 'req' if 'status' is zero.
479 * Otherwise, when 'status' is not zero replies the error string 'errstr'.
481 static void reply_status(afb_req_t req, int status)
483 reply(req, status ? NULL : json_object_get(json_true));
487 * On query "runnables"
489 static void runnables(afb_req_t req)
491 struct params params;
492 struct json_object *resp;
494 /* scan the request */
495 if (!get_params(req, 0, Param_Lang|Param_All, ¶ms))
498 /* get the applications */
499 resp = afm_udb_applications_public(afudb, params.all, params.uid, params.lang);
500 afb_req_success(req, resp, NULL);
506 static void detail(afb_req_t req)
508 struct params params;
509 struct json_object *resp;
511 /* scan the request */
512 if (!get_params(req, Param_Id, Param_Lang, ¶ms))
515 /* get the details */
516 resp = afm_udb_get_application_public(afudb, params.id, params.uid, params.lang);
518 afb_req_success(req, resp, NULL);
526 static void start(afb_req_t req)
528 struct params params;
529 struct json_object *appli, *resp;
532 /* scan the request */
533 if (!get_params(req, Param_Id, 0, ¶ms))
536 /* get the application */
537 appli = afm_udb_get_application_private(afudb, params.id, params.uid);
543 /* launch the application */
544 runid = afm_urun_start(appli, params.uid);
552 wrap_json_pack(&resp, "i", runid);
553 afb_req_success(req, resp, NULL);
559 static void once(afb_req_t req)
561 struct params params;
562 struct json_object *appli, *resp;
565 /* scan the request */
566 if (!get_params(req, Param_Id, 0, ¶ms))
569 /* get the application */
570 appli = afm_udb_get_application_private(afudb, params.id, params.uid);
576 /* launch the application */
577 runid = afm_urun_once(appli, params.uid);
583 /* returns the state */
584 resp = afm_urun_state(afudb, runid, params.uid);
585 afb_req_success(req, resp, NULL);
591 static void pause(afb_req_t req)
593 struct params params;
596 /* scan the request */
597 if (get_params(req, Param_RunId, 0, ¶ms)) {
598 status = afm_urun_pause(params.runid, params.uid);
599 reply_status(req, status);
604 * On query "resume" from 'smsg' with parameters of 'obj'.
606 static void resume(afb_req_t req)
608 struct params params;
611 /* scan the request */
612 if (get_params(req, Param_RunId, 0, ¶ms)) {
613 status = afm_urun_resume(params.runid, params.uid);
614 reply_status(req, status);
619 * On query "terminate"
621 static void terminate(afb_req_t req)
623 struct params params;
626 /* scan the request */
627 if (get_params(req, Param_RunId, 0, ¶ms)) {
628 status = afm_urun_terminate(params.runid, params.uid);
629 reply_status(req, status);
636 static void runners(afb_req_t req)
638 struct params params;
639 struct json_object *resp;
641 /* scan the request */
642 if (!get_params(req, 0, Param_All, ¶ms))
645 resp = afm_urun_list(afudb, params.all, params.uid);
646 afb_req_success(req, resp, NULL);
652 static void state(afb_req_t req)
654 struct params params;
655 struct json_object *resp;
657 /* scan the request */
658 if (get_params(req, Param_RunId, 0, ¶ms)) {
659 resp = afm_urun_state(afudb, params.runid, params.uid);
665 * On querying installation of widget(s)
667 static void install(afb_req_t req)
669 struct params params;
670 struct wgt_info *ifo;
671 struct json_object *resp;
673 /* scan the request */
674 if (!get_params(req, Param_WGT, Param_Root|Param_Force|Param_Reload, ¶ms))
677 /* check if force is allowed */
679 if (!has_auth(req, &auth_uninstall)) {
680 forbidden_request(req);
685 /* supply default values */
686 if (!(params.found & Param_Reload))
688 if (!(params.found & Param_Root))
689 params.root = rootdir;
691 /* install the widget */
692 ifo = install_widget(params.wgt, params.root, params.force);
694 afb_req_fail_f(req, "failed", "installation failed: %m");
696 afm_udb_update(afudb);
697 /* reload if needed */
701 /* build the response */
702 wrap_json_pack(&resp, "{ss}", _added_, wgt_info_desc(ifo)->idaver);
703 afb_req_success(req, resp, NULL);
704 application_list_changed(_install_, wgt_info_desc(ifo)->idaver);
712 * On querying uninstallation of widget(s)
714 static void uninstall(afb_req_t req)
716 struct params params;
719 /* scan the request */
720 if (!get_params(req, Param_Id, Param_Root|Param_Reload, ¶ms))
722 if (!(params.found & Param_Reload))
724 if (!(params.found & Param_Root))
725 params.root = rootdir;
727 /* install the widget */
728 rc = uninstall_widget(params.id, params.root);
730 afb_req_fail_f(req, "failed", "uninstallation failed: %m");
732 afm_udb_update(afudb);
733 afb_req_success(req, NULL, NULL);
734 application_list_changed(_uninstall_, params.id);
738 static void onsighup(int signal)
740 afm_udb_update(afudb);
741 application_list_changed(_update_, _update_);
744 static int init(afb_api_t api)
747 json_true = json_object_new_boolean(1);
750 afudb = afm_udb_create(1, 0, "afm-");
752 ERROR("afm_udb_create failed");
756 signal(SIGHUP, onsighup);
758 /* create the event */
759 applist_changed_event = afb_api_make_event(api, _a_l_c_);
760 return -!afb_event_is_valid(applist_changed_event);
763 static const afb_verb_t verbs[] =
765 {.verb=_runnables_, .callback=runnables, .auth=&auth_detail, .info="Get list of runnable applications", .session=AFB_SESSION_CHECK },
766 {.verb=_detail_ , .callback=detail, .auth=&auth_detail, .info="Get the details for one application", .session=AFB_SESSION_CHECK },
767 {.verb=_start_ , .callback=start, .auth=&auth_start, .info="Start an application", .session=AFB_SESSION_CHECK },
768 {.verb=_once_ , .callback=once, .auth=&auth_start, .info="Start once an application", .session=AFB_SESSION_CHECK },
769 {.verb=_terminate_, .callback=terminate, .auth=&auth_kill, .info="Terminate a running application", .session=AFB_SESSION_CHECK },
770 {.verb=_pause_ , .callback=pause, .auth=&auth_kill, .info="Pause a running application", .session=AFB_SESSION_CHECK },
771 {.verb=_resume_ , .callback=resume, .auth=&auth_kill, .info="Resume a paused application", .session=AFB_SESSION_CHECK },
772 {.verb=_runners_ , .callback=runners, .auth=&auth_state, .info="Get the list of running applications", .session=AFB_SESSION_CHECK },
773 {.verb=_state_ , .callback=state, .auth=&auth_state, .info="Get the state of a running application", .session=AFB_SESSION_CHECK },
774 {.verb=_install_ , .callback=install, .auth=&auth_install, .info="Install an application using a widget file", .session=AFB_SESSION_CHECK },
775 {.verb=_uninstall_, .callback=uninstall, .auth=&auth_uninstall, .info="Uninstall an application", .session=AFB_SESSION_CHECK },
779 const afb_binding_t afbBindingExport = {
781 .specification = NULL,
782 .info = "Application Framework Master Service",
787 .noconcurrency = 1 /* relies on binder for serialization of requests */