Added XML Docbook documentation for YAZ - will eventually replace
[yaz-moved-to-github.git] / doc / frontend.xml
1 <!-- $Header: /home/cvsroot/yaz/doc/frontend.xml,v 1.1 2001-01-04 13:36:24 adam Exp $ -->
2 <chapter><title id="server">Making an IR Server for Your Database</title>
3
4 <sect1><title>Introduction</title>
5
6 <para>
7 If you aren't into documentation, a good way to learn how the
8 backend interface works is to look at the <filename>backend.h</filename>
9 file. Then, look at the small dummy-server in
10 <filename>server/ztest.c</filename>. Finally, you can have a look at
11 the <filename>seshigh.c</filename> file, which is where most of the
12 logic of the frontend server is located. The <filename>backend.h</filename>
13 file also makes a good reference, once you've chewed your way through
14 the prose of this file.
15 </para>
16
17 <para>
18 If you have a database system that you would like to make available by
19 means of Z39.50/SR, &yaz; basically offers your two options. You
20 can use the APIs provided by the &asn;, &odr;, and &comstack;
21 modules to
22 create and decode PDUs, and exchange them with a client. Using this
23 low-level interface gives you access to all fields and options of the
24 protocol, and you can construct your server as close to your existing
25 database as you like. It is also a fairly involved process, requiring
26 you to set up an event-handling mechanism, protocol state machine,
27 etc. To simplify server implementation, we have implemented a compact
28 and simple, but reasonably full-functioned server-frontend that will
29 handle most of the protocol mechanics, while leaving you to
30 concentrate on your database interface.
31 </para>
32
33 <note>
34 <para>
35 The backend interface was designed in anticipation of a specific
36 integration task, while still attempting to achieve some degree of
37 generality. We realise fully that there are points where the
38 interface can be improved significantly. If you have specific
39 functions or parameters that you think could be useful, send us a
40 mail (or better, sign on to the mailing list referred to in the
41 toplevel README file). We will try to fit good suggestions into future
42 releases, to the extent that it can be done without requiring
43 too many structural changes in existing applications.
44 </para>
45 </note>
46 </sect1>
47
48 <sect1><title>The Database Frontend</title>
49
50 <para>
51 We refer to this software as a generic database frontend. Your
52 database system is the <emphasis>backend database</emphasis>, and the
53 interface between the two is called the <emphasis>backend API</emphasis>.
54 The backend API consists of a small number of function prototypes and
55 structure definitions. You are required to provide the
56 <function>main()</function> routine for the server (which can be
57 quite simple), as well as functions to match each of the prototypes.
58 The interface functions that you write can use any mechanism you like
59 to communicate with your database system: You might link the whole
60 thing together with your database application and access it by
61 function calls; you might use IPC to talk to a database server
62 somewhere; or you might link with third-party software that handles
63 the communication for you (like a commercial database client library).
64 At any rate, the functions will perform the tasks of:
65 </para>
66
67 <itemizedlist>
68
69 <listitem><para>
70 Initialization.
71 </para></listitem>
72
73 <listitem><para>
74 Searching.
75 </para></listitem>
76
77 <listitem><para>
78 Fetching records.
79 </para></listitem>
80
81 <listitem><para>
82 Scanning the database index (if you wish to implement SCAN).
83 </para></listitem>
84
85 </itemizedlist>
86
87 <para>
88 (more functions will be added in time to support as much of
89 Z39.50-1995 as possible).
90 </para>
91
92 <para>
93 Because the model where pipes or sockets are used to access the backend
94 database is a fairly common one, we have added a mechanism that allows this
95 communication to take place asynchronously. In this mode, the frontend
96 server doesn't have to block while the backend database is processing
97 a request, but can wait for additional PDUs from the client.
98 </para>
99
100 </sect1>
101 <sect1><title>The Backend API</title>
102
103 <para>
104 The headers files that you need to use the interface are in the
105 <filename>include/yaz</filename> directory. They are called
106 <filename>statserv.h</filename> and <filename>backend.h</filename>. They
107 will include other files from the <filename>include/yaz</filename>
108 directory, so you'll probably want to use the -I option of your
109 compiler to tell it where to find the files. When you run
110 <literal>make</literal> in the toplevel &yaz; directory,
111 everything you need to create your server is put the
112 <filename>lib/libyaz.a</filename> library.
113 </para>
114 </sect1>
115
116 <sect1><title>Your main() Routine</title>
117
118 <para>
119 As mentioned, your <function>main()</function> routine can be quite brief.
120 If you want to initialize global parameters, or read global configuration
121 tables, this is the place to do it. At the end of the routine, you should
122 call the function
123 </para>
124
125 <synopsis>
126   int statserv_main(int argc, char **argv);
127 </synopsis>
128
129 <para>
130 <function>statserv_main</function> will establish listening sockets
131 according to the parameters given. When connection requests are received,
132 the event handler will typically <function>fork()</function> to handle the
133 new request. If you do use global variables, you should be aware, then,
134 that these cannot be shared between associations, unless you explicitly
135 disallow forking by command line parameters (we advise against this for
136 any purposes except debugging, as a crash or hang in the server process
137 will affect all users currently signed on to the server).
138 </para>
139
140 <para>
141 The server provides a mechanism for controlling some of its behavior
142 without using command-line options. The function
143 </para>
144
145 <synopsis>
146   statserv_options_block *statserv_getcontrol(void);
147 </synopsis>
148
149 <para>
150 Will return a pointer to a <literal>struct statserv_options_block</literal>
151 describing the current default settings of the server. The structure
152 contains these elements:
153
154 <variablelist>
155 <varlistentry><term>int dynamic</term><listitem><para>
156 A boolean value, which determines whether the server
157 will fork on each incoming request (TRUE), or not (FALSE). Default is
158 TRUE.
159 </para></listitem></varlistentry>
160 <varlistentry><term>int loglevel</term><listitem><para>
161 Set this by ORing the constants defined in
162 <filename>include/yaz/yaz-log.h</filename>.
163 </para></listitem></varlistentry>
164 <varlistentry><term>char logfile&lsqb;ODR_MAXNAME+1&rsqb;</term>
165 <listitem><para>File for diagnostic output (&quot;&quot;: stderr).
166 </para></listitem></varlistentry>
167 <varlistentry><term>char apdufile&lsqb;ODR_MAXNAME+1&rsqb;</term>
168 <listitem><para>
169 Name of file for logging incoming and outgoing APDUs (&quot;&quot;: don't
170 log APDUs, &quot;-&quot;: <literal>stderr</literal>).
171 </para></listitem></varlistentry>
172 <varlistentry><term>char default_listen&lsqb;1024&rsqb;</term>
173 <listitem><para>Same form as the command-line specification of
174 listener address. &quot;&quot;: no default listener address.
175 Default is to listen at &quot;tcp:@:9999&quot;. You can only
176 specify one default listener address in this fashion.
177 </para></listitem></varlistentry>
178 <varlistentry><term>enum oid_proto default_proto;</term>
179 <listitem><para>Either <literal>PROTO_SR</literal> or
180 <literal>PROTO_Z3950</literal>. Default is <literal>PROTO_Z39_50</literal>.
181 </para></listitem></varlistentry>
182 <varlistentry><term>int idle_timeout;</term>
183 <listitem><para>Maximum session idletime, in minutes. Zero indicates
184 no (infinite) timeout. Default is 120 minutes.
185 </para></listitem></varlistentry>
186 <varlistentry><term>int maxrecordsize;</term>
187 <listitem><para>Maximum permissible record (message) size. Default
188 is 1Mb. This amount of memory will only be allocated if a client requests a
189 very large amount of records in one operation (or a big record). Set it
190 to a lower number
191 if you are worried about resource consumption on your host system.
192 </para></listitem></varlistentry>
193 <varlistentry><term>char configname&lsqb;ODR_MAXNAME+1&rsqb;</term>
194 <listitem><para>Passed to the backend when a new connection is received.
195 </para></listitem></varlistentry>
196 <varlistentry><term>char setuid&lsqb;ODR_MAXNAME+1&rsqb;</term>
197 <listitem><para>Set user id to the user specified, after binding
198 the listener addresses.
199 </para></listitem></varlistentry>
200 </variablelist>
201 </para>
202
203 <para>
204 The pointer returned by <literal>statserv_getcontrol</literal> points to
205 a static area. You are allowed to change the contents of the structure,
206 but the changes will not take effect before you call
207 </para>
208
209 <synopsis>
210   void statserv_setcontrol(statserv_options_block *block);
211 </synopsis>
212
213 <note>
214 <para>
215 that you should generally update this structure before calling
216 <function>statserv_main()</function>.
217 </para>
218 </note>
219 </sect1>
220
221 <sect1><title>The Backend Functions</title>
222
223 <para>
224 For each service of the protocol, the backend interface declares one or
225 two functions. You are required to provide implementations of the
226 functions representing the services that you wish to implement.
227 </para>
228
229 <synopsis>
230   bend_initresult *bend_init(bend_initrequest *r);
231 </synopsis>
232
233 <para>
234 This function is called once for each new connection request, after
235 a new process has been forked, and an initRequest has been received
236 from the client. The parameter and result structures are defined as
237 </para>
238
239 <synopsis>
240 typedef struct bend_initrequest
241 {
242     char *configname;
243 } bend_initrequest;
244
245 typedef struct bend_initresult
246 {
247     int errcode;       /* 0==OK */
248     char *errstring;   /* system error string or NULL */
249     void *handle;      /* private handle to the backend module */
250 } bend_initresult;
251 </synopsis>
252
253 <para>
254 The <literal>configname</literal> of <literal>bend_initrequest</literal>
255 is currently always set to &quot;default-config&quot;. We haven't had
256 use for putting anything special in the initrequest yet, but something
257 might go there if the need arises (account/password info would be obvious).
258 </para>
259
260 <para>
261 In general, the server frontend expects that the
262 <literal>bend_*result</literal> pointer that you return is valid at
263 least until the next call to a <literal>bend_* function</literal>.
264 This applies to all of the functions described herein. The parameter
265 structure passed to you in the call belongs to the server frontend, and
266 you should not make assumptions about its contents after the current
267 function call has completed. In other words, if you want to retain any
268 of the contents of a request structure, you should copy them.
269 </para>
270
271 <para>
272 The <literal>errcode</literal> should be zero if the initialization of
273 the backend went well. Any other value will be interpreted as an error.
274 The <literal>errstring</literal> isn't used in the current version, but one
275 optin would be to stick it in the initResponse as a VisibleString.
276 The <literal>handle</literal> is the most important parameter. It should
277 be set to some value that uniquely identifies the current session to
278 the backend implementation. It is used by the frontend server in any
279 future calls to a backend function.
280 The typical use is to set it to point to a dynamically allocated state
281 structure that is private to your backend module.
282 </para>
283
284 <synopsis>
285 bend_searchresult *bend_search(void *handle, bend_searchrequest *r,
286                                int *fd);
287 bend_searchresult *bend_searchresponse(void *handle);
288
289 typedef struct bend_searchrequest
290 {
291     char *setname;       /* name to give to this set */
292     int replace_set;     /* replace set, if it already exists */
293     int num_bases;       /* number of databases in list */
294     char **basenames;    /* databases to search */
295     Z_Query *query;      /* query structure */
296 } bend_searchrequest;
297
298 typedef struct bend_searchresult
299 {
300     int hits;            /* number of hits */
301     int errcode;         /* 0==OK */
302     char *errstring;     /* system error string or NULL */
303 } bend_searchresult;
304 </synopsis>
305
306 <para>
307 The first thing to notice about the search request interface (as well
308 as all of the following requests), is that it consists of two separate
309 functions. The idea is to provide a simple facility for
310 asynchronous communication with the backend server. When a
311 searchrequest comes in, the server frontend will fill out the
312 <function>bend_searchrequest</function> tructure, and call the
313 <function>bend_search</function> function/. The <literal>fd</literal>
314 argument will point to an integer variable. If you are able to do
315 asynchronous I/O with your database server, you should set
316 <literal>*fd</literal> to the file descriptor you use for the
317 communication, and return a null pointer.
318 The server frontend will then <function>select()</function> on the
319 <literal>*fd</literal>, and will call
320 <function>bend_searchresult</function> when it sees that data is available.
321 If you don't support asynchronous I/O, you should return a pointer to the
322 <function>bend_searchresult</function> immediately, and leave 
323 <literal>*fd</literal> untouched. This construction is common to
324 all of the <function>bend_</function> functions (except 
325 <function>bend_init</function>). Note that you can choose to support
326 this facility in none, any, or all of the <function>bend_</function>
327 functions, and you can respond differently on each request at run-time.
328 The server frontend will adapt accordingly.
329 </para>
330
331 <para>
332 The <function>bend_searchrequest</function> is a fairly close
333 approximation of a protocol searchRequest PDU. The
334 <literal>setname</literal> is the resultSetName from the protocol. You
335 are required to establish a mapping between the set name and whatever
336 your backend database likes to use. Similarly, the
337 <literal>replace_set</literal> is a boolean value corresponding to the
338 resultSetIndicator field in the protocol.
339 <literal>Num_bases/basenames</literal> is a length of/array of character
340 pointers to the database names provided by the client. The
341 <literal>query</literal> is the full query structure as defined in the
342 protocol ASN.1 specification. It can be either of the possible query
343 types, and it's up to you to determine if you can handle the provided
344 query type. Rather than reproduce the C interface here, we'll refer you
345 to the structure definitions in the file
346 <filename>include/yaz/proto.h</filename>. If you want to look at the
347 attributeSetId OID of the RPN query, you can either match it against
348 your own internal tables, or you can use the
349 <literal>oid_getentbyoid</literal> function provided by &yaz;.
350 </para>
351
352 <para>
353 The result structure contains a number of hits, and an
354 <literal>errcode/errstring</literal> pair. If an error occurs
355 during the search, or if you're unhappy with the request, you should
356 set the errcode to a value from the BIB-1 diagnostic set. The value
357 will then be returned to the user in a nonsurrogate diagnostic record
358 in the response. The <literal>errstring</literal>, if provided, will
359 go in the addinfo field. Look at the protocol definition for the
360 defined error codes, and the suggested uses of the addinfo field.
361 </para>
362
363 <synopsis>
364 bend_fetchresult *bend_fetch(void *handle, bend_fetchrequest *r,
365                              int *fd);
366 bend_fetchresult *bend_fetchresponse(void *handle);
367
368 typedef struct bend_fetchrequest
369 {
370     char *setname;       /* set name */
371     int number;          /* record number */
372     oid_value format;
373 } bend_fetchrequest;
374
375 typedef struct bend_fetchresult
376 {
377     char *basename;      /* name of database that provided record */
378     int len;             /* length of record */
379     char *record;        /* record */
380     int last_in_set;     /* is it?  */
381     oid_value format;
382     int errcode;         /* 0==success */
383     char *errstring;     /* system error string or NULL */
384 } bend_fetchresult;
385 </synopsis>
386
387 <note>
388 <para>
389 The <function>bend_fetchresponse()</function> function is not yet supported
390 in this version of the software. Your implementation of
391 <function>bend_fetch()</function> should always return a pointer to a
392 <literal>bend_fetchresult</literal>.
393 </para>
394 </note>
395
396 <para>
397 The frontend server calls <function>bend_fetch</function> when it needs
398 database records to fulfill a searchRequest or a presentRequest.
399 The <literal>setname</literal> is simply the name of the result set
400 that holds the reference to the desired record.
401 The <literal>number</literal> is the offset into the set (with 1
402 being the first record in the set). The <literal>format</literal> field
403 is the record format requested by the client (See section
404 <link linkend="oid">Object Identifiers</link>). The value
405 <literal>VAL_NONE</literal> indicates that the client did not
406 request a specific format. The <literal>stream</literal> argument
407 is an &odr; stream which should be used for
408 allocating space for structured data records. The stream will be reset when
409 all records have been assembled, and the response package has been transmitted.
410 For unstructured data, the backend is responsible for maintaining a static
411 or dynamic buffer for the record between calls.
412 </para>
413
414 <para>
415 In the result structure, the <literal>basename</literal> is the name of the
416 database that holds the
417 record. <literal>len</literal> is the length of the record returned, in
418 bytes, and <literal>record</literal> is a pointer to the record.
419 <literal>Last_in_set</literal> should be nonzero only if the record
420 returned is the last one in the given result set. <literal>errcode</literal>
421 and <literal>errstring</literal>, if given, will currently be
422 interpreted as a global error pertaining to the set, and will be returned in a
423 nonSurrogateDiagnostic.
424 </para>
425
426 <note>
427 <para>
428 This is silly. Add a flag to say which is which.
429 </para>
430 </note>
431
432 <para>
433 If the <literal>len</literal> field has the value -1, then
434 <literal>record</literal> is assumed to point to a constructed data
435 type. The <literal>format</literal> field will be used to determine
436 which encoder should be used to serialize the data.
437 </para>
438
439 <note>
440 <para>
441 If your backend generates structured records, it should use
442 <function>odr_malloc()</function> on the provided stream for allocating
443 data: This allows the frontend server to keep track of the record sizes.
444 </para>
445 </note>
446
447 <para>
448 The <literal>format</literal> field is mapped to an object identifier
449 in the direct reference of the resulting EXTERNAL representation of the record.
450 </para>
451
452 <note>
453 <para>
454 The current version of &yaz; only supports the direct reference mode.
455 </para>
456 </note>
457
458 <synopsis>
459 bend_deleteresult *bend_delete(void *handle, bend_deleterequest *r,
460                                int *fd);
461 bend_deleteresult *bend_deleteresponse(void *handle);
462
463 typedef struct bend_deleterequest
464 {
465     char *setname;
466 } bend_deleterequest;
467
468 typedef struct bend_deleteresult
469 {
470     int errcode;         /* 0==success */
471     char *errstring;     /* system error string or NULL */
472 } bend_deleteresult;
473 </synopsis>
474
475 <note>
476 <para>
477 The &quot;delete&quot; function is not yet supported in this version of
478 the software.
479 </para>
480 </note>
481
482 <note>
483 <para>
484 The delete set function definition is rather primitive, mostly because we
485 have had no practical need for it as of yet. If someone wants
486 to provide a full delete service, we'd be happy to add the
487 extra parameters that are required. Are there clients out there
488 that will actually delete sets they no longer need?
489 </para>
490 </note>
491
492 <synopsis>
493 bend_scanresult *bend_scan(void *handle, bend_scanrequest *r,
494     int *fd);
495 bend_scanresult *bend_scanresponse(void *handle);
496
497 typedef struct bend_scanrequest
498 {
499     int num_bases;      /* number of elements in databaselist */
500     char **basenames;   /* databases to search */
501     Z_AttributesPlusTerm *term;
502     int term_position;  /* desired index of term in result list */
503     int num_entries;    /* number of entries requested */
504 } bend_scanrequest;
505
506 typedef struct bend_scanresult
507 {
508     int num_entries;
509     struct scan_entry
510     {
511         char *term;
512         int occurrences;
513     } *entries;
514     int term_position;
515     enum
516     {
517         BEND_SCAN_SUCCESS,
518         BEND_SCAN_PARTIAL
519     } status;
520     int errcode;
521     char *errstring;
522 } bend_scanresult;
523 </synopsis>
524
525 <note>
526 <para>
527 The <function>bend_scanresponse()</function> function is not yet supported
528 in this version of the software. Your implementation of
529 <function>bend_scan()</function> should always return a pointer to a
530 <literal>bend_scanresult</literal>.
531 </para>
532 </note>
533 </sect1>
534
535 <sect1><title>Application Invocation</title>
536
537 <para>
538 The finished application has the following
539 invocation syntax (by way of <function>statserv_main()</function>):
540 </para>
541
542 <synopsis>
543 appname &lsqb;-szSu -a apdufile -l logfile -v loglevel&rsqb;
544 &lsqb;listener ...&rsqb;
545 </synopsis>
546
547 <para>
548 The options are
549
550 <variablelist>
551
552 <varlistentry><term>-a <replaceable>file</replaceable></term>
553  <listitem><para>
554 Specify a file for dumping PDUs (for diagnostic purposes).
555 The special name &quot;-&quot; sends output to <literal>stderr</literal>.
556 </para></listitem></varlistentry>
557
558 <varlistentry><term>-S</term>
559  <listitem><para>
560 Don't fork on connection requests. This is good for debugging, but
561 not recommended for real operation: Although the server is
562 asynchronous and non-blocking, it can be nice to keep a software
563 malfunction (okay then, a crash) from affecting all current users.
564 </para></listitem></varlistentry>
565
566 <varlistentry><term>-s</term>
567 <listitem><para>
568 Use the SR protocol.
569 </para></listitem></varlistentry>
570
571 <varlistentry><term>-z</term>
572 <listitem><para>
573 Use the Z39.50 protocol (default). These two options complement
574 eachother. You can use both multiple times on the same command
575 line, between listener-specifications (see below). This way, you
576 can set up the server to listen for connections in both protocols
577 concurrently, on different local ports.
578 </para></listitem></varlistentry>
579
580 <varlistentry><term>-l <replaceable>file</replaceable></term>
581 <listitem><para>The logfile.
582 </para></listitem></varlistentry>
583
584 <varlistentry><term>-v <replaceable>level</replaceable></term>
585 <listitem><para>
586 The log level. Use a comma-separated list of members of the set
587 {fatal,debug,warn,log,all,none}.
588 </para></listitem></varlistentry>
589
590 <varlistentry><term>-u <replaceable>userid</replaceable></term>
591 <listitem><para>
592 Set user ID. Sets the real UID of the server process to that of the
593 given user. It's useful if you aren't comfortable with having the
594 server run as root, but you need to start it as such to bind a
595 privileged port.
596 </para></listitem></varlistentry>
597
598 <varlistentry><term>-w <replaceable>dir</replaceable></term>
599 <listitem><para>
600 Working directory.
601 </para></listitem></varlistentry>
602
603 <varlistentry><term>-i</term>
604 <listitem><para>
605 Use this when running from the <application>inetd</application> server.
606 </para></listitem></varlistentry>
607
608 <varlistentry><term>-t <replaceable>minutes</replaceable></term>
609 <listitem><para>
610 Idle session timeout, in minutes.
611 </para></listitem></varlistentry>
612
613 <varlistentry><term>-k <replaceable>size</replaceable></term>
614 <listitem><para>
615 Maximum record size/message size, in kilobytes.
616 </para></listitem></varlistentry>
617
618 </variablelist>
619 </para>
620
621 <para>
622 A listener specification consists of a transport mode followed by a
623 colon (:) followed by a listener address. The transport mode is
624 either <literal>osi</literal> or <literal>tcp</literal>.
625 </para>
626
627 <para>
628 For TCP, an address has the form
629 </para>
630
631 <synopsis>
632   hostname | IP-number &lsqb;: portnumber&rsqb;
633 </synopsis>
634
635 <para>
636 The port number defaults to 210 (standard Z39.50 port).
637 </para>
638
639 <para>
640 For osi, the address form is
641 </para>
642
643 <synopsis>
644   &lsqb;t-selector /&rsqb; hostname | IP-number &lsqb;: portnumber&rsqb;
645 </synopsis>
646
647 <para>
648 The transport selector is given as a string of hex digits (with an even
649 number of digits). The default port number is 102 (RFC1006 port).
650 </para>
651
652 <para>
653 Examples
654 </para>
655
656 <screen>
657   tcp:dranet.dra.com
658
659   osi:0402/dbserver.osiworld.com:3000
660 </screen>
661
662 <para>
663 In both cases, the special hostname &quot;@&quot; is mapped to
664 the address INADDR_ANY, which causes the server to listen on any local
665 interface. To start the server listening on the registered ports for
666 Z39.50 and SR over OSI/RFC1006, and to drop root privileges once the
667 ports are bound, execute the server like this (from a root shell):
668 </para>
669
670 <screen>
671   my-server -u daemon tcp:@ -s osi:@
672 </screen>
673
674 <para>
675 You can replace <literal>daemon</literal> with another user, eg. your
676 own account, or a dedicated IR server account.
677 <literal>my-server</literal> should be the name of your
678 server application. You can test the procedure with the
679 <application>yaz-ztest</application> application.
680 </para>
681
682 </sect1>
683 <sect1><title>Summary and Synopsis</title>
684
685 <synopsis>
686 #include &lt;backend.h>
687
688 bend_initresult *bend_init(bend_initrequest *r);
689
690 bend_searchresult *bend_search(void *handle, bend_searchrequest *r,
691                                  int *fd);
692
693 bend_searchresult *bend_searchresponse(void *handle);
694
695 bend_fetchresult *bend_fetch(void *handle, bend_fetchrequest *r,
696                                int *fd);
697
698 bend_fetchresult *bend_fetchresponse(void *handle);
699
700 bend_scanresult *bend_scan(void *handle, bend_scanrequest *r, int *fd);
701
702 bend_scanresult *bend_scanresponse(void *handle);
703
704 bend_deleteresult *bend_delete(void *handle, bend_deleterequest *r,
705                                   int *fd);
706
707 bend_deleteresult *bend_deleteresponse(void *handle);
708
709 void bend_close(void *handle);
710 </synopsis>
711 </sect1>
712 </chapter>
713