/usr/share/doc/libxpa-dev/server.html is in libxpa-dev 2.1.15-3.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 | <!-- =defdoc xpaserver xpaserver 3 -->
<HTML>
<HEAD>
<TITLE>XPA Server API</TITLE>
</HEAD>
<BODY>
<!-- =section xpaserver NAME -->
<H2><A NAME="xpaserver">XPAServer: The XPA Server-side Programming Interface</A></H2>
<!-- =section xpaserver SYNOPSIS -->
<H2>Summary</H2>
A description of the XPA server-side programming interface.
<!-- =section xpaserver DESCRIPTION -->
<H2><A NAME="intro">Introduction to XPA Server Programming</H2></A>
<P>
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.
<PRE>
#include <xpa.h>
XPA <A HREF="./server.html#xpanew">XPANew</A>(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 <A HREF="./server.html#xpacmdnew">XPACmdNew</A>(char *class, char *name);
XPACmd <A HREF="./server.html#xpacmdadd">XPACmdAdd</A>(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 <A HREF="./server.html#xpacmddel">XPACmdDel</A>(XPA xpa, XPACmd cmd);
XPA <A HREF="./server.html#xpainfonew">XPAInfoNew</A>(char *class, char *name,
int (*info_callback)(), void *info_data, char *info_mode);
int <A HREF="./server.html#xpafree">XPAFree</A>(XPA xpa);
void <A HREF="./server.html#xpamainloop">XPAMainLoop</A>(void);
int <A HREF="./server.html#xpapoll">XPAPoll</A>(int msec, int maxreq);
void <A HREF="./server.html#xpaatexit">XPAAtExit</A>(void);
void <A HREF="./server.html#xpacleanup">XPACleanup</A>(void);
</PRE>
<H2>Introduction</H2>
To use the XPA application programming interface, a software developer
generally will include the xpa.h definitions file:
<PRE>
#include <xpa.h>
</PRE>
in the software module that defines or accesses an XPA access point, and
then will link against the libxpa.a library:
<PRE>
gcc -o foo foo.c libxpa.a
</PRE>
XPA has been compiled using both C and C++ compilers.
<P>
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).
<!-- =defdoc xpanew xpanew 3 -->
<!-- =section xpanew NAME -->
<H2><A NAME="xpanew">XPANew: create a new XPA access point</A></H2>
<!-- =section xpanew SYNOPSIS -->
<B>
<PRE>
#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);
</PRE>
</B>
<!-- =section xpanew DESCRIPTION -->
<P>
Create a new XPA public access point with the class:name
identifier <A HREF="./template.html">template</A>
and enter this access point into the XPA name server, so that it
can be accessed by external processes. XPANew() returns an XPA struct.
Note that the length of the class and name designations must be less
than or equal to 1024 characters each.
<P>
The XPA name server daemon, xpans, will be started automatically if it
is not running already (assuming it can be found in the path). The
program's ip address and listening port are specified by the
environment variable XPA_NSINET, which takes the form <ip>:<port>. If
no such environment variable exists, then xpans is started on the
current machine listening on port 14285. It also uses 14286 as a
known port for its public access point (so that routines do not have
to go to the name server to find the name server ip and port!)
As of XPA 2.1.1, version information is exchanged between the xpans
process and the new access point. If the access point uses an XPA
major/minor version newer than xpans, a warning is issued by both processes,
since mixing of new servers and old xpa programs (xpaset, xpaget,
xpans, etc.) is not likely to work. You can turn off the warning
message by setting the XPA_VERSIONCHECK environment variable to "false".
<P>
The help string is meant to be returned by a request from xpaget:
<PRE>
xpaget class:name -help
</PRE>
<P>
A send_callback and/or a receive_callback can be specified; at
least one of them must be specified.
<P>
A send_callback can be specified that will be executed in response to
an external request from the xpaget program, the XPAGet() routine, or
XPAGetFd() routine. This callback is used to send data to the
requesting client.
<P>
The calling sequence for send_callback() is:
<PRE>
int send_callback(void *send_data, void *call_data,
char *paramlist, char **buf, size_t *len)
{
XPA xpa = (XPA)call_data;
...
return(stat);
}
</PRE>
<P>
The send_mode string is of the form: "key1=value1,key2=value2,..."
The following keywords are recognized:
<PRE>
key value default explanation
------ -------- -------- -----------
acl true/false true enable access control
freebuf true/false true free buf after callback completes
</PRE>
<P>
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.
<P>
The paramlist will be supplied by the client as qualifying parameters
for the callback. There are two ways in which the send_callback()
routine can send data back to the client:
<P>
1. The send_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.
<P>
2. The send_callback can send data directly to the client by writing
to the fd pointed by the macro:
<PRE>
xpa_datafd(xpa)
</PRE>
<P>
Note that this fd is of the kind returned by socket() or open().
<P>
If a buf has been allocated by a standard malloc routine, filled, and
returned to XPA, then freebuf generally is set so that the buffer 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 set to 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.
<P>
On the other hand, if buf is dynamically allocated using a method
other than a standard malloc/calloc/realloc routine (e.g. using Perl's
memory allocation and garbage collection scheme), then it is necessary
to tell XPA how to free the allocated buffer. To do this, use the
XPASetFree() routine within your callback:
<PRE>
void XPASetFree(XPA xpa, void (*myfree)(void *), void *myfree_ptr);
</PRE>
The first argument is the usual XPA handle. The second argument is the
special routine to call to free your allocated memory. The third
argument is an optional pointer. If not NULL, the specified free
routine is called with that pointer as its sole argument. If NULL, the
free routine is called with the standard buf pointer as its sole
argument. This is useful in cases where there is a mapping between the
buffer pointer and the actual allocated memory location, and the
special routine is expecting to be passed the former.
<P>
If, while the callback performs its processing, an error occurs that
should be communicated to the client, then the routine XPAError should be
called:
<PRE>
XPAError(XPA xpa, char *s);
</PRE>
<P>
where s is an arbitrary error message. The returned error message
string will be of the form:
<PRE>
XPA$ERROR [error] (class:name ip:port)
</PRE>
<P>
If the callback wants to send a specific acknowledgment message back
to the client, the routine XPAMessage can be called:
<PRE>
XPAMessage(XPA xpa, char *s);
</PRE>
<P>
where s is an arbitrary error message. The returned error message
string will be of the form:
<PRE>
XPA$MESSAGE [message] (class:name ip:port)
</PRE>
<P>
Otherwise, a standard acknowledgment is sent back to the client
after the callback is completed.
<P>
The callback routine should return 0 if no error occurs, or -1 to
signal an error.
<P>
A receive_callback can be specified that will be executed in response
to an external request from the xpaset program, or the XPASet (or
XPASetFd()) routine. This callback is used to process data received
from an external process.
<P>
The calling sequence for receive_callback is:
<PRE>
int receive_callback(void *receive_data, void *call_data,
char *paramlist, char *buf, size_t len)
{
XPA xpa = (XPA)call_data;
...
return(stat);
}
</PRE>
<P>
The mode string is of the form: "key1=value1,key2=value2,..."
The following keywords are recognized:
<PRE>
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 data into buf before executing callback
freebuf true/false true free buf after callback completes
</PRE>
<P>
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.
<P>
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.
<P>
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:
<PRE>
XPAError(XPA xpa, char *s);
</PRE>
<P>
where s is an arbitrary error message.
<P>
The callback routine should return 0 if no error occurs, or -1 to
signal an error.
<!-- =defdoc xpacmdnew xpacmdnew 3 -->
<!-- =section xpacmdnew NAME -->
<H2><A NAME="xpacmdnew">XPACmdNew: create a new XPA public access point for commands</A></H2>
<!-- =section xpacmdnew SYNOPSIS -->
<B>
<PRE>
#include <xpa.h>
XPA XPACmdNew(char *class, char *name);
</PRE>
</B>
<!-- =section xpacmdnew DESCRIPTION -->
<P>
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.
<P>
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
ds9 image display using:
<PRE>
echo "colormap I8" | xpaset ds9
echo "scale log" | xpaset ds9
echo "file foo.fits" | xpaset ds9
</PRE>
<P>
then to use:
<PRE>
echo "I8" | xpaset ds9_colormap
echo "log" | xpaset ds9_scale
echo "foo.fits" | xpaset ds9_file
</PRE>
<P>
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 ds9. That is, if a second instance of ds9
called DS9 were running, it would be commanded either as:
<PRE>
echo "colormap I8" | xpaset DS9
echo "scale log" | xpaset DS9
echo "file foo.fits" | xpaset DS9
</PRE>
<P>
or as:
<PRE>
echo "I8" | xpaset DS9_colormap
echo "log" | xpaset DS9_scale
echo "foo.fits" | xpaset DS9_file
</PRE>
<P>
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().
<P>
When XPACmdNew() is called, only the class:name identifier is
specified. Each sub-command is subsequently defined using the
XPACmdAdd() routine.
<!-- =defdoc xpacmdadd xpacmdadd 3 -->
<!-- =section xpacmdadd NAME -->
<H2><A NAME="xpacmdadd">XPACmdAdd: add a command to an XPA command public access point</A></H2>
<!-- =section xpacmdadd SYNOPSIS -->
<B>
<PRE>
#include <xpa.h>
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);
</PRE>
</B>
<!-- =section xpacmdadd DESCRIPTION -->
<P>
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.
<P>
When help is requested for a command access point using:
<PRE>
xpaget -h class:name
</PRE>
<P>
all of the command help strings are listed. To get help for a given
command, use:
<PRE>
xpaget -h class:name cmd
</PRE>
<P>
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).
<!-- =defdoc xpacmddel xpacmddel 3 -->
<!-- =section xpacmddel NAME -->
<H2><A NAME="xpacmddel">XPACmdDel: remove a command from an XPA command public access point</A></H2>
<!-- =section xpacmddel SYNOPSIS -->
<B>
<PRE>
#include <xpa.h>
void XPACmdDel(XPA xpa, XPACmd cmd);
</PRE>
</B>
<!-- =section xpacmddel DESCRIPTION -->
<P>
This routine removes a command from the list of available commands in
a given XPA. That command will no longer be available for processing.
<!-- =defdoc xpainfonew xpainfonew 3 -->
<!-- =section xpainfonew NAME -->
<H2><A NAME="xpainfonew">XPAInfoNew: define an XPA info public access point</A></H2>
<!-- =section xpainfonew SYNOPSIS -->
<B>
<PRE>
#include <xpa.h>
XPA XPAInfoNew(char *class, char *name,
int (*info_callback)(),
void *info_data, char *info_mode);
</PRE>
</B>
<!-- =section xpainfonew DESCRIPTION -->
<P>
[NB: this is an experimental interface, new to XPA 2.0, whose value
and best use is evolving.]
<P>
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:
<PRE>
xpainfo IMAGEFILE /data/new.fits
</PRE>
<P>
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:
<PRE>
int info_cb(void *info_data, void *call_data, char *paramlist)
{
XPA xpa = (XPA)call_data;
}
</PRE>
<P>
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.
<P>
The mode string is of the form: "key1=value1,key2=value2,..."
The following keywords are recognized:
<PRE>
key value default explanation
------ -------- -------- -----------
acl true/false true enable access control
</PRE>
<P>
Because no buf is passed to this callback, the usual buf-related keywords
are not applicable here.
<P>
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.
<!-- =defdoc xpafree xpafree 3 -->
<!-- =section xpafree NAME -->
<H2><A NAME="xpafree">XPAFree: remove an XPA public access point</A></H2>
<!-- =section xpafree SYNOPSIS -->
<PRE>
<B>
#include <xpa.h>
int XPAFree(XPA xpa);
</B>
</PRE>
<!-- =section xpafree DESCRIPTION -->
<P>
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, ds9 temporarily creates a public access point when it
loads a new image for display and destroys it when the image is
unloaded.
<!-- =defdoc xpamainloop xpamainloop 3 -->
<!-- =section xpamainloop NAME -->
<H2><A NAME="xpamainloop">XPAMainLoop: optional main loop for XPA</A></H2>
<!-- =section xpamainloop SYNOPSIS -->
<B>
<PRE>
#include <xpa.h>
void XPAMainLoop();
</PRE>
</B>
<!-- =section xpamainloop DESCRIPTION -->
<P>
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):
<PRE>
FD_ZERO(&readfds);
while( XPAAddSelect(NULL, &readfds) ){
if( sgot = select(swidth, &readfds, NULL, NULL, NULL) >0 )
XPAProcessSelect(&readfds, 0);
else
break;
FD_ZERO(&readfds);
}
</PRE>
<P>
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 XPAProcessSelect()
routine is executed to process the pending requests. In this routine,
the maxreq value determines how many requests will be processed: if
maxreq <=0, then all currently pending requests will be processed.
Otherwise, up to maxreq requests will be processed. (The most usual
values for maxreq is 0 to process all requests.)
<P>
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:
<PRE>
XPAAddSelect(xpa, &readfds);
[app-specific ...]
if( select(width, &readfds, ...) ){
XPAProcessSelect(&readfds, maxreq);
[app-specific ...]
FD_ZERO(&readfds);
}
</PRE>
<P>
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 XPAProcessSelect() routine can be called
to process XPA requests. Once again, the maxreq value determines how
many requests will be processed: if maxreq <=0, then all currently
pending requests will be processed. Otherwise, up to maxreq requests
will be processed.
<P>
XPA access points can be added to
<A HREF="./xt.html">Xt event loops</A> (using XtAppMainLoop())
and
<A HREF="./tcl.html">Tcl/Tk event loops</A> (using vwait and the Tk loop).
When using XPA with these event loops, you only need to call:
<PRE>
int XPAXtAddInput(XtAppContext app, XPA xpa)
</PRE>
or
<PRE>
int XPATclAddInput(XPA xpa)
</PRE>
respectively before entering the loop.
<!-- =defdoc xpapoll xpapoll 3 -->
<!-- =section xpapoll NAME -->
<H2><A NAME="xpapoll">XPAPoll: execute existing XPA requests</A></H2>
<!-- =section xpapoll SYNOPSIS -->
<B>
<PRE>
#include <xpa.h>
int XPAPoll(int msec, int maxreq);
</PRE>
</B>
<!-- =section xpapoll DESCRIPTION -->
<P>
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:
<PRE>
XPAPoll(int msec, int maxreq);
</PRE>
<P>
The XPAPoll() 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 XPAProcessSelect()
routine is called to process those requests. The maxreq value determines
how many requests will be processed: if maxreq < 0, then no events are
processed, but instead, the return value indicates the number of events
that are pending. If maxreq == 0, then all currently 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).
<!-- =defdoc xpaatexit xpaatexit 3 -->
<!-- =section xpaatexit NAME -->
<H2><A NAME="xpaatexit">XPAAtExit: install exit handler</A></H2>
<!-- =section xpaatexit SYNOPSIS -->
<PRE>
<B>
#include <xpa.h>
void XPAAtExit(void);
</B>
</PRE>
<!-- =section xpaatexit DESCRIPTION -->
<P>
XPAAtExit() will install an exit handler using atexit() to run XPAFree on all
XPA access points. This might be useful in cases where Unix sockets are being
used: if an explicit call to XPAFree() is not made by the program, the Unix
socket file will not be deleted immediately without an atexit handler. (NB: this
call should not be made in a Tcl/Tk application. Accessing the Tcl native file
system after Tcl has shut down all file systems causes the Tcl/Tl program to
crash).
<!-- =defdoc xpacleanup xpacleanup 3 -->
<!-- =section xpacleanup NAME -->
<H2><A NAME="xpacleanup">XPACleanup: release reserved XPA memory</A></H2>
<!-- =section xpacleanup SYNOPSIS -->
<PRE>
<B>
#include <xpa.h>
void XPACleanup(void);
</B>
</PRE>
<!-- =section xpacleanup DESCRIPTION -->
<P>
When XPA is initialized, it allocates a small amount of memory for the
access control list, temp directory path, and reserved commands. This
memory is found by valgrind to be "still reachable", meaning that "your
program didn't free some memory it could have". Calling the
XPACleanup() routine before exiting the program will free this memory
and make valgrind happy.
<!-- =defdoc xpamacros xpamacros 3 -->
<!-- =section xpamacros NAME -->
<H2><A NAME="macros">XPA Server Callback Macros</A></H2>
<!-- =section xpamacros SYNOPSIS -->
<B>
<PRE>
#include <xpa.h>
xpa_class, xpa_name, xpa_method, xpa_cmdfd, xpa_datafd,
xpa_sendian, xpa_cendian
</PRE>
</B>
<!-- =section xpamacros DESCRIPTION -->
<P>
Server routines have access to information about the XPA being called via
the following macros (each of which takes the xpa handle as an argument):
<PRE>
macro explanation
------ -----------
xpa_class class of this xpa
xpa_name name of this xpa
xpa_method method string (inet or local connect info)
xpa_cmdfd fd of command socket
xpa_datafd fd of data socket
xpa_sendian endian-ness of server ("little" or "big")
xpa_cendian endian-ness of client ("little" or "big"
</PRE>
<P>
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:
<PRE>
XPA xpa = (XPA)call_data;
</PRE>
<P>
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.
<P>
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).
<!-- =defdoc xparace xparace 3 -->
<!-- =section xparace NAME -->
<H2><A NAME="race">XPA Race Conditions</A></H2>
<!-- =section xparace SYNOPSIS -->
Potential XPA race conditions and how to avoid them.
<!-- =section xparace DESCRIPTION -->
<P>
Currently, there is only one known circumstance in which XPA can get
(temporarily) deadlocked in a race condition: if two or more XPA
servers send messages to one another using an XPA client routine such
as XPASet(), they can deadlock while each waits for the other server
to respond. (This can happen if the servers call XPAPoll() with a
time limit, and send messages in between the polling call.) The
reason this happens is that both client routines send a string to the
other server to establish the handshake and then wait for the server
response. Since each client is waiting for a response, neither is able
to enter its event-handling loop and respond to the other's
request. This deadlock will continue until one of the timeout periods
expire, at which point an error condition will be triggered and the
timed-out server will return to its event loop.
<P>
Starting with version 2.1.6, this rare race condition can be
avoided by setting the XPA_IOCALLSXPA environment variable for servers
that will make client calls. Setting this variable causes all XPA
socket IO calls to process outstanding XPA requests whenever the
primary socket is not ready for IO. This means that a server making a
client call will (recursively) process incoming server requests while
waiting for client completion. It also means that a server callback
routine can handle incoming XPA messages if it makes its own XPA call.
The semi-public routine oldvalue=XPAIOCallsXPA(newvalue) can be used
to turn this behavior off and on temporarily. Passing a 0 will turn
off IO processing, 1 will turn it back on. The old value is returned
by the call.
<P>
By default, the XPA_IOCALLSXPA option is turned off, because we judge
that the added code complication and overhead involved will not be
justified by the amount of its use. Moreover, processing XPA requests
within socket IO can lead to non-intuitive results, since incoming
server requests will not necessarily be processed to completion in the
order in which they are received.
<P>
Aside from setting XPA_IOCALLSXPA, the simplest way to avoid this race
condition is to multi-process: when you want to send a client message,
simply start a separate process to call the client routine, so that
the server is not stopped. It probably is fastest and easiest to use
fork() and then have the child call the client routine and exit. But
you also can use either the system() or popen() routine to start one
of the command line programs and do the same thing. Alternatively, you
can use XPA's internal launch() routine instead of system(). Based on
fork() and exec(), this routine is more secure than system() because
it does not call /bin/sh.
<P>
Starting with version 2.1.5, you also can send an XPAInfo() message with
the mode string "ack=false". This will cause the client to send a message
to the server and then exit without waiting for any return message from
the server. This UDP-like behavior will avoid the server deadlock when
sending short XPAInfo messages.
<!-- =section xpaserver SEE ALSO -->
<!-- =text See xpa(n) for a list of XPA help pages -->
<!-- =section xpanew SEE ALSO -->
<!-- =text See xpa(n) for a list of XPA help pages -->
<!-- =section xpacmdnew SEE ALSO -->
<!-- =text See xpa(n) for a list of XPA help pages -->
<!-- =section xpacmdadd SEE ALSO -->
<!-- =text See xpa(n) for a list of XPA help pages -->
<!-- =section xpacmddel SEE ALSO -->
<!-- =text See xpa(n) for a list of XPA help pages -->
<!-- =section xpainfonew SEE ALSO -->
<!-- =text See xpa(n) for a list of XPA help pages -->
<!-- =section xpafree SEE ALSO -->
<!-- =text See xpa(n) for a list of XPA help pages -->
<!-- =section xpacleanup SEE ALSO -->
<!-- =text See xpa(n) for a list of XPA help pages -->
<!-- =section xpamainloop SEE ALSO -->
<!-- =text See xpa(n) for a list of XPA help pages -->
<!-- =section xpapoll SEE ALSO -->
<!-- =text See xpa(n) for a list of XPA help pages -->
<!-- =section xpamacros SEE ALSO -->
<!-- =text See xpa(n) for a list of XPA help pages -->
<!-- =section xparace SEE ALSO -->
<!-- =text See xpa(n) for a list of XPA help pages -->
<!-- =stop -->
<P>
<A HREF="./help.html">Go to XPA Help Index</A>
<H5>Last updated: September 10, 2003</H5>
</BODY>
</HTML>
|