RPC example: How to Use Allegro CL RPC to gain remote access to an application

An earlier Tech Corner entry introduced the new Allegro RPC facility, which implements a general purpose remote procedure call facility for Allegro CL. In this entry, we discuss an example using RPC. The sample code in [Allegro Directory]/examples/rpc-inspect.cl can be used to examine a running Lisp image from another Lisp image running on a separate, possibly remote, machine. Note that we show output, but the exact output you will see if you run the example will be different.

The main application image enables a server that will be used for remote access to the application. The server is enabled with (we make it the value of the variable server so we can later close things):

(load (compile-file-if-needed "rpc-inspect.cl"))
(setf server
    (net.rpc:define-rpc-server nil :local-port 4567 
                               :open :listener :re-connect :demand)

The second form adds one Lisp process (thread) to the application. The new process is listening at the specified socket port, but nothing else is disturbing the application.

To gain access to the remote application, another Lisp image (the client) evaluates the following:

(load (compile-file-if-needed "rpc-inspect.cl"))
(setf net.rpc:*rpc-port* 
      (net.rpc:define-rpc-client nil :remote-port 4567 
                                 :remote-host "hhh" :open t))

where "hhh" is the host name of the application machine. (It might be the same machine on which the cient is running, but it must be specified.) define-rpc-client must return a non-nil value (a nil value means the port was not created).

At this point, the client image can evaluate forms and inspect values in the remote server image:

cl-user(4): (net.rpc:rcall 'eval 'sys:*all-processes*)
#<rpc-ref l11391:pointer#289355342/1001/"cons">

The above expression evaluates system:*all-processes* in the remote image and returns a remote reference that can be viewed with the inspector. Only strings and certain vectors are transmitted from the remote image by value. Most objects are transmitted by reference and then standard tools (in this case, the inspector) can be used for further examination.

cl-user(5): :i *
rpc-remote-ref @ #x20611222 = #<rpc-ref l11391:pointer#289355342/1001/"cons"<

0 RefType ------> The symbol :pointer
1 RemoteHome ---> A simple-string (6) "l11391"
2 RemoteType ---> A simple-string (4) "cons"
3 RemoteParts --> #<remote-field-defs @ #x2061e962>

The initial call to the inspector simply confirms that we are viewing a remote reference to a cons cell or list. When we use an inspector command to view the RemoteParts field, remote components of the list are retrieved from the server image and displayed to the client:


cl-user(6): :i 3
remote-field-defs @ #x2061eaf2 = #<remote-field-defs @ #x2061eaf2>
0 RemoteObject -> #<rpc-ref l11391:pointer#289355342/1001/"cons">
1 Size ---------> fixnum 6 [#x00000018]
2 LookingAt ----> (0 5), a proper list with 2 elements
3 nil ----------> #<rpc-ref l11391:pointer#289355342/1002/"process">
4 nil ----------> #<rpc-ref l11391:pointer#289355342/1003/"process">
5 nil ----------> #<rpc-ref l11391:pointer#289355342/1004/"process">
6 nil ----------> #<c-ref l11391:pointer#289355342/1005/"process">
7 nil ----------> #<rpc-ref l11391:pointer#289355342/1006/"process">
8 nil ----------> #<rpc-ref l11391:pointer#289355342/1007/"process">

cl-user(7): :i 3
rpc-remote-ref @ #x20625032 = #<rpc-ref l11391:pointer#289355342/1008/"process">

0 RefType ------> The symbol :pointer
1 RemoteHome ---> A simple-string (6) "l11391"
2 RemoteType ---> A simple-string (7) "process"
3 RemoteParts --> #<remote-field-defs @ #x206255f2>

When we inspect the RemoteParts field of one of the process refrences in the list, we see the behavior of the inspector when a remote object has more than 10 slots or components:

cl-user(8): :i 3
remote-field-defs @ #x20460aa2 = #<remote-field-defs @ #x20460aa2>

0 RemoteObject -> #<rpc-ref l11391:pointer#289355342/1008/"process">
1 Size ---------> fixnum 30 [#x00000078]
2 LookingAt ----> (0 9), a proper list with 2 elements
3 next ---------> The symbol nil
4 prev ---------> The symbol nil
5 name ---------> A simple-string (21) "RPC1-l11391-do-invoke"
6 thread -------> The symbol nil
7 initial-form -> #<rpc-ref l11391:pointer#289355342/1009/"cons">
8 wait-function -> The symbol nil
9 wait-args ----> The symbol nil
10 run-reasons --> #<rpc-ref l11391:pointer#289355342/1010/"cons">
11 arrest-reasons -> The symbol nil
12 orig-priority -> fixnum 0 [#x00000000]
13 MoreParts ----> #<remote-field-defs @ #x204ed0c2>
cl-user(9):

The MoreParts field allows continued inspection of the remote object. When you are done with a specific inspection, end inspection with :i q.

Arbitrary functions may be called from the client to retrieve other values or to perform side effects. Here are some examples:

(net.rpc:rcall 'member :aclrpc (net.rpc:rref '*features* :type :value))
;;  Assuming the server has a process named "foo":
(setq x (net.rpc:rcall 'mp:process-name-to-process "foo"))
(net.rpc:rcall 'mp:kill-process x)  

While the client is connected to the server, several additional Lisp processes are running in the server image. When done, the client calls net.rpc:rclose.

Once a connection is opened and closed, subsequent connect attempts re-open the original connection so that remote references saved in the client image remain valid. If the limit keyword argument is set to 1 in the definition of the server, then only the first connection made to the server will be allowed to re-connect, all other connections will be rejected. This can serve as a modest security measure that would prevent casual attempts to connect to the server. If strict authentication of clients and strong server security is needed, an SSL connection should be used between the server and the client.

When the server (application) wishes to stop rpc interaction, it calls rpc-close, as with the following form: (recall server is the value returned by net.rpc:define-rpc-server):

(net.rpc:rpc-close server :stop :final)

That form kills all rpc processes from the application.

Return to Tech Corner Archive page. Go to the main Tech Corner page.

Copyright © 2023 Franz Inc., All Rights Reserved | Privacy Statement Twitter