From a1f7f7d2cd79c9391368d0c017452566a5edc44c Mon Sep 17 00:00:00 2001
From: José Bollo <jose.bollo@iot.bzh>
Date: Mon, 14 Nov 2016 11:43:46 +0100
Subject: afm-user-daemon: Adds method "once"
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Adds the method "once" that is intended to start
an application in local mode if it doesn't already
run. Returns the state of the running application.

Change-Id: I4dfff06fa2d3e95f13a2436a2f1af9174799ddcf
Signed-off-by: José Bollo <jose.bollo@iot.bzh>
---
 docs/afm-user-daemon.md | 26 +++++++++++++++++++++++++-
 scripts/afm-util        |  7 +++++++
 src/afm-main-binding.c  |  7 +++++++
 src/afm-run.c           | 36 ++++++++++++++++++++++++++++++++++++
 src/afm-run.h           |  1 +
 src/afm-user-daemon.c   | 38 +++++++++++++++++++++++++++++++++++++-
 src/afm-user-daemon.xml |  4 ++++
 7 files changed, 117 insertions(+), 2 deletions(-)

diff --git a/docs/afm-user-daemon.md b/docs/afm-user-daemon.md
index cb54b10..37cf0e4 100644
--- a/docs/afm-user-daemon.md
+++ b/docs/afm-user-daemon.md
@@ -398,7 +398,7 @@ user sessions.
 The **afm-user-daemon** is listening on destination name
 ***org.AGL.afm.user*** at object path ***/org/AGL/afm/user***
 on interface ***org.AGL.afm.user*** for following members:
- ***runnables***, ***detail***, ***start***, ***terminate***,
+ ***runnables***, ***detail***, ***start***, ***once***, ***terminate***,
 ***pause***, ***resume***, ***runners***, ***state***,
 ***install*** and ***uninstall***.
 
@@ -464,6 +464,10 @@ Here is the summary of ***afm-util***:
 
    start an instance of the widget of id
 
+ - **afm-util once      id  **:
+
+   run once an instance of the widget of id
+
  - **afm-util terminate  rid **:
 
    terminate the running instance rid
@@ -634,6 +638,26 @@ The field "mode" is a string equal to either "local" or "remote".
 
 ---
 
+#### Method org.AGL.afm.user.once
+
+**Description**:
+
+**Input**: the *id* of the application
+
+Either just a string:
+
+    "appli@x.y"
+
+Or an object containing field "id" of type string.
+
+    {"id":"appli@x.y"}
+
+**output**: The *state* of the application retrieved or launched.
+See *org.AGL.afm.user.state* to get a description of the returned
+object.
+
+---
+
 #### Method org.AGL.afm.user.terminate
 
 **Description**: Terminates the application attached to *runid*.
diff --git a/scripts/afm-util b/scripts/afm-util
index 243b43c..eb800d9 100755
--- a/scripts/afm-util
+++ b/scripts/afm-util
@@ -52,6 +52,11 @@ case "$1" in
     send start "\"$i\""
     ;;
 
+  once)
+    i=$2
+    send once "\"$i\""
+    ;;
+
   terminate|kill)
     i=$2
     send terminate "$i"
@@ -96,6 +101,8 @@ The commands are:
   run id
   start id       start an instance of the widget of id
 
+  once id        run once an instance of the widget of id
+
   kill rid
   terminate rid  terminate the running instance rid
 
diff --git a/src/afm-main-binding.c b/src/afm-main-binding.c
index 5141888..e8e08db 100644
--- a/src/afm-main-binding.c
+++ b/src/afm-main-binding.c
@@ -34,6 +34,7 @@ static const char _id_[]        = "id";
 static const char _install_[]   = "install";
 static const char _local_[]     = "local";
 static const char _mode_[]      = "mode";
+static const char _once_[]      = "once";
 static const char _pause_[]     = "pause";
 static const char _remote_[]    = "remote";
 static const char _resume_[]    = "resume";
@@ -294,6 +295,11 @@ static void start(struct afb_req request)
 	free(query);
 }
 
+static void once(struct afb_req request)
+{
+	call_appid(request, _once_);
+}
+
 static void terminate(struct afb_req request)
 {
 	call_runid(request, _terminate_);
@@ -375,6 +381,7 @@ static const struct afb_verb_desc_v1 verbs[] =
 	{_runnables_, AFB_SESSION_CHECK, runnables,  "Get list of runnable applications"},
 	{_detail_   , AFB_SESSION_CHECK, detail, "Get the details for one application"},
 	{_start_    , AFB_SESSION_CHECK, start, "Start an application"},
+	{_once_     , AFB_SESSION_CHECK, once, "Start once an application"},
 	{_terminate_, AFB_SESSION_CHECK, terminate, "Terminate a running application"},
 	{_pause_    , AFB_SESSION_CHECK, pause, "Pause a running application"},
 	{_resume_   , AFB_SESSION_CHECK, resume, "Resume a paused application"},
diff --git a/src/afm-run.c b/src/afm-run.c
index 537ba62..f4f55e5 100644
--- a/src/afm-run.c
+++ b/src/afm-run.c
@@ -247,6 +247,25 @@ static struct apprun *getrunner(int runid)
 	return result;
 }
 
+/*
+ * Get first runner of 'appli' (NULL if not found)
+ */
+static struct apprun *getrunner_appli(json_object *appli)
+{
+	int i;
+	struct apprun *result;
+
+	for (i = 0 ; i < ROOT_RUNNERS_COUNT ; i++) {
+		result = runners_by_pgid[i];
+		while (result != NULL) {
+			if (result->appli == appli)
+				return result;
+			result = result->next_by_pgid;
+		}
+	}
+	return NULL;
+}
+
 /*
  * Free an existing 'runner'
  */
@@ -592,6 +611,23 @@ int afm_run_start(struct json_object *appli, enum afm_launch_mode mode,
 	return rc;
 }
 
+/*
+ * Returns the runid of a previously started application 'appli'
+ * or if none is running, starts the application described by 'appli'
+ * in local mode.
+ *
+ * A reference to 'appli' is kept during the live of the
+ * runner. This is made using json_object_get. Thus be aware
+ * that further modifications to 'appli' might create errors.
+ *
+ * Returns the runid in case of success or -1 in case of error
+ */
+int afm_run_once(struct json_object *appli)
+{
+	struct apprun *runner = getrunner_appli(appli);
+	return runner && is_alive(runner) ? runner->runid : afm_run_start(appli, mode_local, NULL);
+}
+
 /*
  * Terminates the runner of 'runid'
  *
diff --git a/src/afm-run.h b/src/afm-run.h
index 6e3469a..00ea4e8 100644
--- a/src/afm-run.h
+++ b/src/afm-run.h
@@ -17,6 +17,7 @@
 */
 
 extern int afm_run_start(struct json_object *appli, enum afm_launch_mode mode, char **uri);
+extern int afm_run_once(struct json_object *appli);
 extern int afm_run_terminate(int runid);
 extern int afm_run_pause(int runid);
 extern int afm_run_resume(int runid);
diff --git a/src/afm-user-daemon.c b/src/afm-user-daemon.c
index e71e011..d4a9558 100644
--- a/src/afm-user-daemon.c
+++ b/src/afm-user-daemon.c
@@ -185,7 +185,6 @@ static void on_detail(struct sd_bus_message *smsg, struct json_object *obj, void
 	json_object_put(resp);
 }
 
-
 /*
  * On query "start" from 'smsg' with parameters of 'obj'.
  */
@@ -253,6 +252,42 @@ static void on_start(struct sd_bus_message *smsg, struct json_object *obj, void
 	free(uri);
 }
 
+/*
+ * On query "once" from 'smsg' with parameters of 'obj'.
+ */
+static void on_once(struct sd_bus_message *smsg, struct json_object *obj, void *unused)
+{
+	const char *appid;
+	struct json_object *appli, *resp;
+	int runid;
+
+	/* get the parameters */
+	if (!j_read_string(obj, &appid) && !j_read_string_at(obj, "id", &appid)) {
+		jbus_reply_error_s(smsg, error_bad_request);
+		return;
+	}
+
+	/* get the application */
+	INFO("method once called for %s", appid);
+	appli = afm_db_get_application(afdb, appid);
+	if (appli == NULL) {
+		jbus_reply_error_s(smsg, error_not_found);
+		return;
+	}
+
+	/* launch the application */
+	runid = afm_run_once(appli);
+	if (runid <= 0) {
+		jbus_reply_error_s(smsg, error_cant_start);
+		return;
+	}
+
+	/* returns the state */
+	resp = afm_run_state(runid);
+	reply(smsg, resp, error_not_found);
+	json_object_put(resp);
+}
+
 /*
  * On query "pause" from 'smsg' with parameters of 'obj'.
  */
@@ -600,6 +635,7 @@ int main(int ac, char **av)
 	if (jbus_add_service_j(user_bus, "runnables", on_runnables, NULL)
 	 || jbus_add_service_j(user_bus, "detail",    on_detail, NULL)
 	 || jbus_add_service_j(user_bus, "start",     on_start, NULL)
+	 || jbus_add_service_j(user_bus, "once",      on_once, NULL)
 	 || jbus_add_service_j(user_bus, "terminate", on_terminate, NULL)
 	 || jbus_add_service_j(user_bus, "pause",     on_pause, NULL)
 	 || jbus_add_service_j(user_bus, "resume",    on_resume, NULL)
diff --git a/src/afm-user-daemon.xml b/src/afm-user-daemon.xml
index 772e45d..48ff7cd 100644
--- a/src/afm-user-daemon.xml
+++ b/src/afm-user-daemon.xml
@@ -13,6 +13,10 @@
       <arg name="in" type="s" direction="in"/>
       <arg name="out" type="s" direction="out"/>
     </method>
+    <method name="once">
+      <arg name="in" type="s" direction="in"/>
+      <arg name="out" type="s" direction="out"/>
+    </method>
     <method name="terminate">
       <arg name="in" type="s" direction="in"/>
       <arg name="out" type="s" direction="out"/>
-- 
cgit