diff options
-rwxr-xr-x | dev-shell | 59 | ||||
-rw-r--r-- | protocol.txt | 10 | ||||
-rw-r--r-- | server.lua | 17 | ||||
-rw-r--r-- | web/client.js | 18 |
4 files changed, 73 insertions, 31 deletions
@@ -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) @@ -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" + ); |