aboutsummaryrefslogtreecommitdiffstats
path: root/src/afm-binding.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/afm-binding.c')
-rw-r--r--src/afm-binding.c487
1 files changed, 487 insertions, 0 deletions
diff --git a/src/afm-binding.c b/src/afm-binding.c
new file mode 100644
index 0000000..b111f09
--- /dev/null
+++ b/src/afm-binding.c
@@ -0,0 +1,487 @@
+/*
+ * Copyright (C) 2015, 2016, 2017 IoT.bzh
+ * Author "Fulup Ar Foll"
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define _GNU_SOURCE /* See feature_test_macros(7) */
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+#include <json-c/json.h>
+
+#define AFB_BINDING_VERSION 2
+#include <afb/afb-binding.h>
+
+#include "verbose.h"
+#include "utils-systemd.h"
+#include "afm.h"
+#include "afm-udb.h"
+#include "afm-urun.h"
+#include "wgt-info.h"
+#include "wgtpkg-install.h"
+#include "wgtpkg-uninstall.h"
+#include "wrap-json.h"
+
+static const char _added_[] = "added";
+static const char _a_l_c_[] = "application-list-changed";
+static const char _detail_[] = "detail";
+static const char _id_[] = "id";
+static const char _install_[] = "install";
+static const char _not_found_[] = "not-found";
+static const char _once_[] = "once";
+static const char _pause_[] = "pause";
+static const char _resume_[] = "resume";
+static const char _runid_[] = "runid";
+static const char _runnables_[] = "runnables";
+static const char _runners_[] = "runners";
+static const char _start_[] = "start";
+static const char _state_[] = "state";
+static const char _terminate_[] = "terminate";
+static const char _uninstall_[] = "uninstall";
+
+static const char *rootdir = FWK_APP_DIR;
+static struct afb_event applist_changed_event;
+static struct afm_udb *afudb;
+static struct json_object *json_true;
+
+static void do_reloads()
+{
+ /* enforce daemon reload */
+ systemd_daemon_reload(0);
+ systemd_unit_restart_name(0, "sockets.target");
+}
+
+static void bad_request(struct afb_req req)
+{
+ afb_req_fail(req, "bad-request", NULL);
+}
+
+static void not_found(struct afb_req req)
+{
+ afb_req_fail(req, _not_found_, NULL);
+}
+
+static void cant_start(struct afb_req req)
+{
+ afb_req_fail(req, "cannot-start", NULL);
+}
+
+/*
+ * Broadcast the event "application-list-changed".
+ * This event is sent was the event "changed" is received from dbus.
+ */
+static void application_list_changed(const char *operation, const char *data)
+{
+ struct json_object *e = NULL;
+ wrap_json_pack(&e, "{ss ss}", "operation", operation, "data", data);
+ afb_event_broadcast(applist_changed_event, e);
+}
+
+/*
+ * retrieves the 'runid' in parameters received with the
+ * request 'req' for the 'method'.
+ *
+ * Returns 1 in case of success.
+ * Otherwise, if the 'runid' can't be retrived, an error stating
+ * the bad request is replied for 'req' and 0 is returned.
+ */
+static int onrunid(struct afb_req req, const char *method, int *runid)
+{
+ struct json_object *json;
+
+ json = afb_req_json(req);
+ if (wrap_json_unpack(json, "s", runid)
+ || wrap_json_unpack(json, "{ss}", "runid", runid)) {
+ INFO("bad request method %s: %s", method,
+ json_object_to_json_string(json));
+ bad_request(req);
+ return 0;
+ }
+
+ INFO("method %s called for %d", method, *runid);
+ return 1;
+}
+
+/*
+ * Sends the reply 'resp' to the request 'req' if 'resp' is not NULLzero.
+ * Otherwise, when 'resp' is NULL replies the error string 'errstr'.
+ */
+static void reply(struct afb_req req, struct json_object *resp, const char *errstr)
+{
+ if (!resp)
+ afb_req_fail(req, errstr, NULL);
+ else
+ afb_req_success(req, resp, NULL);
+}
+
+/*
+ * Sends the reply "true" to the request 'req' if 'status' is zero.
+ * Otherwise, when 'status' is not zero replies the error string 'errstr'.
+ */
+static void reply_status(struct afb_req req, int status, const char *errstr)
+{
+ reply(req, status ? NULL : json_object_get(json_true), errstr);
+}
+
+/*
+ * On query "runnables"
+ */
+static void runnables(struct afb_req req)
+{
+ struct json_object *resp;
+ INFO("method runnables called");
+ resp = afm_udb_applications_public(afudb);
+ afb_req_success(req, resp, NULL);
+}
+
+/*
+ * On query "detail"
+ */
+static void detail(struct afb_req req)
+{
+ const char *appid;
+ struct json_object *resp, *json;
+
+ /* scan the request */
+ json = afb_req_json(req);
+ if (wrap_json_unpack(json, "s", &appid)
+ || wrap_json_unpack(json, "{ss}", _id_, &appid)) {
+ bad_request(req);
+ return;
+ }
+
+ /* wants details for appid */
+ INFO("method detail called for %s", appid);
+ resp = afm_udb_get_application_public(afudb, appid);
+ if (resp)
+ afb_req_success(req, resp, NULL);
+ else
+ not_found(req);
+}
+
+/*
+ * On query "start"
+ */
+static void start(struct afb_req req)
+{
+ const char *appid;
+ struct json_object *appli, *resp, *json;
+ int runid;
+
+ /* scan the request */
+ json = afb_req_json(req);
+ if (wrap_json_unpack(json, "s", &appid)
+ || wrap_json_unpack(json, "{ss}", _id_, &appid)) {
+ bad_request(req);
+ return;
+ }
+
+ /* get the application */
+ INFO("method start called for %s", appid);
+ appli = afm_udb_get_application_private(afudb, appid);
+ if (appli == NULL) {
+ not_found(req);
+ return;
+ }
+
+ /* launch the application */
+ runid = afm_urun_start(appli);
+ if (runid <= 0) {
+ cant_start(req);
+ return;
+ }
+
+ /* returns */
+ resp = NULL;
+ wrap_json_pack(&resp, "{si}", _runid_, runid);
+ afb_req_success(req, resp, NULL);
+}
+
+/*
+ * On query "once"
+ */
+static void once(struct afb_req req)
+{
+ const char *appid;
+ struct json_object *appli, *resp, *json;
+ int runid;
+
+ /* scan the request */
+ json = afb_req_json(req);
+ if (wrap_json_unpack(json, "s", &appid)
+ || wrap_json_unpack(json, "{ss}", _id_, &appid)) {
+ bad_request(req);
+ return;
+ }
+
+ /* get the application */
+ INFO("method once called for %s", appid);
+ appli = afm_udb_get_application_private(afudb, appid);
+ if (appli == NULL) {
+ not_found(req);
+ return;
+ }
+
+ /* launch the application */
+ runid = afm_urun_once(appli);
+ if (runid <= 0) {
+ cant_start(req);
+ return;
+ }
+
+ /* returns the state */
+ resp = afm_urun_state(afudb, runid);
+ afb_req_success(req, resp, NULL);
+}
+
+/*
+ * On query "pause"
+ */
+static void pause(struct afb_req req)
+{
+ int runid, status;
+ if (onrunid(req, "pause", &runid)) {
+ status = afm_urun_pause(runid);
+ reply_status(req, status, _not_found_);
+ }
+}
+
+/*
+ * On query "resume" from 'smsg' with parameters of 'obj'.
+ */
+static void resume(struct afb_req req)
+{
+ int runid, status;
+ if (onrunid(req, "resume", &runid)) {
+ status = afm_urun_resume(runid);
+ reply_status(req, status, _not_found_);
+ }
+}
+
+/*
+ * On query "terminate"
+ */
+static void terminate(struct afb_req req)
+{
+ int runid, status;
+ if (onrunid(req, "terminate", &runid)) {
+ status = afm_urun_terminate(runid);
+ reply_status(req, status, _not_found_);
+ }
+}
+
+/*
+ * On query "runners"
+ */
+static void runners(struct afb_req req)
+{
+ struct json_object *resp;
+ INFO("method runners called");
+ resp = afm_urun_list(afudb);
+ afb_req_success(req, resp, NULL);
+}
+
+/*
+ * On query "state"
+ */
+static void state(struct afb_req req)
+{
+ int runid;
+ struct json_object *resp;
+ if (onrunid(req, "state", &runid)) {
+ resp = afm_urun_state(afudb, runid);
+ reply(req, resp, _not_found_);
+ }
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+static void install(struct afb_req req)
+{
+ const char *wgtfile;
+ const char *root;
+ int force;
+ int reload;
+ struct wgt_info *ifo;
+ struct json_object *json;
+ struct json_object *resp;
+
+ /* default settings */
+ root = rootdir;
+ force = 0;
+ reload = 1;
+
+ /* scan the request */
+ json = afb_req_json(req);
+ if (wrap_json_unpack(json, "s", &wgtfile)
+ || wrap_json_unpack(json, "{ss s?s s?b s?b}",
+ "widget", &wgtfile,
+ "root", &root,
+ "force", &force,
+ "reload", &reload)) {
+ return bad_request(req);
+ }
+
+ /* install the widget */
+ ifo = install_widget(wgtfile, root, force);
+ if (ifo == NULL)
+ afb_req_fail_f(req, "failed", "installation failed: %m");
+ else {
+ /* reload if needed */
+ if (reload)
+ do_reloads();
+
+ /* build the response */
+ wrap_json_pack(&resp, "{ss}", _added_, wgt_info_desc(ifo)->idaver);
+ afb_req_success(req, resp, NULL);
+ application_list_changed(_install_, wgt_info_desc(ifo)->idaver);
+
+ /* clean-up */
+ wgt_info_unref(ifo);
+ }
+}
+
+static void uninstall(struct afb_req req)
+{
+ const char *idaver;
+ const char *root;
+ struct json_object *json;
+ int rc;
+
+ /* default settings */
+ root = rootdir;
+
+ /* scan the request */
+ json = afb_req_json(req);
+ if (wrap_json_unpack(json, "s", &idaver)
+ || wrap_json_unpack(json, "{ss s?s}",
+ _id_, &idaver,
+ "root", &root)) {
+ return bad_request(req);
+ }
+
+ /* install the widget */
+ rc = uninstall_widget(idaver, root);
+ if (rc)
+ afb_req_fail_f(req, "failed", "uninstallation failed: %m");
+ else {
+ afb_req_success(req, NULL, NULL);
+ application_list_changed(_uninstall_, idaver);
+ }
+}
+
+static int init()
+{
+ /* init database */
+ afudb = afm_udb_create(1, 1, "afm-appli-");
+ if (!afudb) {
+ ERROR("afm_udb_create failed");
+ return -1;
+ }
+
+ /* set the systemd's buses */
+ systemd_set_bus(0, afb_daemon_get_system_bus());
+ systemd_set_bus(1, afb_daemon_get_user_bus());
+
+ /* create TRUE */
+ json_true = json_object_new_boolean(1);
+
+ /* create the event */
+ applist_changed_event = afb_daemon_make_event(_a_l_c_);
+ return -!afb_event_is_valid(applist_changed_event);
+}
+
+static const struct afb_auth
+ auth_install = {
+ .type = afb_auth_Permission,
+ .text = "urn:AGL:permission:afm:system:widget:install"
+ },
+ auth_uninstall = {
+ .type = afb_auth_Permission,
+ .text = "urn:AGL:permission:afm:system:widget:uninstall"
+ }
+;
+
+static const struct afb_verb_v2 verbs[] =
+{
+ {_runnables_, runnables, NULL, "Get list of runnable applications", AFB_SESSION_CHECK_V2 },
+ {_detail_ , detail, NULL, "Get the details for one application", AFB_SESSION_CHECK_V2 },
+ {_start_ , start, NULL, "Start an application", AFB_SESSION_CHECK_V2 },
+ {_once_ , once, NULL, "Start once an application", AFB_SESSION_CHECK_V2 },
+ {_terminate_, terminate, NULL, "Terminate a running application", AFB_SESSION_CHECK_V2 },
+ {_pause_ , pause, NULL, "Pause a running application", AFB_SESSION_CHECK_V2 },
+ {_resume_ , resume, NULL, "Resume a paused application", AFB_SESSION_CHECK_V2 },
+ {_runners_ , runners, NULL, "Get the list of running applications", AFB_SESSION_CHECK_V2 },
+ {_state_ , state, NULL, "Get the state of a running application", AFB_SESSION_CHECK_V2 },
+ {_install_ , install, NULL, "Install an application using a widget file", AFB_SESSION_CHECK_V2 },
+ {_uninstall_, uninstall, NULL, "Uninstall an application", AFB_SESSION_CHECK_V2 },
+ { NULL, NULL, NULL, NULL, 0 }
+};
+
+const struct afb_binding_v2 afbBindingV2 = {
+ .api = "afm-main",
+ .specification = NULL,
+ .info = "Application Framework Master Service",
+ .verbs = verbs,
+ .preinit = NULL,
+ .init = init,
+ .onevent = NULL,
+ .noconcurrency = 1 /* relies on binder for serialisation of requests */
+};
+