2 * Copyright (C) 2015-2018 "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 _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 _not_running_[] = "not-running";
53 static const char _once_[] = "once";
54 static const char _pause_[] = "pause";
55 static const char _resume_[] = "resume";
56 static const char _runid_[] = "runid";
57 static const char _runnables_[] = "runnables";
58 static const char _runners_[] = "runners";
59 static const char _start_[] = "start";
60 static const char _state_[] = "state";
61 static const char _terminate_[] = "terminate";
62 static const char _uninstall_[] = "uninstall";
63 static const char _update_[] = "update";
68 static const struct afb_auth
70 .type = afb_auth_Permission,
71 .text = "urn:AGL:permission:afm:system:widget"
73 auth_perm_widget_install = {
74 .type = afb_auth_Permission,
75 .text = "urn:AGL:permission:afm:system:widget:install"
77 auth_perm_widget_uninstall = {
78 .type = afb_auth_Permission,
79 .text = "urn:AGL:permission:afm:system:widget:uninstall"
81 auth_perm_widget_preinstall = {
82 .type = afb_auth_Permission,
83 .text = "urn:AGL:permission:afm:system:widget:preinstall"
85 auth_perm_widget_detail = {
86 .type = afb_auth_Permission,
87 .text = "urn:AGL:permission:afm:system:widget:detail"
89 auth_perm_widget_start = {
90 .type = afb_auth_Permission,
91 .text = "urn:AGL:permission:afm:system:widget:start"
93 auth_perm_widget_view_all = {
94 .type = afb_auth_Permission,
95 .text = "urn:AGL:permission:afm:system:widget:view-all"
98 .type = afb_auth_Permission,
99 .text = "urn:AGL:permission:afm:system:runner"
101 auth_perm_runner_state = {
102 .type = afb_auth_Permission,
103 .text = "urn:AGL:permission:afm:system:runner:state"
105 auth_perm_runner_kill = {
106 .type = afb_auth_Permission,
107 .text = "urn:AGL:permission:afm:system:runner:kill"
112 .first = &auth_perm_widget,
113 .next = &auth_perm_widget_install
117 .first = &auth_perm_widget,
118 .next = &auth_perm_widget_uninstall
122 .first = &auth_perm_widget,
123 .next = &auth_perm_widget_preinstall
127 .first = &auth_perm_widget,
128 .next = &auth_perm_widget_detail
132 .first = &auth_perm_widget,
133 .next = &auth_perm_widget_start
137 .first = &auth_perm_widget,
138 .next = &auth_perm_widget_view_all
142 .first = &auth_perm_runner,
143 .next = &auth_perm_runner_state
147 .first = &auth_perm_runner,
148 .next = &auth_perm_runner_kill
155 static const char *rootdir = FWK_APP_DIR;
158 * the internal application database
160 static struct afm_udb *afudb;
163 * the event signaling that application list changed
165 static afb_event_t applist_changed_event;
168 * the preallocated true json_object
170 static struct json_object *json_true;
172 /* enforce daemon reload */
173 static void do_reloads()
175 systemd_daemon_reload(0);
176 systemd_unit_restart_name(0, "sockets.target");
179 /* common bad request reply */
180 static void bad_request(afb_req_t req)
182 afb_req_fail(req, _bad_request_, NULL);
185 /* common not found reply */
186 static void not_found(afb_req_t req)
188 afb_req_fail(req, _not_found_, NULL);
191 /* common not running reply */
192 static void not_running(afb_req_t req)
194 afb_req_fail(req, _not_running_, NULL);
197 /* common can't start reply */
198 static void cant_start(afb_req_t req)
200 afb_req_fail(req, _cannot_start_, NULL);
204 * Broadcast the event "application-list-changed".
205 * This event is sent was the event "changed" is received from dbus.
207 static void application_list_changed(const char *operation, const char *data)
209 struct json_object *e = NULL;
210 wrap_json_pack(&e, "{ss ss}", "operation", operation, "data", data);
211 afb_event_broadcast(applist_changed_event, e);
215 * Retrieve the required language from 'req'.
217 static const char *get_lang(afb_req_t req)
221 /* get the optional language */
222 lang = afb_req_value(req, _lang_);
224 /* TODO use the req to get the lang of the session (if any) */
231 * retrieves the 'appid' in parameters received with the
232 * request 'req' for the 'method'.
234 * Returns 1 in case of success.
235 * Otherwise, if the 'appid' can't be retrieved, an error stating
236 * the bad request is replied for 'req' and 0 is returned.
238 static int onappid(afb_req_t req, const char *method, const char **appid)
240 struct json_object *json;
242 /* get the paramaters of the request */
243 json = afb_req_json(req);
245 /* get the appid if any */
246 if (!wrap_json_unpack(json, "s", appid)
247 || !wrap_json_unpack(json, "{ss}", _id_, appid)) {
249 INFO("method %s called for %s", method, *appid);
253 /* nothing appropriate */
254 INFO("bad request method %s: %s", method,
255 json_object_to_json_string(json));
261 * retrieves the 'runid' in parameters received with the
262 * request 'req' for the 'method'.
264 * Returns 1 in case of success.
265 * Otherwise, if the 'runid' can't be retrieved, an error stating
266 * the bad request is replied for 'req' and 0 is returned.
268 static int onrunid(afb_req_t req, const char *method, int *runid)
270 struct json_object *json;
273 /* get the paramaters of the request */
274 json = afb_req_json(req);
276 /* get the runid if any */
277 if (!wrap_json_unpack(json, "i", runid)
278 || !wrap_json_unpack(json, "{si}", _runid_, runid)) {
279 INFO("method %s called for %d", method, *runid);
283 /* get the appid if any */
284 if (!onappid(req, method, &appid))
287 /* search the runid of the appid */
288 *runid = afm_urun_search_runid(afudb, appid, afb_req_get_uid(req));
290 /* nothing appropriate */
291 INFO("method %s can't get runid for %s: %m", method,
301 INFO("method %s called for %s -> %d", method, appid, *runid);
306 * Sends the reply 'resp' to the request 'req' if 'resp' is not NULLzero.
307 * Otherwise, when 'resp' is NULL replies the error string 'errstr'.
309 static void reply(afb_req_t req, struct json_object *resp)
312 afb_req_reply(req, resp, NULL, NULL);
314 afb_req_reply(req, NULL, "failed", strerror(errno));
318 * Sends the reply "true" to the request 'req' if 'status' is zero.
319 * Otherwise, when 'status' is not zero replies the error string 'errstr'.
321 static void reply_status(afb_req_t req, int status)
323 reply(req, status ? NULL : json_object_get(json_true));
327 * On query "runnables"
329 static void runnables(afb_req_t req)
332 struct json_object *resp;
334 /* get the language */
335 lang = get_lang(req);
337 /* get the details */
338 resp = afm_udb_applications_public(afudb, afb_req_get_uid(req), lang);
339 afb_req_success(req, resp, NULL);
345 static void detail(afb_req_t req)
349 struct json_object *resp;
351 /* scan the request */
352 if (!onappid(req, _detail_, &appid))
355 /* get the language */
356 lang = get_lang(req);
358 /* wants details for appid */
359 resp = afm_udb_get_application_public(afudb, appid, afb_req_get_uid(req), lang);
361 afb_req_success(req, resp, NULL);
369 static void start(afb_req_t req)
372 struct json_object *appli, *resp;
375 /* scan the request */
376 if (!onappid(req, _start_, &appid))
379 /* get the application */
380 appli = afm_udb_get_application_private(afudb, appid, afb_req_get_uid(req));
386 /* launch the application */
387 runid = afm_urun_start(appli, afb_req_get_uid(req));
396 wrap_json_pack(&resp, "{si}", _runid_, runid);
398 wrap_json_pack(&resp, "i", runid);
400 afb_req_success(req, resp, NULL);
406 static void once(afb_req_t req)
409 struct json_object *appli, *resp;
412 /* scan the request */
413 if (!onappid(req, _once_, &appid))
416 /* get the application */
417 appli = afm_udb_get_application_private(afudb, appid, afb_req_get_uid(req));
423 /* launch the application */
424 runid = afm_urun_once(appli, afb_req_get_uid(req));
430 /* returns the state */
431 resp = afm_urun_state(afudb, runid, afb_req_get_uid(req));
432 afb_req_success(req, resp, NULL);
438 static void pause(afb_req_t req)
441 if (onrunid(req, "pause", &runid)) {
442 status = afm_urun_pause(runid, afb_req_get_uid(req));
443 reply_status(req, status);
448 * On query "resume" from 'smsg' with parameters of 'obj'.
450 static void resume(afb_req_t req)
453 if (onrunid(req, "resume", &runid)) {
454 status = afm_urun_resume(runid, afb_req_get_uid(req));
455 reply_status(req, status);
460 * On query "terminate"
462 static void terminate(afb_req_t req)
465 if (onrunid(req, "terminate", &runid)) {
466 status = afm_urun_terminate(runid, afb_req_get_uid(req));
467 reply_status(req, status);
474 static void runners(afb_req_t req)
476 struct json_object *resp;
477 resp = afm_urun_list(afudb, afb_req_get_uid(req));
478 afb_req_success(req, resp, NULL);
484 static void state(afb_req_t req)
487 struct json_object *resp;
488 if (onrunid(req, "state", &runid)) {
489 resp = afm_urun_state(afudb, runid, afb_req_get_uid(req));
495 * On querying installation of widget(s)
497 static void install(afb_req_t req)
503 struct wgt_info *ifo;
504 struct json_object *json;
505 struct json_object *resp;
507 /* default settings */
512 /* scan the request */
513 json = afb_req_json(req);
514 if (wrap_json_unpack(json, "s", &wgtfile)
515 && wrap_json_unpack(json, "{ss s?s s?b s?b}",
519 "reload", &reload)) {
520 return bad_request(req);
523 /* install the widget */
524 ifo = install_widget(wgtfile, root, force);
526 afb_req_fail_f(req, "failed", "installation failed: %m");
528 afm_udb_update(afudb);
529 /* reload if needed */
533 /* build the response */
534 wrap_json_pack(&resp, "{ss}", _added_, wgt_info_desc(ifo)->idaver);
535 afb_req_success(req, resp, NULL);
536 application_list_changed(_install_, wgt_info_desc(ifo)->idaver);
544 * On querying uninstallation of widget(s)
546 static void uninstall(afb_req_t req)
550 struct json_object *json;
553 /* default settings */
556 /* scan the request */
557 json = afb_req_json(req);
558 if (wrap_json_unpack(json, "s", &idaver)
559 && wrap_json_unpack(json, "{ss s?s}",
562 return bad_request(req);
565 /* install the widget */
566 rc = uninstall_widget(idaver, root);
568 afb_req_fail_f(req, "failed", "uninstallation failed: %m");
570 afm_udb_update(afudb);
571 afb_req_success(req, NULL, NULL);
572 application_list_changed(_uninstall_, idaver);
576 static void onsighup(int signal)
578 afm_udb_update(afudb);
579 application_list_changed(_update_, _update_);
582 static int init(afb_api_t api)
585 json_true = json_object_new_boolean(1);
588 afudb = afm_udb_create(1, 0, "afm-appli-");
590 ERROR("afm_udb_create failed");
594 signal(SIGHUP, onsighup);
596 /* create the event */
597 applist_changed_event = afb_api_make_event(api, _a_l_c_);
598 return -!afb_event_is_valid(applist_changed_event);
601 static const afb_verb_t verbs[] =
603 {.verb=_runnables_, .callback=runnables, .auth=&auth_detail, .info="Get list of runnable applications", .session=AFB_SESSION_CHECK },
604 {.verb=_detail_ , .callback=detail, .auth=&auth_detail, .info="Get the details for one application", .session=AFB_SESSION_CHECK },
605 {.verb=_start_ , .callback=start, .auth=&auth_start, .info="Start an application", .session=AFB_SESSION_CHECK },
606 {.verb=_once_ , .callback=once, .auth=&auth_start, .info="Start once an application", .session=AFB_SESSION_CHECK },
607 {.verb=_terminate_, .callback=terminate, .auth=&auth_kill, .info="Terminate a running application", .session=AFB_SESSION_CHECK },
608 {.verb=_pause_ , .callback=pause, .auth=&auth_kill, .info="Pause a running application", .session=AFB_SESSION_CHECK },
609 {.verb=_resume_ , .callback=resume, .auth=&auth_kill, .info="Resume a paused application", .session=AFB_SESSION_CHECK },
610 {.verb=_runners_ , .callback=runners, .auth=&auth_state, .info="Get the list of running applications", .session=AFB_SESSION_CHECK },
611 {.verb=_state_ , .callback=state, .auth=&auth_state, .info="Get the state of a running application", .session=AFB_SESSION_CHECK },
612 {.verb=_install_ , .callback=install, .auth=&auth_install, .info="Install an application using a widget file", .session=AFB_SESSION_CHECK },
613 {.verb=_uninstall_, .callback=uninstall, .auth=&auth_uninstall, .info="Uninstall an application", .session=AFB_SESSION_CHECK },
617 const afb_binding_t afbBindingExport = {
619 .specification = NULL,
620 .info = "Application Framework Master Service",
625 .noconcurrency = 1 /* relies on binder for serialization of requests */