From 3d50d615fbd4322315dd66859d886576e208b36d Mon Sep 17 00:00:00 2001
From: José Bollo <jose.bollo@iot.bzh>
Date: Tue, 27 Feb 2018 15:07:54 +0100
Subject: Add configuration variables for runtime socket dirs
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

2 new configuration variables:

 - afm_platform_rundir  (default /run/platform)

     Path to location of platform runtime sockets

 - afm_users_rundir     (default /run/user)

     Path to location of users runtime sockets

Change-Id: I65007fee1ca2ce6f1247a67ba0b121f97c08f209
Signed-off-by: José Bollo <jose.bollo@iot.bzh>
---
 CMakeLists.txt                           |   2 +
 conf/system/afm-system-daemon.socket.in  |   2 +-
 conf/unit/afm-unit-debug.conf.in         |  12 ++--
 conf/unit/afm-unit.conf.in               |  10 +--
 conf/unit/generate-unit-conf/afm-unit    |   3 +-
 conf/unit/generate-unit-conf/service.inc |   6 +-
 conf/user/CMakeLists.txt                 |   4 +-
 conf/user/afm-user-daemon.service.in     |   2 +-
 scripts/CMakeLists.txt                   |   6 +-
 scripts/afm-debug                        |  90 ------------------------
 scripts/afm-debug.in                     |  90 ++++++++++++++++++++++++
 scripts/afm-util                         | 117 -------------------------------
 scripts/afm-util.in                      | 117 +++++++++++++++++++++++++++++++
 13 files changed, 233 insertions(+), 228 deletions(-)
 delete mode 100755 scripts/afm-debug
 create mode 100755 scripts/afm-debug.in
 delete mode 100755 scripts/afm-util
 create mode 100755 scripts/afm-util.in

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5c0dd0c..1e289fa 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -47,6 +47,8 @@ set(afm_user_appdir         "app-data" CACHE STRING "User subdirectory for appli
 set(afm_user_appdir_label   "User::App-Shared" CACHE STRING "Smack label of the user subdirectory for applications")
 set(systemd_units_root      "${CMAKE_INSTALL_FULL_LIBDIR}/systemd" CACHE STRING "Place where unit files are to be set")
 set(wgtpkg_trusted_cert_dir "${afm_confdir}/certs" CACHE STRING "Path to internal certificates")
+set(afm_platform_rundir     "/run/platform" CACHE STRING "Path to location of platform runtime sockets")
+set(afm_users_rundir        "/run/user" CACHE STRING "Path to location of users runtime sockets")
 
 if(USE_SIMULATION)
     set(SIMULATE_SECMGR ON)
diff --git a/conf/system/afm-system-daemon.socket.in b/conf/system/afm-system-daemon.socket.in
index eeae06f..6ab8d8d 100644
--- a/conf/system/afm-system-daemon.socket.in
+++ b/conf/system/afm-system-daemon.socket.in
@@ -1,7 +1,7 @@
 
 [Socket]
 SmackLabel=*
-ListenStream=/run/afm/apis/ws/afm-main
+ListenStream=@afm_platform_rundir@/apis/ws/afm-main
 FileDescriptorName=afm-main
 
 Service=afm-system-daemon.service
diff --git a/conf/unit/afm-unit-debug.conf.in b/conf/unit/afm-unit-debug.conf.in
index fbf530a..ee6b69b 100644
--- a/conf/unit/afm-unit-debug.conf.in
+++ b/conf/unit/afm-unit-debug.conf.in
@@ -145,11 +145,11 @@ Environment=LD_LIBRARY_PATH={{:#metadata.install-dir}}/lib
 Environment=XDG_DATA_HOME=/home/%i/app-data/{{:id}}
 Environment=XDG_CONFIG_HOME=/home/%i/app-data/{{:id}}
 Environment=XDG_CACHE_HOME=/home/%i/app-data/{{:id}}
-Environment=XDG_RUNTIME_DIR=/run/user/%i
-Environment=DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/%i/bus
+Environment=XDG_RUNTIME_DIR=@afm_users_rundir@/%i
+Environment=DBUS_SESSION_BUS_ADDRESS=unix:path=@afm_users_rundir@/%i/bus
 ; Needed to enable debug
 Environment=AFM_ID={{idaver}}{{^#target=main}}@{{:#target}}{{/#target=main}}
-EnvironmentFile=-/var/run/afm-debug/{{idaver}}{{^#target=main}}@{{:#target}}{{/#target=main}}.env
+EnvironmentFile=-@afm_platform_rundir@/debug/{{idaver}}{{^#target=main}}@{{:#target}}{{/#target=main}}.env
 SyslogIdentifier=afbd-{{idaver}}{{^#target=main}}@{{:#target}}{{/#target=main}}
 StandardInput=null
 StandardOutput=journal
@@ -172,9 +172,9 @@ ExecStart=/usr/bin/afb-daemon \
 	--roothttp={{#content.type=application/vnd.agl.service}}.{{/content.type=application/vnd.agl.service}}{{^content.type=application/vnd.agl.service}}{{#required-permission.urn:AGL:permission::public:no-htdocs}}.{{/required-permission.urn:AGL:permission::public:no-htdocs}}{{^required-permission.urn:AGL:permission::public:no-htdocs}}htdocs{{/required-permission.urn:AGL:permission::public:no-htdocs}}{{/content.type=application/vnd.agl.service}} \
 	{{#required-permission.urn:AGL:permission::public:applications:read}}--alias=/icons:{{:#metadata.icons-dir}}{{/required-permission.urn:AGL:permission::public:applications:read}} \
 	{{#required-api}} \
-		{{#value=auto|ws}}--ws-client=unix:/run/user/%i/apis/ws/{{name}}{{/value=auto|ws}} \
+		{{#value=auto|ws}}--ws-client=unix:@afm_users_rundir@/%i/apis/ws/{{name}}{{/value=auto|ws}} \
 		{{#value=dbus}}--dbus-client={{name}}{{/value=dbus}} \
-		{{#value=link}}--binding=/run/user/%i/apis/lib/{{name}}{{/value=link}} \
+		{{#value=link}}--binding=@afm_users_rundir@/%i/apis/lib/{{name}}{{/value=link}} \
 		{{#value=cloud}}--cloud-client={{name}}{{/value=cloud}} \
 		{{#value=local}}--binding={{:#metadata.install-dir}}/{{name}}{{/value=local}} \
 	{{/required-api}} \
@@ -217,7 +217,7 @@ DefaultDependencies=no
 SmackLabel=*
 SmackLabelIPIn=*
 SmackLabelIPOut=*
-ListenStream=/run/user/%i/apis/ws/{{name}}
+ListenStream=@afm_users_rundir@/%i/apis/ws/{{name}}
 FileDescriptorName={{name}}
 Service=afm-{{#required-permission.urn:AGL:permission::public:hidden}}service{{/required-permission.urn:AGL:permission::public:hidden}}{{^required-permission.urn:AGL:permission::public:hidden}}appli{{/required-permission.urn:AGL:permission::public:hidden}}-{{:id}}--{{:ver}}--{{:#target}}@%i.service
 %nl
diff --git a/conf/unit/afm-unit.conf.in b/conf/unit/afm-unit.conf.in
index 1824358..9fa3794 100644
--- a/conf/unit/afm-unit.conf.in
+++ b/conf/unit/afm-unit.conf.in
@@ -145,8 +145,8 @@ Environment=LD_LIBRARY_PATH={{:#metadata.install-dir}}/lib
 Environment=XDG_DATA_HOME=/home/%i/app-data/{{:id}}
 Environment=XDG_CONFIG_HOME=/home/%i/app-data/{{:id}}
 Environment=XDG_CACHE_HOME=/home/%i/app-data/{{:id}}
-Environment=XDG_RUNTIME_DIR=/run/user/%i
-Environment=DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/%i/bus
+Environment=XDG_RUNTIME_DIR=@afm_users_rundir@/%i
+Environment=DBUS_SESSION_BUS_ADDRESS=unix:path=@afm_users_rundir@/%i/bus
 SyslogIdentifier=afbd-{{idaver}}{{^#target=main}}@{{:#target}}{{/#target=main}}
 StandardInput=null
 StandardOutput=journal
@@ -172,9 +172,9 @@ ExecStart=/usr/bin/afb-daemon \
 	{{/content.type=application/vnd.agl.service}} \
 	{{#required-permission.urn:AGL:permission::public:applications:read}}--alias=/icons:{{:#metadata.icons-dir}}{{/required-permission.urn:AGL:permission::public:applications:read}} \
 	{{#required-api}} \
-		{{#value=auto|ws}}--ws-client=unix:/run/user/%i/apis/ws/{{name}}{{/value=auto|ws}} \
+		{{#value=auto|ws}}--ws-client=unix:@afm_users_rundir@/%i/apis/ws/{{name}}{{/value=auto|ws}} \
 		{{#value=dbus}}--dbus-client={{name}}{{/value=dbus}} \
-		{{#value=link}}--binding=/run/user/%i/apis/lib/{{name}}{{/value=link}} \
+		{{#value=link}}--binding=@afm_users_rundir@/%i/apis/lib/{{name}}{{/value=link}} \
 		{{#value=cloud}}--cloud-client={{name}}{{/value=cloud}} \
 		{{#value=local}}--binding={{:#metadata.install-dir}}/{{name}}{{/value=local}} \
 	{{/required-api}} \
@@ -217,7 +217,7 @@ DefaultDependencies=no
 SmackLabel=*
 SmackLabelIPIn=*
 SmackLabelIPOut=*
-ListenStream=/run/user/%i/apis/ws/{{name}}
+ListenStream=@afm_users_rundir@/%i/apis/ws/{{name}}
 FileDescriptorName={{name}}
 Service=afm-{{#required-permission.urn:AGL:permission::public:hidden}}service{{/required-permission.urn:AGL:permission::public:hidden}}{{^required-permission.urn:AGL:permission::public:hidden}}appli{{/required-permission.urn:AGL:permission::public:hidden}}-{{:id}}--{{:ver}}--{{:#target}}@%i.service
 %nl
diff --git a/conf/unit/generate-unit-conf/afm-unit b/conf/unit/generate-unit-conf/afm-unit
index 9a8cd62..e2fb7d0 100644
--- a/conf/unit/generate-unit-conf/afm-unit
+++ b/conf/unit/generate-unit-conf/afm-unit
@@ -52,7 +52,8 @@ define( `UNIT_NAME_BASE', `afm-ON_PERM(`:public:hidden', `service', `appli')-{{:
 define( `UNIT_NAME_SERVICE', `UNIT_NAME_BASE%i.service')
 define( `UNIT_NAME_SOCKET_FOR', `afm-api-ws-$1@%i.socket')
 define( `APP_DATA_DIR', `/home/%i/app-data')
-define( `USER_RUN_DIR', `/run/user/%i')
+define( `USER_RUN_DIR', `@afm_users_rundir@/%i')
+define( `DEBUGGING_DIR', `@afm_platform_rundir@/debug')
 
 -----------------------------------------------------------------------
 -- 
diff --git a/conf/unit/generate-unit-conf/service.inc b/conf/unit/generate-unit-conf/service.inc
index 0d784da..88e4346 100644
--- a/conf/unit/generate-unit-conf/service.inc
+++ b/conf/unit/generate-unit-conf/service.inc
@@ -80,13 +80,13 @@ Environment=LD_LIBRARY_PATH={{:#metadata.install-dir}}/lib
 Environment=XDG_DATA_HOME=APP_DATA_DIR/{{:id}}
 Environment=XDG_CONFIG_HOME=APP_DATA_DIR/{{:id}}
 Environment=XDG_CACHE_HOME=APP_DATA_DIR/{{:id}}
-Environment=XDG_RUNTIME_DIR=/run/user/%i
-Environment=DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/%i/bus
+Environment=XDG_RUNTIME_DIR=USER_RUN_DIR
+Environment=DBUS_SESSION_BUS_ADDRESS=unix:path=USER_RUN_DIR/bus
 
 IF_AGL_DEVEL
 ; Needed to enable debug
 Environment=AFM_ID=TARGET
-EnvironmentFile=-/var/run/afm-debug/TARGET.env
+EnvironmentFile=-DEBUGGING_DIR/TARGET.env
 ENDIF
 
 SyslogIdentifier=afbd-TARGET
diff --git a/conf/user/CMakeLists.txt b/conf/user/CMakeLists.txt
index 714a962..146dd6b 100644
--- a/conf/user/CMakeLists.txt
+++ b/conf/user/CMakeLists.txt
@@ -18,9 +18,9 @@
 
 cmake_minimum_required(VERSION 2.8)
 
-configure_file(afm-user-daemon.service.in afm-user-daemon.service)
-
 if(NOT USE_SDK)
+	configure_file(afm-user-daemon.service.in afm-user-daemon.service)
+
 	set(SYSCONFDIR_DBUS_USER   ${CMAKE_INSTALL_SYSCONFDIR}/dbus-1/session.d CACHE STRING "Path to dbus session configuration files")
 	set(UNITDIR_USER           ${CMAKE_INSTALL_LIBDIR}/systemd/user CACHE STRING "Path to systemd system unit files")
 
diff --git a/conf/user/afm-user-daemon.service.in b/conf/user/afm-user-daemon.service.in
index 11e479c..ec3684c 100644
--- a/conf/user/afm-user-daemon.service.in
+++ b/conf/user/afm-user-daemon.service.in
@@ -4,7 +4,7 @@ Description=Application Framework Master, User side
 [Service]
 Type=dbus                                
 BusName=org.AGL.afm.user          
-ExecStart=/usr/bin/afm-user-daemon --user-dbus=unix:path=%t/bus unix:/run/afm/apis/ws/afm-main
+ExecStart=/usr/bin/afm-user-daemon --user-dbus=unix:path=%t/bus unix:@afm_platform_rundir@/apis/ws/afm-main
 Environment=AFM_APP_INSTALL_DIR=%%r
 EnvironmentFile=-@afm_confdir@/unit.env.d/*
 Restart=on-failure
diff --git a/scripts/CMakeLists.txt b/scripts/CMakeLists.txt
index e0f80ac..b1ba1d7 100644
--- a/scripts/CMakeLists.txt
+++ b/scripts/CMakeLists.txt
@@ -19,10 +19,12 @@
 cmake_minimum_required(VERSION 2.8)
 
 if(NOT USE_SDK)
-	install(PROGRAMS afm-util DESTINATION ${CMAKE_INSTALL_BINDIR})
+	configure_file(afm-util.in afm-util)
+	install(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/afm-util DESTINATION ${CMAKE_INSTALL_BINDIR})
 	install(PROGRAMS afm-install DESTINATION ${CMAKE_INSTALL_BINDIR})
 	if(AGL_DEVEL)
-		install(PROGRAMS afm-debug DESTINATION ${CMAKE_INSTALL_BINDIR})
+		configure_file(afm-debug.in afm-debug)
+		install(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/afm-debug DESTINATION ${CMAKE_INSTALL_BINDIR})
 	endif()
 endif()
 
diff --git a/scripts/afm-debug b/scripts/afm-debug
deleted file mode 100755
index 18ecae2..0000000
--- a/scripts/afm-debug
+++ /dev/null
@@ -1,90 +0,0 @@
-#!/bin/bash
-
-###########################################################################
-# Copyright 2017 IoT.bzh
-#
-# Author: Stephane Desneux <sdx@iot.bzh>
-#         Sebastien Douheret <sebastien@iot.bzh>
-#
-# 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.
-###########################################################################
-
-# This script should be invoked by gdb client through a ssh connection.
-# It relays gdbmi protocol from gdbserver to gdb client
-#
-# WARNING: nothing should be sent to stdout except gdbserver output
-
-# FIXME: add support of --debugger option to support tcf or gdb-remote
-
-
-function error() {
-	echo "ERR: $@" >&2
-	exit 1
-}
-function info() {
-	echo "INF: $@" >&2
-}
-
-# setup debug dir (shared with service file)
-DBGDIR=/var/run/afm-debug
-mkdir -p $DBGDIR
-
-# check application name passed as first arg by gdb
-APP=$1
-[[ -z "$APP" ]] && error "Invalid application name"
-
-# redirect to log file
-exec 2>$DBGDIR/$APP.dbgclt.log
-
-# activate DEBUG in environment file sourced in systemd service
-AFB_WAIT_POINT="start-start"
-echo "AFB_DEBUG_WAIT=$AFB_WAIT_POINT" >$DBGDIR/$APP.env
-
-# remove debug env file on exit
-trap "rm $DBGDIR/$APP.*" STOP INT QUIT EXIT
-
-# ask appfw to start application
-pid=$(afm-util start $APP)
-[[ -z "$pid" || ! -e "/proc/$pid" ]] && error "Failed to start application $APP"
-info "$APP started with pid=$pid"
-
-# wait debugging process is stop/waiting at start-start point
-AFB_FILE=/tmp/afb-debug-$pid
-tmo=100
-info "Waiting for process stopped..."
-while [[ ! -e "$AFB_FILE" ]]; do
-    sleep 0.1
-    tmo=$(expr $tmo - 1)
-    [[ "$tmo" == "0" ]] && error "Timeout waiting for process $pid stopped"
-done
-
-info "Waiting for process stopped..."
-AFB_WAIT_FILE=/tmp/afb-debug-$pid
-tmo=100
-res=1
-while [[ "$res" != "0" ]]; do
-    sleep 0.1
-    tmo=$(expr $tmo - 1)
-    [[ "$tmo" == "0" ]] && error "Timeout waiting for process $pid stopped"
-    grep $AFB_WAIT_POINT $AFB_WAIT_FILE > /dev/null 2>&1
-    res=$?
-done
-
-# debugging
-info "Attaching gdbserver to pid $pid ..."
-gdbserver --attach - $pid
-
-# end of debug session
-info "proxy connection terminated - killing $APP (pid $pid)"
-afm-util kill $pid >&2
-info "$APP killed"
diff --git a/scripts/afm-debug.in b/scripts/afm-debug.in
new file mode 100755
index 0000000..e676469
--- /dev/null
+++ b/scripts/afm-debug.in
@@ -0,0 +1,90 @@
+#!/bin/bash
+
+###########################################################################
+# Copyright 2017 IoT.bzh
+#
+# Author: Stephane Desneux <sdx@iot.bzh>
+#         Sebastien Douheret <sebastien@iot.bzh>
+#
+# 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.
+###########################################################################
+
+# This script should be invoked by gdb client through a ssh connection.
+# It relays gdbmi protocol from gdbserver to gdb client
+#
+# WARNING: nothing should be sent to stdout except gdbserver output
+
+# FIXME: add support of --debugger option to support tcf or gdb-remote
+
+
+function error() {
+	echo "ERR: $@" >&2
+	exit 1
+}
+function info() {
+	echo "INF: $@" >&2
+}
+
+# setup debug dir (shared with service file)
+DBGDIR=@afm_platform_rundir@/debug
+mkdir -p $DBGDIR
+
+# check application name passed as first arg by gdb
+APP=$1
+[[ -z "$APP" ]] && error "Invalid application name"
+
+# redirect to log file
+exec 2>$DBGDIR/$APP.dbgclt.log
+
+# activate DEBUG in environment file sourced in systemd service
+AFB_WAIT_POINT="start-start"
+echo "AFB_DEBUG_WAIT=$AFB_WAIT_POINT" >$DBGDIR/$APP.env
+
+# remove debug env file on exit
+trap "rm $DBGDIR/$APP.*" STOP INT QUIT EXIT
+
+# ask appfw to start application
+pid=$(afm-util start $APP)
+[[ -z "$pid" || ! -e "/proc/$pid" ]] && error "Failed to start application $APP"
+info "$APP started with pid=$pid"
+
+# wait debugging process is stop/waiting at start-start point
+AFB_FILE=/tmp/afb-debug-$pid
+tmo=100
+info "Waiting for process stopped..."
+while [[ ! -e "$AFB_FILE" ]]; do
+    sleep 0.1
+    tmo=$(expr $tmo - 1)
+    [[ "$tmo" == "0" ]] && error "Timeout waiting for process $pid stopped"
+done
+
+info "Waiting for process stopped..."
+AFB_WAIT_FILE=/tmp/afb-debug-$pid
+tmo=100
+res=1
+while [[ "$res" != "0" ]]; do
+    sleep 0.1
+    tmo=$(expr $tmo - 1)
+    [[ "$tmo" == "0" ]] && error "Timeout waiting for process $pid stopped"
+    grep $AFB_WAIT_POINT $AFB_WAIT_FILE > /dev/null 2>&1
+    res=$?
+done
+
+# debugging
+info "Attaching gdbserver to pid $pid ..."
+gdbserver --attach - $pid
+
+# end of debug session
+info "proxy connection terminated - killing $APP (pid $pid)"
+afm-util kill $pid >&2
+info "$APP killed"
diff --git a/scripts/afm-util b/scripts/afm-util
deleted file mode 100755
index c5e48eb..0000000
--- a/scripts/afm-util
+++ /dev/null
@@ -1,117 +0,0 @@
-#!/bin/bash
-
-send() {
-	afb-client-demo -H -d unix:/run/afm/apis/ws/afm-main "$1" "$2" |
-	awk '$1=="ON-REPLY-FAIL"{$1="ERROR:";$2="";print;exit 1;}NR>1'
-}
-
-case "$1" in
-
-  list|runnables)
-    send runnables true
-    ;;
-
-  add|install)
-    f=$(realpath $2)
-    r=true
-    if [[ "$(basename $0)" = "afm-install" ]]; then r=false; fi
-    send install '{"wgt":"'"$f"'","force":true,"reload":'"$r"'}'
-    ;;
-
-  remove|uninstall)
-    i=$2
-    send uninstall "\"$i\""
-    ;;
-
-  info|detail)
-    i=$2
-    send detail "\"$i\""
-    ;;
-
-  ps|runners)
-    send runners true
-    ;;
-
-  run|start)
-    i=$2
-    send start "\"$i\""
-    ;;
-
-  run-remote|start-remote)
-    i=$2
-    send start '{"id":"'"$i"'","mode":"remote"}'
-    ;;
-
-  once)
-    i=$2
-    send once "\"$i\""
-    ;;
-
-  terminate|kill)
-    i=$2
-    send terminate "$i"
-    ;;
-
-  stop|pause)
-    i=$2
-    send pause "$i"
-    ;;
-
-  resume|continue)
-    i=$2
-    send resume "$i"
-    ;;
-
-  state|status)
-    i=$2
-    send state "$i"
-    ;;
-
-  -h|--help|help)
-    cat << EOC
-usage: $(basename $0) command [arg]
-
-The commands are:
-
-  list
-  runnables      list the runnable widgets installed
-
-  add wgt
-  install wgt    install the wgt file
-
-  remove id
-  uninstall id   remove the installed widget of id
-
-  info id
-  detail id      print detail about the installed widget of id
-
-  ps
-  runners        list the running instance
-
-  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
-
-  stop rid
-  pause rid      pause the running instance rid
-
-  resume rid
-  continue rid   continue the previously paused rid
-
-  status rid
-  state rid      get status of the running instance rid
-
-EOC
-    ;;
-
-  *)
-    echo "unknown command $1" >&2
-    exit 1
-    ;;
-esac
-
-
diff --git a/scripts/afm-util.in b/scripts/afm-util.in
new file mode 100755
index 0000000..40bc7da
--- /dev/null
+++ b/scripts/afm-util.in
@@ -0,0 +1,117 @@
+#!/bin/bash
+
+send() {
+	afb-client-demo -H -d unix:@afm_platform_rundir@/apis/ws/afm-main "$1" "$2" |
+	awk '$1=="ON-REPLY-FAIL"{$1="ERROR:";$2="";print;exit 1;}NR>1'
+}
+
+case "$1" in
+
+  list|runnables)
+    send runnables true
+    ;;
+
+  add|install)
+    f=$(realpath $2)
+    r=true
+    if [[ "$(basename $0)" = "afm-install" ]]; then r=false; fi
+    send install '{"wgt":"'"$f"'","force":true,"reload":'"$r"'}'
+    ;;
+
+  remove|uninstall)
+    i=$2
+    send uninstall "\"$i\""
+    ;;
+
+  info|detail)
+    i=$2
+    send detail "\"$i\""
+    ;;
+
+  ps|runners)
+    send runners true
+    ;;
+
+  run|start)
+    i=$2
+    send start "\"$i\""
+    ;;
+
+  run-remote|start-remote)
+    i=$2
+    send start '{"id":"'"$i"'","mode":"remote"}'
+    ;;
+
+  once)
+    i=$2
+    send once "\"$i\""
+    ;;
+
+  terminate|kill)
+    i=$2
+    send terminate "$i"
+    ;;
+
+  stop|pause)
+    i=$2
+    send pause "$i"
+    ;;
+
+  resume|continue)
+    i=$2
+    send resume "$i"
+    ;;
+
+  state|status)
+    i=$2
+    send state "$i"
+    ;;
+
+  -h|--help|help)
+    cat << EOC
+usage: $(basename $0) command [arg]
+
+The commands are:
+
+  list
+  runnables      list the runnable widgets installed
+
+  add wgt
+  install wgt    install the wgt file
+
+  remove id
+  uninstall id   remove the installed widget of id
+
+  info id
+  detail id      print detail about the installed widget of id
+
+  ps
+  runners        list the running instance
+
+  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
+
+  stop rid
+  pause rid      pause the running instance rid
+
+  resume rid
+  continue rid   continue the previously paused rid
+
+  status rid
+  state rid      get status of the running instance rid
+
+EOC
+    ;;
+
+  *)
+    echo "unknown command $1" >&2
+    exit 1
+    ;;
+esac
+
+
-- 
cgit