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) */
25 #include <json-c/json.h>
27 #define AFB_BINDING_VERSION 3
28 #include <afb/afb-binding.h>
31 #include "utils-systemd.h"
35 #include "wgtpkg-install.h"
36 #include "wgtpkg-uninstall.h"
37 #include "wrap-json.h"
42 static const char _added_[] = "added";
43 static const char _a_l_c_[] = "application-list-changed";
44 static const char _bad_request_[] = "bad-request";
45 static const char _cannot_start_[] = "cannot-start";
46 static const char _detail_[] = "detail";
47 static const char _id_[] = "id";
48 static const char _install_[] = "install";
49 static const char _lang_[] = "lang";
50 static const char _not_found_[] = "not-found";
51 static const char _once_[] = "once";
52 static const char _pause_[] = "pause";
53 static const char _resume_[] = "resume";
54 static const char _runid_[] = "runid";
55 static const char _runnables_[] = "runnables";
56 static const char _runners_[] = "runners";
57 static const char _start_[] = "start";
58 static const char _state_[] = "state";
59 static const char _terminate_[] = "terminate";
60 static const char _uninstall_[] = "uninstall";
61 static const char _update_[] = "update";
66 static const struct afb_auth
68 .type = afb_auth_Permission,
69 .text = "urn:AGL:permission:afm:system:widget"
71 auth_perm_widget_install = {
72 .type = afb_auth_Permission,
73 .text = "urn:AGL:permission:afm:system:widget:install"
75 auth_perm_widget_uninstall = {
76 .type = afb_auth_Permission,
77 .text = "urn:AGL:permission:afm:system:widget:uninstall"
79 auth_perm_widget_preinstall = {
80 .type = afb_auth_Permission,
81 .text = "urn:AGL:permission:afm:system:widget:preinstall"
83 auth_perm_widget_detail = {
84 .type = afb_auth_Permission,
85 .text = "urn:AGL:permission:afm:system:widget:detail"
87 auth_perm_widget_start = {
88 .type = afb_auth_Permission,
89 .text = "urn:AGL:permission:afm:system:widget:start"
91 auth_perm_widget_view_all = {
92 .type = afb_auth_Permission,
93 .text = "urn:AGL:permission:afm:system:widget:view-all"
96 .type = afb_auth_Permission,
97 .text = "urn:AGL:permission:afm:system:runner"
99 auth_perm_runner_state = {
100 .type = afb_auth_Permission,
101 .text = "urn:AGL:permission:afm:system:runner:state"
103 auth_perm_runner_kill = {
104 .type = afb_auth_Permission,
105 .text = "urn:AGL:permission:afm:system:runner:kill"
110 .first = &auth_perm_widget,
111 .next = &auth_perm_widget_install
115 .first = &auth_perm_widget,
116 .next = &auth_perm_widget_uninstall
120 .first = &auth_perm_widget,
121 .next = &auth_perm_widget_preinstall
125 .first = &auth_perm_widget,
126 .next = &auth_perm_widget_detail
130 .first = &auth_perm_widget,
131 .next = &auth_perm_widget_start
135 .first = &auth_perm_widget,
136 .next = &auth_perm_widget_view_all
140 .first = &auth_perm_runner,
141 .next = &auth_perm_runner_state
145 .first = &auth_perm_runner,
146 .next = &auth_perm_runner_kill
153 static const char *rootdir = FWK_APP_DIR;
156 * the internal application database
158 static struct afm_udb *afudb;
161 * the event signaling that application list changed
163 static afb_event_t applist_changed_event;
166 * the preallocated true json_object
168 static struct json_object *json_true;
170 /* enforce daemon reload */
171 static void do_reloads()
173 systemd_daemon_reload(0);
174 systemd_unit_restart_name(0, "sockets.target");
177 /* common bad request reply */
178 static void bad_request(afb_req_t req)
180 afb_req_fail(req, _bad_request_, NULL);
183 /* common not found reply */
184 static void not_found(afb_req_t req)
186 afb_req_fail(req, _not_found_, NULL);
189 /* common can't start reply */
190 static void cant_start(afb_req_t req)
192 afb_req_fail(req, _cannot_start_, NULL);
196 * Broadcast the event "application-list-changed".
197 * This event is sent was the event "changed" is received from dbus.
199 static void application_list_changed(const char *operation, const char *data)
201 struct json_object *e = NULL;
202 wrap_json_pack(&e, "{ss ss}", "operation", operation, "data", data);
203 afb_event_broadcast(applist_changed_event, e);
207 * Retrieve the required language from 'req'.
209 static const char *get_lang(afb_req_t req)
213 /* get the optional language */
214 lang = afb_req_value(req, _lang_);
216 /* TODO use the req to get the lang of the session (if any) */
223 * retrieves the 'appid' in parameters received with the
224 * request 'req' for the 'method'.
226 * Returns 1 in case of success.
227 * Otherwise, if the 'appid' can't be retrieved, an error stating
228 * the bad request is replied for 'req' and 0 is returned.
230 static int onappid(afb_req_t req, const char *method, const char **appid)
232 struct json_object *json;
234 /* get the paramaters of the request */
235 json = afb_req_json(req);
237 /* get the appid if any */
238 if (!wrap_json_unpack(json, "s", appid)
239 || !wrap_json_unpack(json, "{si}", _id_, appid)) {
241 INFO("method %s called for %s", method, *appid);
245 /* nothing appropriate */
246 INFO("bad request method %s: %s", method,
247 json_object_to_json_string(json));
253 * retrieves the 'runid' in parameters received with the
254 * request 'req' for the 'method'.
256 * Returns 1 in case of success.
257 * Otherwise, if the 'runid' can't be retrieved, an error stating
258 * the bad request is replied for 'req' and 0 is returned.
260 static int onrunid(afb_req_t req, const char *method, int *runid)
262 struct json_object *json;
265 /* get the paramaters of the request */
266 json = afb_req_json(req);
268 /* get the runid if any */
269 if (!wrap_json_unpack(json, "i", runid)
270 || !wrap_json_unpack(json, "{si}", _runid_, runid)) {
271 INFO("method %s called for %d", method, *runid);
275 /* get the appid if any */
276 if (!onappid(req, method, &appid))
279 /* search the runid of the appid */
280 *runid = afm_urun_search_runid(afudb, appid, afb_req_get_uid(req));
282 /* nothing appropriate */
283 INFO("method %s can't get runid for %s: %m", method,
290 INFO("method %s called for %s -> %d", method, appid, *runid);
295 * Sends the reply 'resp' to the request 'req' if 'resp' is not NULLzero.
296 * Otherwise, when 'resp' is NULL replies the error string 'errstr'.
298 static void reply(afb_req_t req, struct json_object *resp, const char *errstr)
301 afb_req_fail(req, errstr, NULL);
303 afb_req_success(req, resp, NULL);
307 * Sends the reply "true" to the request 'req' if 'status' is zero.
308 * Otherwise, when 'status' is not zero replies the error string 'errstr'.
310 static void reply_status(afb_req_t req, int status, const char *errstr)
312 reply(req, status ? NULL : json_object_get(json_true), errstr);
316 * On query "runnables"
318 static void runnables(afb_req_t req)
321 struct json_object *resp;
323 /* get the language */
324 lang = get_lang(req);
326 /* get the details */
327 resp = afm_udb_applications_public(afudb, afb_req_get_uid(req), lang);
328 afb_req_success(req, resp, NULL);
334 static void detail(afb_req_t req)
338 struct json_object *resp;
340 /* scan the request */
341 if (!onappid(req, _detail_, &appid))
344 /* get the language */
345 lang = get_lang(req);
347 /* wants details for appid */
348 resp = afm_udb_get_application_public(afudb, appid, afb_req_get_uid(req), lang);
350 afb_req_success(req, resp, NULL);
358 static void start(afb_req_t req)
361 struct json_object *appli, *resp;
364 /* scan the request */
365 if (!onappid(req, _start_, &appid))
368 /* get the application */
369 appli = afm_udb_get_application_private(afudb, appid, afb_req_get_uid(req));
375 /* launch the application */
376 runid = afm_urun_start(appli, afb_req_get_uid(req));
385 wrap_json_pack(&resp, "{si}", _runid_, runid);
387 wrap_json_pack(&resp, "i", runid);
389 afb_req_success(req, resp, NULL);
395 static void once(afb_req_t req)
398 struct json_object *appli, *resp;
401 /* scan the request */
402 if (!onappid(req, _once_, &appid))
405 /* get the application */
406 appli = afm_udb_get_application_private(afudb, appid, afb_req_get_uid(req));
412 /* launch the application */
413 runid = afm_urun_once(appli, afb_req_get_uid(req));
419 /* returns the state */
420 resp = afm_urun_state(afudb, runid, afb_req_get_uid(req));
421 afb_req_success(req, resp, NULL);
427 static void pause(afb_req_t req)
430 if (onrunid(req, "pause", &runid)) {
431 status = afm_urun_pause(runid, afb_req_get_uid(req));
432 reply_status(req, status, _not_found_);
437 * On query "resume" from 'smsg' with parameters of 'obj'.
439 static void resume(afb_req_t req)
442 if (onrunid(req, "resume", &runid)) {
443 status = afm_urun_resume(runid, afb_req_get_uid(req));
444 reply_status(req, status, _not_found_);
449 * On query "terminate"
451 static void terminate(afb_req_t req)
454 if (onrunid(req, "terminate", &runid)) {
455 status = afm_urun_terminate(runid, afb_req_get_uid(req));
456 reply_status(req, status, _not_found_);
463 static void runners(afb_req_t req)
465 struct json_object *resp;
466 resp = afm_urun_list(afudb, afb_req_get_uid(req));
467 afb_req_success(req, resp, NULL);
473 static void state(afb_req_t req)
476 struct json_object *resp;
477 if (onrunid(req, "state", &runid)) {
478 resp = afm_urun_state(afudb, runid, afb_req_get_uid(req));
479 reply(req, resp, _not_found_);
484 * On querying installation of widget(s)
486 static void install(afb_req_t req)
492 struct wgt_info *ifo;
493 struct json_object *json;
494 struct json_object *resp;
496 /* default settings */
501 /* scan the request */
502 json = afb_req_json(req);
503 if (wrap_json_unpack(json, "s", &wgtfile)
504 && wrap_json_unpack(json, "{ss s?s s?b s?b}",
508 "reload", &reload)) {
509 return bad_request(req);
512 /* install the widget */
513 ifo = install_widget(wgtfile, root, force);
515 afb_req_fail_f(req, "failed", "installation failed: %m");
517 afm_udb_update(afudb);
518 /* reload if needed */
522 /* build the response */
523 wrap_json_pack(&resp, "{ss}", _added_, wgt_info_desc(ifo)->idaver);
524 afb_req_success(req, resp, NULL);
525 application_list_changed(_install_, wgt_info_desc(ifo)->idaver);
533 * On querying uninstallation of widget(s)
535 static void uninstall(afb_req_t req)
539 struct json_object *json;
542 /* default settings */
545 /* scan the request */
546 json = afb_req_json(req);
547 if (wrap_json_unpack(json, "s", &idaver)
548 && wrap_json_unpack(json, "{ss s?s}",
551 return bad_request(req);
554 /* install the widget */
555 rc = uninstall_widget(idaver, root);
557 afb_req_fail_f(req, "failed", "uninstallation failed: %m");
559 afm_udb_update(afudb);
560 afb_req_success(req, NULL, NULL);
561 application_list_changed(_uninstall_, idaver);
565 static void onsighup(int signal)
567 afm_udb_update(afudb);
568 application_list_changed(_update_, _update_);
571 static int init(afb_api_t api)
574 json_true = json_object_new_boolean(1);
577 afudb = afm_udb_create(1, 0, "afm-appli-");
579 ERROR("afm_udb_create failed");
583 signal(SIGHUP, onsighup);
585 /* create the event */
586 applist_changed_event = afb_api_make_event(api, _a_l_c_);
587 return -!afb_event_is_valid(applist_changed_event);
590 static const afb_verb_t verbs[] =
592 {.verb=_runnables_, .callback=runnables, .auth=&auth_detail, .info="Get list of runnable applications", .session=AFB_SESSION_CHECK },
593 {.verb=_detail_ , .callback=detail, .auth=&auth_detail, .info="Get the details for one application", .session=AFB_SESSION_CHECK },
594 {.verb=_start_ , .callback=start, .auth=&auth_start, .info="Start an application", .session=AFB_SESSION_CHECK },
595 {.verb=_once_ , .callback=once, .auth=&auth_start, .info="Start once an application", .session=AFB_SESSION_CHECK },
596 {.verb=_terminate_, .callback=terminate, .auth=&auth_kill, .info="Terminate a running application", .session=AFB_SESSION_CHECK },
597 {.verb=_pause_ , .callback=pause, .auth=&auth_kill, .info="Pause a running application", .session=AFB_SESSION_CHECK },
598 {.verb=_resume_ , .callback=resume, .auth=&auth_kill, .info="Resume a paused application", .session=AFB_SESSION_CHECK },
599 {.verb=_runners_ , .callback=runners, .auth=&auth_state, .info="Get the list of running applications", .session=AFB_SESSION_CHECK },
600 {.verb=_state_ , .callback=state, .auth=&auth_state, .info="Get the state of a running application", .session=AFB_SESSION_CHECK },
601 {.verb=_install_ , .callback=install, .auth=&auth_install, .info="Install an application using a widget file", .session=AFB_SESSION_CHECK },
602 {.verb=_uninstall_, .callback=uninstall, .auth=&auth_uninstall, .info="Uninstall an application", .session=AFB_SESSION_CHECK },
606 const afb_binding_t afbBindingExport = {
608 .specification = NULL,
609 .info = "Application Framework Master Service",
614 .noconcurrency = 1 /* relies on binder for serialization of requests */