Remote Procedure Calls

Webmin versions 0.82 and above has several common functions for executing code on remote Webmin servers. They are used by some of the standard modules (such as those in the Cluster category) to control multiple servers from a single interface, and may be useful in your own modules as well. These functions, all of which have names starting with remote_, let you call functions, evaluation Perl code, and transfer data to and from other system running Webmin.

Before a 'master' server can make RPC calls to a remote host, it must be registered in the Webmin Servers Index module on the master system. The Link type field must be set to Login via Webmin and a username and password entered. The user specified should be root or admin, as others are not by default allowed to accept RPC calls.

RPC is usually used to call functions in other modules on a remote system, or common functions. This is done with the remote_foreign_call function, but before it can be used remote_foreign_require must be called to load the library for the module that you want to call. This is very similar to calling functions in other local modules with the foreign_ functions, explained in the "Functions in Other Modules" section.

A piece of code that edits a user on a remote system might look like :

$server = "www.example.com";
$user = "joe";
&remote_foreign_require($server, "useradmin", "user-lib.pl");
@users = &remote_foreign_call($server, "useradmin", "list_users");
($joe) = grep { $_->{'user'} eq $user } @users;
if ($joe) {
    $joe->{'real'} = "Joe Bloggs";
    &remote_foreign_call($server, "useradmin", "modify_user", $joe, $joe);
    }
Of course, you need to be familiar with the available functions in other modules, and also to be sure that the module that you want to call is actually installed and of the right version.

All parameters passed to remote functions are converted to a serialized text form for transfer to the remote server, and any return value is also sent back in serialized form. The common functions serialize_variable and unserialize_variable are used, but the process is hidden from both the caller and the remote function - they only see scalars and references in their original format. One thing to look out for is circular references though - trying to send a structure that contains links to itself (such as a doubly-linked list) will fail due to the shortcomings of the serialize_variable function. Also, try to avoid using extremely large parameters, such as strings over 1 MB in size, as serialization may make them massive.

Parameters that are references to hashes, arrays or scalars that would normally be filled in by the function will not be transferred properly. For example, the read_file function normally fills in the hash referenced by its second argument with the contents of a file. This will not work when it is called remotely, as all parameters and anything that they refer to are 'copied' to the other system.

The remote_eval function can be used to execute an arbitrary block of Perl code on a remote system, which allows you to do things that calls to remote functions cannot. It is the only way to call native Perl functions such as unlink, to read and write arbitrary format files, set global variables and properly call functions that set their parameters. Whatever the Perl code evaluates to will be sent back returned by this function. This example shows remote_eval in use :

$data = &remote_eval($server, "useradmin",
	"rename('/etc/foo', '/etc/bar');\n".
	"local \%data;\n".
	"&read_file('/etc/bar', \\%data);\n".
	"return \\%data;\n");
&write_file('/etc/foo', $data);
As you can see, proper quoting is necesary when constructing the Perl code string, so that any variable symbols (such as $, % and @) are escape, as is the \ character. The second module parameter to remote_eval can be set to undef, which indicates that the code should be executed in the global Webmin context, rather than in any module's.

The functions remote_read and remote_write can be used to transfer the contents of an entire file between the master and remote systems. They are must faster than reading in the file and encoding it for use in the remote_foreign_call or remote_eval functions, as the file is transferred unencoded over a TCP connection.

If your module makes RPC calls, you may want the user to select a system to make calls to from a menu. A list of the names of all those available can be obtained from the Webmin Servers Index module with code like this :

&foreign_require("servers", "servers-lib.pl");
@allservers = &servers::list_servers();
@rpcservers = map { $_->{'host'} } grep { $_->{'user'} } @allservers;

In addition, all of the remote_ functions will accept undef for the server parameter. This indicates that the local system should be used, which never needs to be defined in the Webmin Servers Index module. This is how all of the Cluster category modules can include the this server option in their lists of hosts to manage.