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 _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";
67 static const struct afb_auth
69 .type = afb_auth_Permission,
70 .text = "urn:AGL:permission:afm:system:widget"
72 auth_perm_widget_install = {
73 .type = afb_auth_Permission,
74 .text = "urn:AGL:permission:afm:system:widget:install"
76 auth_perm_widget_uninstall = {
77 .type = afb_auth_Permission,
78 .text = "urn:AGL:permission:afm:system:widget:uninstall"
80 auth_perm_widget_preinstall = {
81 .type = afb_auth_Permission,
82 .text = "urn:AGL:permission:afm:system:widget:preinstall"
84 auth_perm_widget_detail = {
85 .type = afb_auth_Permission,
86 .text = "urn:AGL:permission:afm:system:widget:detail"
88 auth_perm_widget_start = {
89 .type = afb_auth_Permission,
90 .text = "urn:AGL:permission:afm:system:widget:start"
92 auth_perm_widget_view_all = {
93 .type = afb_auth_Permission,
94 .text = "urn:AGL:permission:afm:system:widget:view-all"
97 .type = afb_auth_Permission,
98 .text = "urn:AGL:permission:afm:system:runner"
100 auth_perm_runner_state = {
101 .type = afb_auth_Permission,
102 .text = "urn:AGL:permission:afm:system:runner:state"
104 auth_perm_runner_kill = {
105 .type = afb_auth_Permission,
106 .text = "urn:AGL:permission:afm:system:runner:kill"
111 .first = &auth_perm_widget,
112 .next = &auth_perm_widget_install
116 .first = &auth_perm_widget,
117 .next = &auth_perm_widget_uninstall
121 .first = &auth_perm_widget,
122 .next = &auth_perm_widget_preinstall
126 .first = &auth_perm_widget,
127 .next = &auth_perm_widget_detail
131 .first = &auth_perm_widget,
132 .next = &auth_perm_widget_start
136 .first = &auth_perm_widget,
137 .next = &auth_perm_widget_view_all
141 .first = &auth_perm_runner,
142 .next = &auth_perm_runner_state
146 .first = &auth_perm_runner,
147 .next = &auth_perm_runner_kill
154 static const char *rootdir = FWK_APP_DIR;
157 * the internal application database
159 static struct afm_udb *afudb;
162 * the event signaling that application list changed
164 static afb_event_t applist_changed_event;
167 * the preallocated true json_object
169 static struct json_object *json_true;
171 /* enforce daemon reload */
172 static void do_reloads()
174 systemd_daemon_reload(0);
175 systemd_unit_restart_name(0, "sockets.target");
178 /* common bad request reply */
179 static void bad_request(afb_req_t req)
181 afb_req_fail(req, _bad_request_, NULL);
184 /* common not found reply */
185 static void not_found(afb_req_t req)
187 afb_req_fail(req, _not_found_, NULL);
190 /* common can't start reply */
191 static void cant_start(afb_req_t req)
193 afb_req_fail(req, _cannot_start_, NULL);
197 * Broadcast the event "application-list-changed".
198 * This event is sent was the event "changed" is received from dbus.
200 static void application_list_changed(const char *operation, const char *data)
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);
208 * Retrieve the required language from 'req'.
210 static const char *get_lang(afb_req_t req)
214 /* get the optional language */
215 lang = afb_req_value(req, _lang_);
217 /* TODO use the req to get the lang of the session (if any) */
224 * retrieves the 'appid' in parameters received with the
225 * request 'req' for the 'method'.
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.
231 static int onappid(afb_req_t req, const char *method, const char **appid)
233 struct json_object *json;
235 /* get the paramaters of the request */
236 json = afb_req_json(req);
238 /* get the appid if any */
239 if (!wrap_json_unpack(json, "s", appid)
240 || !wrap_json_unpack(json, "{si}", _id_, appid)) {
242 INFO("method %s called for %s", method, *appid);
246 /* nothing appropriate */
247 INFO("bad request method %s: %s", method,
248 json_object_to_json_string(json));
254 * retrieves the 'runid' in parameters received with the
255 * request 'req' for the 'method'.
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.
261 static int onrunid(afb_req_t req, const char *method, int *runid)
263 struct json_object *json;
266 /* get the paramaters of the request */
267 json = afb_req_json(req);
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);
276 /* get the appid if any */
277 if (!onappid(req, method, &appid))
280 /* search the runid of the appid */
281 *runid = afm_urun_search_runid(afudb, appid, afb_req_get_uid(req));
283 /* nothing appropriate */
284 INFO("method %s can't get runid for %s: %m", method,
291 INFO("method %s called for %s -> %d", method, appid, *runid);
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'.
299 static void reply(afb_req_t req, struct json_object *resp)
302 afb_req_reply(req, resp, NULL, NULL);
304 afb_req_reply(req, NULL, "failed", strerror(errno));
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'.
311 static void reply_status(afb_req_t req, int status)
313 reply(req, status ? NULL : json_object_get(json_true));
317 * On query "runnables"
319 static void runnables(afb_req_t req)
322 struct json_object *resp;
324 /* get the language */
325 lang = get_lang(req);
327 /* get the details */
328 resp = afm_udb_applications_public(afudb, afb_req_get_uid(req), lang);
329 afb_req_success(req, resp, NULL);
335 static void detail(afb_req_t req)
339 struct json_object *resp;
341 /* scan the request */
342 if (!onappid(req, _detail_, &appid))
345 /* get the language */
346 lang = get_lang(req);
348 /* wants details for appid */
349 resp = afm_udb_get_application_public(afudb, appid, afb_req_get_uid(req), lang);
351 afb_req_success(req, resp, NULL);
359 static void start(afb_req_t req)
362 struct json_object *appli, *resp;
365 /* scan the request */
366 if (!onappid(req, _start_, &appid))
369 /* get the application */
370 appli = afm_udb_get_application_private(afudb, appid, afb_req_get_uid(req));
376 /* launch the application */
377 runid = afm_urun_start(appli, afb_req_get_uid(req));
386 wrap_json_pack(&resp, "{si}", _runid_, runid);
388 wrap_json_pack(&resp, "i", runid);
390 afb_req_success(req, resp, NULL);
396 static void once(afb_req_t req)
399 struct json_object *appli, *resp;
402 /* scan the request */
403 if (!onappid(req, _once_, &appid))
406 /* get the application */
407 appli = afm_udb_get_application_private(afudb, appid, afb_req_get_uid(req));
413 /* launch the application */
414 runid = afm_urun_once(appli, afb_req_get_uid(req));
420 /* returns the state */
421 resp = afm_urun_state(afudb, runid, afb_req_get_uid(req));
422 afb_req_success(req, resp, NULL);
428 static void pause(afb_req_t req)
431 if (onrunid(req, "pause", &runid)) {
432 status = afm_urun_pause(runid, afb_req_get_uid(req));
433 reply_status(req, status);
438 * On query "resume" from 'smsg' with parameters of 'obj'.
440 static void resume(afb_req_t req)
443 if (onrunid(req, "resume", &runid)) {
444 status = afm_urun_resume(runid, afb_req_get_uid(req));
445 reply_status(req, status);
450 * On query "terminate"
452 static void terminate(afb_req_t req)
455 if (onrunid(req, "terminate", &runid)) {
456 status = afm_urun_terminate(runid, afb_req_get_uid(req));
457 reply_status(req, status);
464 static void runners(afb_req_t req)
466 struct json_object *resp;
467 resp = afm_urun_list(afudb, afb_req_get_uid(req));
468 afb_req_success(req, resp, NULL);
474 static void state(afb_req_t req)
477 struct json_object *resp;
478 if (onrunid(req, "state", &runid)) {
479 resp = afm_urun_state(afudb, runid, afb_req_get_uid(req));
485 * On querying installation of widget(s)
487 static void install(afb_req_t req)
493 struct wgt_info *ifo;
494 struct json_object *json;
495 struct json_object *resp;
497 /* default settings */
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}",
509 "reload", &reload)) {
510 return bad_request(req);
513 /* install the widget */
514 ifo = install_widget(wgtfile, root, force);
516 afb_req_fail_f(req, "failed", "installation failed: %m");
518 afm_udb_update(afudb);
519 /* reload if needed */
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);
534 * On querying uninstallation of widget(s)
536 static void uninstall(afb_req_t req)
540 struct json_object *json;
543 /* default settings */
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}",
552 return bad_request(req);
555 /* install the widget */
556 rc = uninstall_widget(idaver, root);
558 afb_req_fail_f(req, "failed", "uninstallation failed: %m");
560 afm_udb_update(afudb);
561 afb_req_success(req, NULL, NULL);
562 application_list_changed(_uninstall_, idaver);
566 static void onsighup(int signal)
568 afm_udb_update(afudb);
569 application_list_changed(_update_, _update_);
572 static int init(afb_api_t api)
575 json_true = json_object_new_boolean(1);
578 afudb = afm_udb_create(1, 0, "afm-appli-");
580 ERROR("afm_udb_create failed");
584 signal(SIGHUP, onsighup);
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);
591 static const afb_verb_t verbs[] =
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 },
607 const afb_binding_t afbBindingExport = {
609 .specification = NULL,
610 .info = "Application Framework Master Service",
615 .noconcurrency = 1 /* relies on binder for serialization of requests */