NCD Request Server - Sukumi/badvpn GitHub Wiki
Introduction
The [http://code.google.com/p/badvpn/source/browse/trunk/ncd/modules/sys_request_server.c sys.request_server()] module in NCD provides an interface for client programs on the same system or on the network to communicate with a running NCD program. Such an interface opens many new possibilities for things to use NCD for. The long-term goal is to implement a (better) equivalent to the !NetworkManager daemon completely in the NCD programming language, and also a corresponding GUI.
Currently, a simple GUI network status indicator is available which retrieves information from an NCD network configurator via the interface described here; see the [http://code.google.com/p/ncdgui/ ncdgui] project.
Example
Here's a small example that illustrates how NCD's request interface works.
process request_server {
# Listen on a Unix socket. It is also possible to listen on TCP.
var({"unix", "/home/ambro/request.socket"}) listen_addr;
#var({"tcp", "127.0.0.1", "8000"}) listen_addr;
# Start the request server. For each request, template request_handler
# is instantiated to handle it.
sys.request_server(listen_addr, "request_handler", {});
println("Up and running!");
}
template request_handler {
# Create value objects for request data, to allow examining it.
value(_request.data) req;
# Create value object for parse results. The parser will write results here.
# In case parsing fails, assume it's a bad command.
value(["cmd":"bad"]) res;
# Parse request data. This writes fields into the 'res' map above.
try("parse_request", {"_caller.req", "_caller.res"}) parse;
# Dispatch. If parsing failed, this calls template bad_request.
res->get("cmd") cmd;
concat(cmd, "_request") func;
call(func, {});
}
# This handles invalid requests.
template bad_request {
_caller._request->reply({"Error", "Bad request."});
_caller._request->finish();
}
# This handles ping requests.
template ping_request {
_caller._request->reply({"OK", "Pong."});
_caller._request->finish();
}
# This handles echo requests.
template echo_request {
alias("_caller._request") request;
alias("_caller.res") res;
# Reply with echo argument and finish. The argument was written to the map in 'res' by parse_echo.
res->get("echo_arg") echo_arg;
request->reply({"OK", echo_arg});
request->finish();
}
template parse_request {
alias(_arg0) req;
alias(_arg1) res;
# If an assertion here fails, we will be immediately stopped; see try() documentation.
# Request must be a list...
strcmp(req.type, "list") a;
_try->assert(a);
# ...with at least the first element...
num_greater_equal(req.length, "1") a;
_try->assert(a);
# ...which is a string...
req->get("0") cmd;
strcmp(cmd.type, "string") a;
_try->assert(a);
# ...and is one of the supported commands.
strcmp(cmd, "ping") is_ping;
strcmp(cmd, "echo") is_echo;
or(is_ping, is_echo) a;
_try->assert(a);
# Parse specific commands.
concat("parse_", cmd) func;
call(func, {});
# Only on success, set 'cmd' in the result, else "bad" is left by default.
res->insert("cmd", cmd);
}
template parse_ping {
alias("_caller._try") try;
alias("_caller.req") req;
# Request must have zero arguments.
num_equal(req.length, "1") a;
try->assert(a);
}
template parse_echo {
alias("_caller._try") try;
alias("_caller.req") req;
alias("_caller.res") res;
# Request must have one argument.
num_equal(req.length, "2") a;
try->assert(a);
# Remember that argument
req->get("1") echo_arg;
res->insert("echo_arg", echo_arg);
}
Whenever a request from a client is received, a new request_handler
process is created to handle it. There, the request data sent by the client is accessed via _request.data
, and replies can be sent using _request.reply()
and _request.finish()
. The request handler code uses the [http://code.google.com/p/badvpn/source/browse/trunk/ncd/modules/value.c value()] and [http://code.google.com/p/badvpn/source/browse/trunk/ncd/modules/try.c try()] commands to check the format of the request.
Usage
Requests can be issued to an NCD daemon running a request server using the standalone badvpn-ncd-request
program (installed by the BadVPN package).
$ badvpn-ncd-request unix:/home/ambro/request.socket '{}'
{"Error", "Bad request."}
$ badvpn-ncd-request unix:/home/ambro/request.socket '{"ping"}'
{"OK", "Pong."}
$ badvpn-ncd-request unix:/home/ambro/request.socket '{"echo", {"Hello", "server!"}}'
{"OK", {"Hello", "server!"}}
$ badvpn-ncd-request unix:/home/ambro/request.socket '{"echo", "Please", "Respond"}'
{"Error", "Bad request."}
$
It is also possible to issue requests directly from another NCD program using the [http://code.google.com/p/badvpn/source/browse/trunk/ncd/modules/sys_request_client.c sys.request_client()] module.