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>
|