Remote control

Detroit engine supports remote function calls wrapped in S-expression style remote control language. This means that every function declared in Ano script can be called over the network or by cat'ing S-expressions to a program's FIFO pipe. See more complete S-expression syntax example here. Remote function call is handy in automated testing and such, or programs can be daisy chained to control each other. Remote control is not enabled by default, support for it must not be disabled when running configure script (do not use --disable-remote), and remote control must be initialized in Ano script in order it to work.

Somewhere at the beginning of Ano script, call remote_init() function:

	remote_init

See examples/remote_control directory for live example.

When program to be remote controlled is started, it needs at least --auth_secret command line switch to be set, or secret can be set in config file. Auth secret is the secret string that remote controller must know when it is calling functions over the net or echo'ing them to FIFO pipe. As an example, here is minimal Ano script which can be remote controlled:

;
; @ANO_SCRIPT_NAME		remote_control
; @ANO_SCRIPT_VERSION		0.0.3
; @ANO_SCRIPT_DESCRIPTION	Example how to run functions remotely
;
; @ANO_FLAGS_USE_PROTOS		[ ]
; @ANO_FLAGS_VAR_NAME_SUBS	[ ]
; @ANO_FLAGS_VAR_WARN_UNUSED	[ ]
;
; Copyright (c) 2016-2026, Jani Salonen <salojan@goto10.co>
; All rights reserved.
;
; Detroit engine supports remote function calls either over the network or by
; using a FIFO. This means that every function declared in Ano script can be
; called remotely. Remote function call is handy in automated testing and such,
; or programs can be daisy chained to control each other. Remote control
; support must not be disabled when running configure script (do not use
; --disable-remote), and remote control must be initialized in Ano script in
; order it to work.
;
; Procedure remote call can be enabled or disabled by using ANO_REMOTE_FUNCS
; tag, where each function or callback has either allow or deny keyword.
; Each function and keyword pair must be quoted. When using remote funcs tag,
; function remote attribute is not needed.
;
; (at)ANO_REMOTE_FUNCS	"remote_test = allow", "remote_end = allow", \
;			"remote_forbidden = deny"
;

main {
	; This is main program, just start remote thread and let it handle
	; remote connections. Program terminates when "remote_end" label is
	; called (see below) or <ctrl-c> is pressed.
	;
	remote_init
}

function remote_test [remote: allow] (param1, param2, param3) {
	; To call this function remotely (set SECRET with -na switch):
	;
	;  $ telnet localhost 2109
	;    SECRET (remote_test [int] 1 [uint] 2 [float] 3.3)
	;
	; or by using remotetool:
	;
	;  $ echo '(remote_test [int] 1 [uint] 2 [float] 3.3)' | \
	;    ./engine/tools/remotetool -na SECRET
	;
	; Value returned to caller is the sum of params.
	;
	dump	param1
	dump	param2
	dump	param3

	ret	(param1 + param2 + param3)
}

function remote_end [remote: allow] {
	; To call this function remotely (set SECRET with -na switch):
	;
	;  $ telnet localhost 2109
	;    SECRET (remote_end)
	;
	; or by using remotetool:
	;
	;  $ echo '(remote_end)' | ./engine/tools/remotetool -na SECRET
	;
	; Exit code returned to caller is 123.
	;
	exit	(123)
}

function remote_forbidden {
	; This function cannot be run remotely, as there is no remote: allow
	; attribute.
	;
}

There are attributes available to use with Ano script functions to allow or deny to run them remotely:

;
; Procedure remote call can be enabled or disabled by using ANO_REMOTE_FUNCS
; tag, where each function or callback has either allow or deny keyword.
; Each function and keyword pair must be quoted. When using remote funcs tag,
; function remote attribute is not needed.
;
; @ANO_REMOTE_FUNCS	"remote_enabled = allow", \
;			"remote_forbidden = deny"
;

function remote_enabled {
	; This function can be used remotely.
}

function remote_forbidden {
	; This function can not be used remotely as there is no allow
	; attribute.
}

Remote control can be done using telnet, or whatever client which is capable to send plain text to TCP socket. There is remotetool available in engine/tools directory which can be used for the job. It can be compiled by typing gmake remotetool in that directory. remotetool supports encrypted connections with GnuTLS or OpenSSL.

String to send to remote control port or FIFO to control a program consists of two parts; first is the auth secret which must match to remote controlled program's --auth_secret string, and the second part is S-expression style string. S-expression is always enclosed in parentheses. In simplest form, S-expression has just function name to call, and maybe some parameters with it, all separated by whitespace.

To call remote_test function with three different type of numbers, pass program's remote control port something like:

SECRET (remote_test [int] 1 [uint] 2 [float] 3.3)

If remotetool is in use, just give S-expression (function name and its parameters) leaving out the secret, as remotetool takes care of sending the secret along with remote function call. Example:

$ remotetool -na SECRET -n6 -nh localhost -np 2109
  (remote_test [int] 1 [uint] 2 [float] 3.3) <enter>
  (remote_end) <enter>

Alternatively, remote function call can be echo'ed to remotetool:

$ echo '(remote_end)' | remotetool -na SECRET -n6 -nh localhost -np 2109

If remote function returns something, it will echo back to remote controller. List of supported number types for casting numeral parameters are:

Authentication

Remote control requires authentication. As it simplest, it is just a password that goes to receiving side with the expression. Both ends must know this password in order to remote call to work. Password is set with auth_secret parameter. auth_secure parameter can be used to provide some degree of protection. This works like lightweight HMAC: the connecting party must prepare the auth_secret by concatenating the real auth_secret with the expression to be evaluated and calculating a hash of the resulting string. This hash then serves as the auth_secret. If it is in hexadecimal form, prepend 0x to it. This way, the sender does not need to transmit secrets over the network. The receiving end uses the auth_secret as plain text (not hashing it), but additionally enables auth_secure switch. If sequence numbering is in use, it comes between authentication secret and expression strings without any delimiter characters when producing a hash.

Possible values for auth_secure parameter are:

In sending side, here is how to prepare authentication secret for each method:

auth_extras parameter supports following key=value pairs:

  1. key, value is string (yes, you can not use comma in your key), to use as SipHash 2-4 key.
  2. sequence, value is 64 bit positive integer, to use against replay attack. This increases by one on every successful authentication. Each source address or FIFO has its own sequence number. If number ever wraps around, it will start again from zero, not the value what was initially set.
  3. siphash_compression_rounds, value is 32 bit positive integer, good standard is two, one for performance, four for more security.
  4. siphash_finalization_rounds, value is 32 bit positive integer, good standard is four, three for performance, eight for more security.
  5. siphash_secure_key, value is yes/no switch, and when enabled, hashes the key parameter using SipHash 2-4, where auth_secret is used as secure key with this hash. Otherwise key parameter is used directly as it is when SipHash 2-4 is generated.

Copyright © 2026, Jani Salonen <salojan at goto10 piste co>. Piste is finnish word and means dot. All rights reserved.