Creating an XPA server is easy: you generally only need to call the XPANew() subroutine to define a named XPA access point and set up the send and receive callback routines. You then enter an event loop such as XPAMainLoop() to field XPA requests.
#include <xpa.h> XPA XPANew(char *class, char *name, char *help, int (*send_callback)(), void *send_data, char *send_mode, int (*rec_callback)(), void *rec_data, char *rec_mode); XPA XPACmdNew(char *class, char *name); XPACmd XPACmdAdd(XPA xpa, char *name, char *help, int (*send_callback)(), void *send_data, char *send_mode, int (*rec_callback)(), void *rec_data, char *rec_mode); void XPACmdDel(XPA xpa, XPACmd cmd); XPA XPAInfoNew(char *class, char *name, int (*info_callback)(), void *info_data, char *info_mode); int XPAFree(XPA xpa); void XPAMainLoop(void);
#include <xpa.h>in the software module that defines or accesses an XPA access point, and then will link against the libxpa.a library:
gcc -o foo foo.c libxpa.aXPA has been compiled using both C and C++ compilers.
A server program generally defines an XPA access point by calling the XPANew() routine and specifies "send" and/or "receive" callback procedures to be executed by the program when an external process either sends data or commands to this access point or requests data or information from this access point. A program also can define several sub-commands for a single access point by calling XPACmdNew() and XPACmdAdd() instead. Having defined one or more public access points in this way, an XPA server program enters its usual event loop (or uses the standard XPA event loop).
XPA XPANew(char *class, char *name, char *help, int (*send_callback)(), void *send_data, char *send_mode, int (*rec_callback)(), void *rec_data, char *rec_mode);
Create a new XPA public access point with the class:name identifier template and enter this access point into the XPA name server, so that it can be accessed by external processes. XPANew() returns an XPA struct.
The XPA name server daemon, xpans, will be started automatically if it
is not running already. The program's ip address and listening port are
specified by the environment variable XPA_NAMESERVER, which takes the
form
The help string is meant to be returned by a request from xpaget:
A send_callback and/or a receive_callback can be specified; at
least one of them must be specified.
A send_callback can be specified that will be executed in response to
an external request from the xpaget program, the XPAGet() routine, or
XPAGetStream() routine. This callback is used to send data to the
requesting client.
The calling sequence for send_callback() is:
The send_mode string is of the form: "key1=value1,key2=value2,..."
The following keywords are recognized:
key value default explanation acl true/false true enable access control freebuf true/false true free buf after callback completes
The call_data should be recast to the XPA struct as shown. In
addition, client-specific data can be passed to the callback in
send_data.
The paramlist will be supplied by the client as qualifying parameters
for the callback. There are two ways in which the receive_callback()
routine can send data back to the client:
1. The receive_callback() routine can fill in a buffer and pass back a
pointer to this buffer. An integer len also is returned to specify the
number of bytes of data in buf. XPA will send this buffer to the
client after the callback is complete.
2. The receive_callback can send data directly to the client by writing
to the fd pointed by the macro:
Note that this fd is of the kind returned by socket() or open().
If a buf has been allocated, filled, and returned to XPA, then freebuf
generally is set so that it will be freed automatically when the
callback is completed and data has been sent to the client. If a
static buf is returned, freebuf should be false to avoid a system
error when freeing static storage. Note that default value for freebuf
implies that the callback will allocate a buffer rather than use
static storage.
If, while the callback performs its processing, an error occurs that
should be communicated to the client, then the routine XPAError should be
called:
where s is an arbitrary error message. The returned error message
string will be of the form:
If the callback wants to send a specific acknowledgment message back
to the client, the routine XPAMessage can be called:
where s is an arbitrary error message. The returned error message
string will be of the form:
Otherwise, a standard acknowledgment is sent back to the client
after the callback is completed.
The callback routine should return 0 if no error occurs, or -1 to
signal an error.
A receive_callback can be specified that will be executed in response
to an external request from the xpaset program, or the XPASet (or
XPASetStream) routine. This callback is used to process data received
from an external process.
The calling sequence for receive_callback is:
The mode string is of the form: "key1=value1,key2=value2,..."
The following keywords are recognized:
key value default explanation acl true/false true enable access control buf true/false true server expects data bytes from client fillbuf true/false true read bytes into buf before executing callback freebuf true/false true free buf after callback completes
The call_data should be recast to the XPA struct as shown. In
addition, client-specific data can be passed to the callback in
receive_data.
The paramlist will be supplied by the client. In addition, if the
receive_mode keywords buf and fillbuf are true, then on entry into the
receive_callback() routine, buf will contain the data sent by the
client. If buf is true but fillbuf is false, it becomes the callback's
responsibility to retrieve the data from the client, using the data fd
pointed to by the macro xpa_datafd(xpa). If freebuf is true, then buf
will be freed when the callback is complete.
If, while the callback is performing its processing, an error occurs
that should be communicated to the client, then the routine XPAError
can be called:
where s is an arbitrary error message.
The callback routine should return 0 if no error occurs, or -1 to
signal an error.
Create a new XPA public access point for commands that will share a
common identifier class:name. Enter this access point into the XPA
name server, so that it can be accessed by external processes.
XPACmdNew() returns an XPA struct.
It often is more convenient to have one public access point that can
manage a number of commands, rather than having individual access
points for each command. For example, it is easier to command the
SAOtng image display using:
then to use:
In the first case, the commands remain the same regardless of the
target XPA name. In the second case, the command names must change
for each instance of SAOtng. That is, if a second instance of SAOtng
called DS9 were running, it would be commanded either as:
or as:
Thus, in cases where a program is going to manage many commands, it
generally is easier to define them as commands associated with the
XPACmdNew() routine, rather than as separate access points using
XPANew().
When XPACmdNew() is called, only the class:name identifier is
specified. Each sub-command is subsequently defined using the
XPACmdAdd() routine.
Add a command to an XPA command access point. The xpa argument specifies the
XPA struct returned by a call to XPANewCmd(). The name argument is the
name of the command. The other arguments function identically to the
arguments in the XPANew() command, i.e., the send_callback and rec_callback
routines have identical calling sequences to their XPANew() counterparts,
with the exceptions noted below.
When help is requested for a command access point using:
all of the command help strings are listed. To get help for a given
command, use:
Also, the acl keyword in the send_mode and receive_mode strings is
global to the access point, not local to the command. Thus, the value
for the acl mode should be the same in all send_mode (or receive_mode)
strings for each command in a command access point. (The acl for
send_mode need not be the same as the acl for receive_mode, though).
This routine removes a command from the list of available commands in
a given XPA. That command will no longer be available for processing.
NB: this is an experimental interface, new to XPA 2.0, whose value
and best use is evolving.
A program can register interest in receiving a short message about a
particular topic from any other process that cares to send such a
message. Neither has to be an XPA server. For example, if a user
starts to work with a new image file called new.fits, she might
wish to alert interested programs about this new file by sending a
short message using xpainfo:
In this example, each process that has used the XPAInfoNew() call to
register interest in messages associated with the identifier IMAGEFILE
will have its info_callback() executed with the following calling
sequence:
The arguments passed to this routine are equivalent to those sent in
the send_callback() routine. The main difference is that there is no
buf sent to the info callback: this mechanism is meant for short
announcement of messages of interest to many clients.
The mode string is of the form: "key1=value1,key2=value2,..."
The following keywords are recognized:
key value default explanation acl true/false true enable access control
Because no buf is passed to this callback, the usual buf-related keywords
are not applicable here.
The information sent in the parameter list is arbitrary. However, we
envision sending information such as file names or xpa access points
from which to collect more data. Note that the xpainfo program and
the XPAInfo() routine that cause the info_callback to execute do not
wait for the callback to complete before returning.
Remove the specified XPA public access point from the name server and
free all associated storage. Note that removal from the name server
happens automatically when the process terminates, so this call is not
generally needed. It is used when public access points are being
defined temporarily and then destroyed when no longer needed. For
example, SAOtng temporarily creates a public access point when it
loads a new image for display and destroys it when the image is
unloaded.
Once XPA access points have been defined, a program must enter an
event loop to watch for requests from external programs. This can be
done in a variety of ways, depending on whether the event loop is
processing events other than XPA events. In cases where there are no
non-XPA events to be processed, the program can simply call the
XPAMainLoop() event loop. This loop is implemented essentially as
follows (error checking is simplified in this example):
The XPAAddSelect() routine sets up the select() readfds variable so
that select() will wait for I/O on all the active XPA channels. It
returns the number of XPAs that are active; the loop will end when
there are no active XPAs. The standard select() routine is called to
wait for an external I/O request. Since no timeout struct is passed
in argument 5, the select() call hangs until there is an external
request. When an external I/O request is made, the XPAProcess()
routine is executed to process the request.
If a program has its own Unix select() loop, then XPA access points can
be added to it by using a variation of the standard XPAMainLoop:
XPAAddSelect() is called before select() to add the access points.
If the first argument is NULL, then all active XPA access points
are added. Otherwise only the specified access point is added.
After select() is called, the XPAProcess() routine can be called
to process XPA requests.
XPA access points can be added to
Xt event loops (using XtAppMainLoop())
and
Tcl/Tk event loops (using vwait and the Tk loop).
When using XPA with these event loops, you only need to call:
It is sometimes desirable to implement a polling loop, i.e., where one
checks for and processes XPA requests without blocking. For this
situation, use the XPAPoll() routine:
This routine will perform XPAAddSelect() and select(), but with a
timeout specified in millisecs by the msec argument. If one or more
XPA requests are made before the timeout expires, the XPAProcess()
routine is called to process those requests. The maxreq value determines
how many requests will be processed: if maxreq <= 0, then all pending
requests will be processed. Otherwise, up to maxreq requests will be
processed. (The most usual values for maxreq are 0 to process all requests
and 1 to process one request).
Server routines have access to information about the xpa being called via
the following macros:
macro explanation xpa_class(xpa) class of this xpa xpa_name(xpa) name of this xpa xpa_method(xpa) method string (inet or local connect info) xpa_cmdfd(xpa) fd of command socket xpa_datafd(xpa) fd of data socket xpa_sendian(xpa) endian-ness of server ("little" or "big") xpa_cendian(xpa) endian-ness of client ("little" or "big")
The argument to these macros is the call_data pointer that is passed
to the server procedure. This pointer should be type case to XPA
in the server routine:
The most important of these macros is xpa_datafd(). A server routine
that sets "fillbuf=false" in receive_mode or send_mode can use this
macro to perform I/O directly to/from the client, rather than using
buf.
The xpa_cendian and xpa_sendian macros can be used together to determine
if the data transferred from the client is byte swapped with respect
to the server. Values for these macros are: "little", "big", or "?".
In order to do a proper conversion, you still need to know the format
of the data (i.e., byte swapping is dependent on the size of the data
element being converted).
xpaget class:name -help
int send_callback(void *send_data, void *call_data,
char *paramlist, char **buf, int *len)
{
XPA xpa = (XPA)call_data;
...
return(stat);
}
xpa_datafd(xpa)
XPAError(XPA xpa, char *s);
XPA$ERROR [error] (class:name ip:port)
XPAMessage(XPA xpa, char *s);
XPA$MESSAGE [message] (class:name ip:port)
int receive_callback(void *receive_data, void *call_data,
char *paramlist, char *buf, int len)
{
XPA xpa = (XPA)call_data;
...
return(stat);
}
XPAError(XPA xpa, char *s);
XPACmdNew - create a new XPA public access point for commands
XPA XPACmdNew(char *class, char *name);
echo "colormap I8" | xpaset SAOtng
echo "scale log" | xpaset SAOtng
echo "file foo.fits" | xpaset SAOtng
echo "I8" | xpaset SAOtng_colormap
echo "log" | xpaset SAOtng_scale
echo "foo.fits" | xpaset SAOtng_file
echo "colormap I8" | xpaset DS9
echo "scale log" | xpaset DS9
echo "file foo.fits" | xpaset DS9
echo "I8" | xpaset DS9_colormap
echo "log" | xpaset DS9_scale
echo "foo.fits" | xpaset DS9_file
XPACmdAdd - add a command to an XPA command public access point
XPACmd XPACmdAdd(XPA xpa, char *name, char *help,
int (*send_callback)(), void *send_data, char *send_mode,
int (*rec_callback)(), void *rec_data, char *rec_mode);
xpaget -h class:name
xpaget -h class:name cmd
XPACmdDel - remove a command from an XPA command public access point
void XPACmdDel(XPA xpa, XPACmd cmd);
XPAInfoNew - define an XPA info public access point
XPA XPAInfoNew(char *class, char *name,
int (*info_callback)(), void *info_data, char *info_mode);
xpainfo IMAGEFILE /data/new.fits
int info_cb(void *info_data, void *call_data, char *paramlist)
{
XPA xpa = (XPA)call_data;
}
XPAFree - remove an XPA public access point
int XPAFree(XPA xpa);
XPAMainLoop - optional main loop for XPA
void XPAMainLoop();
FD_ZERO(&readfds);
while( XPAAddSelect(NULL, &readfds) ){
if( sgot = select(swidth, &readfds, NULL, NULL, NULL) >0 )
XPAProcessSelect(&readfds, 0);
else
break;
FD_ZERO(&readfds);
}
XPAAddSelect(xpa, &readfds);
[app-specific ...]
if( select(width, &readfds, ...) ){
XPAProcessSelect(&readfds);
[app-specific ...]
FD_ZERO(&readfds);
}
int XPAXtAddInput(XtAppContext app, XPA xpa)
or
int XPATclAddInput(XPA xpa)
respectively before entering the loop.
XPAPoll - execute existing XPA request(s)
int XPAPoll(int msec, int maxreq);
XPAPoll(int msec, int maxreq);
XPA Server Callback Macros
XPA xpa = (XPA)call_data;
Last updated: May 18, 1999