summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xdev-shell59
-rw-r--r--protocol.txt10
-rw-r--r--server.lua17
-rw-r--r--web/client.js18
4 files changed, 73 insertions, 31 deletions
diff --git a/dev-shell b/dev-shell
index 8bb75d1..dfe8db2 100755
--- a/dev-shell
+++ b/dev-shell
@@ -3,18 +3,14 @@
# Copyright (c) 2012-2013 Kaarle Ritvanen
# See LICENSE file for license details
-function parse_var {
- echo "$2" | sed "s/^$1: \\(.\\+\\)"$'\r'"\$/\\1/;ta;d;:a;q"
-}
-
function _acf_req {
local url=$ACF_URL$1
shift
local resp
resp=$(curl -s -w $'\n\nStatus: %{response_code}\r\n' \
- ${ACF_AUTH_TOKEN:+-H "X-ACF-Auth-Token: $ACF_AUTH_TOKEN"} \
- ${ACF_TXN_ID:+-H "X-ACF-Transaction-ID: $ACF_TXN_ID"} \
+ ${ACF_Auth_Token:+-H "X-ACF-Auth-Token: $ACF_Auth_Token"} \
+ ${ACF_Transaction_ID:+-H "X-ACF-Transaction-ID: $ACF_Transaction_ID"} \
"$@" $url)
local code=$?
@@ -23,9 +19,10 @@ function _acf_req {
return 1
fi
+ resp=$(echo "$resp" | sed $'s/\r//')
echo "$resp"
- local status=$(parse_var Status "$resp")
+ local status=$(echo "$resp" | sed 's/^Status: //;ta;d;:a;q')
[ "${status:0:1}" = 2 ] && return
echo "Request failed" >&2
@@ -34,31 +31,30 @@ function _acf_req {
function _acf_start_req {
local url=$1
- local hdr=X-ACF-$2
- local var=ACF_$3
- shift 3
-
- local current=${!var}
- local status=
+ shift
local resp
resp=$(_acf_req "$url" "$@" -D /proc/self/fd/1 -o /proc/self/fd/3)
[ $? -eq 0 ] || return
- local id=$(parse_var $hdr "$resp")
- eval export $var=$id
+ local txn_id=$ACF_Transaction_ID
+
+ while read line; do
+ eval export "$line"
+ done < <(echo "$resp" | \
+ sed 's/^X-\(ACF-[-A-Za-z]\+\): /\1=/;ta;d;:a;y/-/_/')
bash --rcfile "$ACF_QD_CLI"
[ $? -eq 254 ] || _acf_req $url -X DELETE
- eval $var=$current
+ ACF_Transaction_ID=$txn_id
}
-if [ "$ACF_AUTH_TOKEN" ]; then
+if [ "$ACF_Auth_Token" ]; then
- if [ "$ACF_TXN_ID" ]; then
+ if [ "$ACF_Transaction_ID" ]; then
cat >&2 <<EOF
-Transaction $ACF_TXN_ID started
+Transaction $ACF_Transaction_ID started
Type 'commit' to commit, 'exit' to abort
EOF
@@ -71,16 +67,31 @@ Available commands:
Delete object: delete <path>
Fetch metadata: meta <path>
Start transaction: start
+EOF
+ if [ $ACF_Save_Required = 1 ]; then
+ echo " Save changes persistently: save" >&2
+ fi
+cat >&2 <<EOF
Example: put /awall/zone/internet '{"iface": ["eth0"]}'
EOF
fi
- PS1="$ACF_USER@acf2-dev-shell${ACF_TXN_ID:+($ACF_TXN_ID)}> "
+ PS1="$ACF_USER@acf2-dev-shell${ACF_Transaction_ID:+($ACF_Transaction_ID)}> "
+
+ if [ $ACF_Save_Required = 1 ]; then
+ function save {
+ if [ "$ACF_Transaction_ID" ]; then
+ echo "Transaction not committed" >&2
+ return 1
+ fi
+ _acf_req /save -X POST
+ }
+ fi
function start {
- _acf_start_req /transaction Transaction-ID TXN_ID -X POST
+ _acf_start_req /transaction -X POST
}
function meta {
@@ -114,7 +125,7 @@ EOF
}
function commit {
- if [ "$ACF_TXN_ID" ]; then
+ if [ "$ACF_Transaction_ID" ]; then
if _acf_req /transaction -X PUT; then
echo Committed >&2
exit 254
@@ -177,8 +188,8 @@ EOF
echo >&2
fi
- ACF_TXN_ID=
+ ACF_Transaction_ID=
- _acf_start_req /login Auth-Token AUTH_TOKEN \
+ _acf_start_req /login \
-d "{\"username\": \"$ACF_USER\", \"password\": \"$PASSWORD\"}"
fi
diff --git a/protocol.txt b/protocol.txt
index bf00116..d9f861a 100644
--- a/protocol.txt
+++ b/protocol.txt
@@ -7,13 +7,19 @@ req: GET /
Log in:
req: POST /login
- body is a JSON object with username and password attributes
-resp: authentication token (in header as X-ACF-Auth-Token)
- - use X-ACF-Auth-Token in the header of subsequent requests
+resp headers:
+ - X-ACF-Auth-Token: authentication token, use in the header of
+ subsequent requests
+ - X-ACF-Save-Required: if set to 1, an explicit request is
+ required to save the changed files persistently
Log out:
req: DELETE /login
X-ACF-Auth-Token: <token>
+Save configuration files persistently (lbu commit):
+req: POST /save
+
Start transaction:
req: POST /transaction
resp: txn ID (in header as X-ACF-Transaction-ID)
diff --git a/server.lua b/server.lua
index 7168c0e..9a17c44 100644
--- a/server.lua
+++ b/server.lua
@@ -13,6 +13,8 @@ local stringy = require('stringy')
math.randomseed(os.time())
+local save_req = os.execute('[ $(stat -f -c "%T" /) = tmpfs ]')
+
-- TODO shared storage for sessions
-- TODO expire stale sessions
local sessions = {}
@@ -98,7 +100,13 @@ return function(env)
until not sessions[sid]
sessions[sid] = {user=data.username, last_txn_id=0, txns={}}
- return wrap(204, {['X-ACF-Auth-Token']=sid})
+ return wrap(
+ 204,
+ {
+ ['X-ACF-Auth-Token']=sid,
+ ['X-ACF-Save-Required']=save_req and 1 or 0
+ }
+ )
end
return wrap(401)
end
@@ -115,6 +123,13 @@ return function(env)
if not user then return wrap(401) end
+ if path == '/save' then
+ if not save_req then return wrap(404) end
+ if method ~= 'POST' then return wrap(405) end
+ if os.execute('lbu commit') then wrap(204) end
+ return wrap(500, nil, 'lbu commit failed')
+ end
+
local success, code, hdr, res = acf.call(
function()
if stringy.startswith(path, '/meta/') then
diff --git a/web/client.js b/web/client.js
index fc352c7..17ac45c 100644
--- a/web/client.js
+++ b/web/client.js
@@ -72,7 +72,7 @@ $(function() {
}
- var txnMgr = (function(token) {
+ var txnMgr = (function(token, saveRequired) {
var txn, changed, invalid;
function reset() {
@@ -319,12 +319,19 @@ $(function() {
commit: function() {
var def = $.Deferred();
+ function reject(xhr) { def.reject(xhr); }
request(
"/transaction", {type: "PUT"}
).done(function() {
reset();
- def.resolve();
- }).fail(function(xhr) { def.reject(xhr); });
+ if (saveRequired)
+ request(
+ "/save", {type: "POST"}
+ ).done(function() {
+ def.resolve();
+ }).fail(reject);
+ else def.resolve();
+ }).fail(reject);
return def;
},
@@ -336,7 +343,10 @@ $(function() {
return request("/login", {type: "DELETE"});
}
};
- })(xhr.getResponseHeader("X-ACF-Auth-Token"));
+ })(
+ xhr.getResponseHeader("X-ACF-Auth-Token"),
+ xhr.getResponseHeader("X-ACF-Save-Required") == "1"
+ );