XMLFILES=bookinfo.xml introduction.xml installation.xml \
indexdata.xml \
- asn.xml tools.xml odr.xml comstack.xml frontend.xml license.xml \
+ asn.xml tools.xml odr.xml comstack.xml server.xml license.xml \
future.xml zoom.xml credits.xml gfs-options.xml \
yaz.xml yaz-client-commands.xml soap.xml gfs-virtual.xml gfs-synopsis.xml \
std-oid-table.xml manref.xml
<!ENTITY chap-tools SYSTEM "tools.xml">
<!ENTITY chap-odr SYSTEM "odr.xml">
<!ENTITY chap-comstack SYSTEM "comstack.xml">
-<!ENTITY chap-frontend SYSTEM "frontend.xml">
+<!ENTITY chap-server SYSTEM "server.xml">
<!ENTITY chap-future SYSTEM "future.xml">
<!ENTITY std-oid-table SYSTEM "std-oid-table.xml">
<!ENTITY app-license SYSTEM "license.xml">
+++ /dev/null
- <chapter id="server"><title>Generic server</title>
- <sect1 id="server.introduction"><title>Introduction</title>
-
- <para>
- If you aren't into documentation, a good way to learn how the
- back end interface works is to look at the <filename>backend.h</filename>
- file. Then, look at the small dummy-server in
- <filename>ztest/ztest.c</filename>. The <filename>backend.h</filename>
- file also makes a good reference, once you've chewed your way through
- the prose of this file.
- </para>
-
- <para>
- If you have a database system that you would like to make available by
- means of Z39.50 or SRU, &yaz; basically offers your two options. You
- can use the APIs provided by the &asn;, &odr;, and &comstack;
- modules to
- create and decode PDUs, and exchange them with a client.
- Using this low-level interface gives you access to all fields and
- options of the protocol, and you can construct your server as close
- to your existing database as you like.
- It is also a fairly involved process, requiring
- you to set up an event-handling mechanism, protocol state machine,
- etc. To simplify server implementation, we have implemented a compact
- and simple, but reasonably full-functioned server-frontend that will
- handle most of the protocol mechanics, while leaving you to
- concentrate on your database interface.
- </para>
-
- <note>
- <para>
- The backend interface was designed in anticipation of a specific
- integration task, while still attempting to achieve some degree of
- generality. We realize fully that there are points where the
- interface can be improved significantly. If you have specific
- functions or parameters that you think could be useful, send us a
- mail (or better, sign on to the mailing list referred to in the
- top-level README file). We will try to fit good suggestions into future
- releases, to the extent that it can be done without requiring
- too many structural changes in existing applications.
- </para>
- </note>
-
- <note>
- <para>
- The &yaz; server does not support XCQL.
- </para>
- </note>
- </sect1>
-
- <sect1 id="server.frontend"><title>The Database Frontend</title>
-
- <para>
- We refer to this software as a generic database frontend. Your
- database system is the <emphasis>backend database</emphasis>, and the
- interface between the two is called the <emphasis>backend API</emphasis>.
- The backend API consists of a small number of function handlers and
- structure definitions. You are required to provide the
- <function>main()</function> routine for the server (which can be
- quite simple), as well as a set of handlers to match each of the
- prototypes.
- The interface functions that you write can use any mechanism you like
- to communicate with your database system: You might link the whole
- thing together with your database application and access it by
- function calls; you might use IPC to talk to a database server
- somewhere; or you might link with third-party software that handles
- the communication for you (like a commercial database client library).
- At any rate, the handlers will perform the tasks of:
- </para>
-
- <itemizedlist>
-
- <listitem><para>
- Initialization.
- </para></listitem>
-
- <listitem><para>
- Searching.
- </para></listitem>
-
- <listitem><para>
- Fetching records.
- </para></listitem>
-
- <listitem><para>
- Scanning the database index (optional - if you wish to implement SCAN).
- </para></listitem>
-
- <listitem><para>
- Extended Services (optional).
- </para></listitem>
-
- <listitem><para>
- Result-Set Delete (optional).
- </para></listitem>
-
- <listitem><para>
- Result-Set Sort (optional).
- </para></listitem>
-
- <listitem><para>
- Return Explain for SRU (optional).
- </para></listitem>
-
- </itemizedlist>
-
- <para>
- (more functions will be added in time to support as much of
- Z39.50-1995 as possible).
- </para>
-
- </sect1>
- <sect1 id="server.backend"><title>The Backend API</title>
-
- <para>
- The header file that you need to use the interface are in the
- <filename>include/yaz</filename> directory. It's called
- <filename>backend.h</filename>. It will include other files from
- the <filename>include/yaz</filename> directory, so you'll
- probably want to use the -I option of your compiler to tell it
- where to find the files. When you run
- <literal>make</literal> in the top-level &yaz; directory,
- everything you need to create your server is to link with the
- <filename>lib/libyaz.la</filename> library.
- </para>
- </sect1>
-
- <sect1 id="server.main"><title>Your main() Routine</title>
-
- <para>
- As mentioned, your <function>main()</function> routine can be quite brief.
- If you want to initialize global parameters, or read global configuration
- tables, this is the place to do it. At the end of the routine, you should
- call the function
- </para>
-
- <synopsis>
-int statserv_main(int argc, char **argv,
- bend_initresult *(*bend_init)(bend_initrequest *r),
- void (*bend_close)(void *handle));
- </synopsis>
-
- <para>
- The third and fourth arguments are pointers to handlers. Handler
- <function>bend_init</function> is called whenever the server receives
- an Initialize Request, so it serves as a Z39.50 session initializer. The
- <function>bend_close</function> handler is called when the session is
- closed.
- </para>
-
- <para>
- <function>statserv_main</function> will establish listening sockets
- according to the parameters given. When connection requests are received,
- the event handler will typically <function>fork()</function> and
- create a sub-process to handle a new connection.
- Alternatively the server may be setup to create threads for each
- connection.
- If you do use global variables and forking, you should be aware, then,
- that these cannot be shared between associations, unless you explicitly
- disable forking by command line parameters.
- </para>
-
- <para>
- The server provides a mechanism for controlling some of its behavior
- without using command-line options. The function
- </para>
-
- <synopsis>
- statserv_options_block *statserv_getcontrol(void);
- </synopsis>
-
- <para>
- will return a pointer to a <literal>struct statserv_options_block</literal>
- describing the current default settings of the server. The structure
- contains these elements:
-
- <variablelist>
- <varlistentry><term>
- <literal>int dynamic</literal></term><listitem><para>
- A boolean value, which determines whether the server
- will fork on each incoming request (TRUE), or not (FALSE). Default is
- TRUE. This flag is only read by UNIX-based servers (WIN32 based servers
- doesn't fork).
- </para></listitem></varlistentry>
-
- <varlistentry><term>
- <literal>int threads</literal></term><listitem><para>
- A boolean value, which determines whether the server
- will create a thread on each incoming request (TRUE), or not (FALSE).
- Default is FALSE. This flag is only read by UNIX-based servers
- that offer POSIX Threads support.
- WIN32-based servers always operate in threaded mode.
- </para></listitem></varlistentry>
-
- <varlistentry><term>
- <literal>int inetd</literal></term><listitem><para>
- A boolean value, which determines whether the server
- will operates under a UNIX INET daemon (inetd). Default is FALSE.
- </para></listitem></varlistentry>
-
- <varlistentry><term>
- <literal>char logfile[ODR_MAXNAME+1]</literal></term>
- <listitem><para>File for diagnostic output ("": stderr).
- </para></listitem></varlistentry>
-
- <varlistentry><term>
- <literal>char apdufile[ODR_MAXNAME+1]</literal></term>
- <listitem><para>
- Name of file for logging incoming and outgoing APDUs
- ("": don't log APDUs, "-":
- <literal>stderr</literal>).
- </para></listitem></varlistentry>
-
- <varlistentry><term>
- <literal>char default_listen[1024]</literal></term>
- <listitem><para>Same form as the command-line specification of
- listener address. "": no default listener address.
- Default is to listen at "tcp:@:9999". You can only
- specify one default listener address in this fashion.
- </para></listitem></varlistentry>
-
- <varlistentry><term>
- <literal>enum oid_proto default_proto;</literal></term>
- <listitem><para>Either <literal>PROTO_Z3950</literal> or
- <literal>PROTO_SR</literal>.
- Default is <literal>PROTO_Z39_50</literal>.
- </para></listitem></varlistentry>
-
- <varlistentry><term>
- <literal>int idle_timeout;</literal></term>
- <listitem><para>Maximum session idle-time, in minutes. Zero indicates
- no (infinite) timeout. Default is 15 minutes.
- </para></listitem></varlistentry>
-
- <varlistentry><term>
- <literal>int maxrecordsize;</literal></term>
- <listitem><para>Maximum permissible record (message) size. Default
- is 1Mb. This amount of memory will only be allocated if a
- client requests a very large amount of records in one operation
- (or a big record).
- Set it to a lower number if you are worried about resource
- consumption on your host system.
- </para></listitem></varlistentry>
-
- <varlistentry><term>
- <literal>char configname[ODR_MAXNAME+1]</literal></term>
- <listitem><para>Passed to the backend when a new connection is received.
- </para></listitem></varlistentry>
-
- <varlistentry><term>
- <literal>char setuid[ODR_MAXNAME+1]</literal></term>
- <listitem><para>Set user id to the user specified, after binding
- the listener addresses.
- </para></listitem></varlistentry>
-
- <varlistentry><term>
- <literal>void (*bend_start)(struct statserv_options_block *p)</literal>
- </term>
- <listitem><para>Pointer to function which is called after the
- command line options have been parsed - but before the server
- starts listening.
- For forked UNIX servers this handler is called in the mother
- process; for threaded servers this handler is called in the
- main thread.
- The default value of this pointer is NULL in which case it
- isn't invoked by the frontend server.
- When the server operates as an NT service this handler is called
- whenever the service is started.
- </para></listitem></varlistentry>
-
- <varlistentry><term>
- <literal>void (*bend_stop)(struct statserv_options_block *p)</literal>
- </term>
- <listitem><para>Pointer to function which is called whenever the server
- has stopped listening for incoming connections. This function pointer
- has a default value of NULL in which case it isn't called.
- When the server operates as an NT service this handler is called
- whenever the service is stopped.
- </para></listitem></varlistentry>
-
- <varlistentry><term>
- <literal>void *handle</literal></term>
- <listitem><para>User defined pointer (default value NULL).
- This is a per-server handle that can be used to specify "user-data".
- Do not confuse this with the session-handle as returned by bend_init.
- </para></listitem></varlistentry>
-
- </variablelist>
- </para>
-
- <para>
- The pointer returned by <literal>statserv_getcontrol</literal> points to
- a static area. You are allowed to change the contents of the structure,
- but the changes will not take effect before you call
- </para>
-
- <synopsis>
-void statserv_setcontrol(statserv_options_block *block);
- </synopsis>
-
- <note>
- <para>
- that you should generally update this structure before calling
- <function>statserv_main()</function>.
- </para>
- </note>
- </sect1>
-
- <sect1 id="server.backendfunctions"><title>The Backend Functions</title>
-
- <para>
- For each service of the protocol, the backend interface declares one or
- two functions. You are required to provide implementations of the
- functions representing the services that you wish to implement.
- </para>
-
- <sect2 id="server.init"><title>Init</title>
-
- <synopsis>
-bend_initresult (*bend_init)(bend_initrequest *r);
- </synopsis>
-
- <para>
- This handler is called once for each new connection request, after
- a new process/thread has been created, and an Initialize Request has
- been received from the client. The pointer to the
- <function>bend_init</function> handler is passed in the call to
- <function>statserv_start</function>.
- </para>
-
- <para>
- This handler is also called when operating in SRU mode - when
- a connection has been made (even though SRU does not offer
- this service).
- </para>
-
- <para>
- Unlike previous versions of YAZ, the <function>bend_init</function> also
- serves as a handler that defines the Z39.50 services that the backend
- wish to support. Pointers to <emphasis>all</emphasis> service handlers,
- including search - and fetch must be specified here in this handler.
- </para>
- <para>
- The request - and result structures are defined as
- </para>
-
- <synopsis>
-typedef struct bend_initrequest
-{
- /** \brief user/name/password to be read */
- Z_IdAuthentication *auth;
- /** \brief encoding stream (for results) */
- ODR stream;
- /** \brief printing stream */
- ODR print;
- /** \brief decoding stream (use stream for results) */
- ODR decode;
- /** \brief reference ID */
- Z_ReferenceId *referenceId;
- /** \brief peer address of client */
- char *peer_name;
-
- /** \brief character set and language negotiation
-
- see include/yaz/z-charneg.h
- */
- Z_CharSetandLanguageNegotiation *charneg_request;
-
- /** \brief character negotiation response */
- Z_External *charneg_response;
-
- /** \brief character set (encoding) for query terms
-
- This is NULL by default. It should be set to the native character
- set that the backend assumes for query terms */
- char *query_charset;
-
- /** \brief whehter query_charset also applies to recors
-
- Is 0 (No) by default. Set to 1 (yes) if records is in the same
- character set as queries. If in doubt, use 0 (No).
- */
- int records_in_same_charset;
-
- char *implementation_id;
- char *implementation_name;
- char *implementation_version;
-
- /** \brief Z39.50 sort handler */
- int (*bend_sort)(void *handle, bend_sort_rr *rr);
- /** \brief SRU/Z39.50 search handler */
- int (*bend_search)(void *handle, bend_search_rr *rr);
- /** \brief SRU/Z39.50 fetch handler */
- int (*bend_fetch)(void *handle, bend_fetch_rr *rr);
- /** \brief SRU/Z39.50 present handler */
- int (*bend_present)(void *handle, bend_present_rr *rr);
- /** \brief Z39.50 extended services handler */
- int (*bend_esrequest) (void *handle, bend_esrequest_rr *rr);
- /** \brief Z39.50 delete result set handler */
- int (*bend_delete)(void *handle, bend_delete_rr *rr);
- /** \brief Z39.50 scan handler */
- int (*bend_scan)(void *handle, bend_scan_rr *rr);
- /** \brief Z39.50 segment facility handler */
- int (*bend_segment)(void *handle, bend_segment_rr *rr);
- /** \brief SRU explain handler */
- int (*bend_explain)(void *handle, bend_explain_rr *rr);
- /** \brief SRU scan handler */
- int (*bend_srw_scan)(void *handle, bend_scan_rr *rr);
- /** \brief SRU record update handler */
- int (*bend_srw_update)(void *handle, bend_update_rr *rr);
-
- /** \brief whether named result sets are supported (0=disable, 1=enable) */
- int named_result_sets;
-} bend_initrequest;
-
-typedef struct bend_initresult
-{
- int errcode; /* 0==OK */
- char *errstring; /* system error string or NULL */
- void *handle; /* private handle to the backend module */
-} bend_initresult;
- </synopsis>
-
- <para>
- In general, the server frontend expects that the
- <literal>bend_*result</literal> pointer that you return is valid at
- least until the next call to a <literal>bend_* function</literal>.
- This applies to all of the functions described herein. The parameter
- structure passed to you in the call belongs to the server frontend, and
- you should not make assumptions about its contents after the current
- function call has completed. In other words, if you want to retain any
- of the contents of a request structure, you should copy them.
- </para>
-
- <para>
- The <literal>errcode</literal> should be zero if the initialization of
- the backend went well. Any other value will be interpreted as an error.
- The <literal>errstring</literal> isn't used in the current version, but
- one option would be to stick it in the initResponse as a VisibleString.
- The <literal>handle</literal> is the most important parameter. It should
- be set to some value that uniquely identifies the current session to
- the backend implementation. It is used by the frontend server in any
- future calls to a backend function.
- The typical use is to set it to point to a dynamically allocated state
- structure that is private to your backend module.
- </para>
-
- <para>
- The <literal>auth</literal> member holds the authentication information
- part of the Z39.50 Initialize Request. Interpret this if your serves
- requires authentication.
- </para>
-
- <para>
- The members <literal>peer_name</literal>,
- <literal>implementation_id</literal>,
- <literal>implementation_name</literal> and
- <literal>implementation_version</literal> holds
- DNS of client, ID of implementor, name
- of client (Z39.50) implementation - and version.
- </para>
-
- <para>
- The <literal>bend_</literal> - members are set to NULL when
- <function>bend_init</function> is called. Modify the pointers by
- setting them to point to backend functions.
- </para>
-
- </sect2>
-
- <sect2 id="server.search.retrieve"><title>Search and Retrieve</title>
-
- <para>We now describe the handlers that are required to support search -
- and retrieve. You must support two functions - one for search - and one
- for fetch (retrieval of one record). If desirable you can provide a
- third handler which is called when a present request is received which
- allows you to optimize retrieval of multiple-records.
- </para>
-
- <synopsis>
-int (*bend_search) (void *handle, bend_search_rr *rr);
-
-typedef struct {
- char *setname; /* name to give to this set */
- int replace_set; /* replace set, if it already exists */
- int num_bases; /* number of databases in list */
- char **basenames; /* databases to search */
- Z_ReferenceId *referenceId;/* reference ID */
- Z_Query *query; /* query structure */
- ODR stream; /* encode stream */
- ODR decode; /* decode stream */
- ODR print; /* print stream */
-
- bend_request request;
- bend_association association;
- int *fd;
- int hits; /* number of hits */
- int errcode; /* 0==OK */
- char *errstring; /* system error string or NULL */
- Z_OtherInformation *search_info; /* additional search info */
- char *srw_sortKeys; /* holds SRU/SRW sortKeys info */
- char *srw_setname; /* holds SRU/SRW generated resultsetID */
- int *srw_setnameIdleTime; /* holds SRU/SRW life-time */
- int estimated_hit_count; /* if hit count is estimated */
- int partial_resultset; /* if result set is partial */
-} bend_search_rr;
- </synopsis>
-
- <para>
- The <function>bend_search</function> handler is a fairly close
- approximation of a protocol Z39.50 Search Request - and Response PDUs
- The <literal>setname</literal> is the resultSetName from the protocol.
- You are required to establish a mapping between the set name and whatever
- your backend database likes to use.
- Similarly, the <literal>replace_set</literal> is a boolean value
- corresponding to the resultSetIndicator field in the protocol.
- <literal>num_bases/basenames</literal> is a length of/array of character
- pointers to the database names provided by the client.
- The <literal>query</literal> is the full query structure as defined in
- the protocol ASN.1 specification.
- It can be either of the possible query types, and it's up to you to
- determine if you can handle the provided query type.
- Rather than reproduce the C interface here, we'll refer you to the
- structure definitions in the file
- <filename>include/yaz/z-core.h</filename>. If you want to look at the
- attributeSetId OID of the RPN query, you can either match it against
- your own internal tables, or you can use the <link linkend="tools.oid">
- OID tools</link>.
- </para>
-
- <para>
- The structure contains a number of hits, and an
- <literal>errcode/errstring</literal> pair. If an error occurs
- during the search, or if you're unhappy with the request, you should
- set the errcode to a value from the BIB-1 diagnostic set. The value
- will then be returned to the user in a nonsurrogate diagnostic record
- in the response. The <literal>errstring</literal>, if provided, will
- go in the addinfo field. Look at the protocol definition for the
- defined error codes, and the suggested uses of the addinfo field.
- </para>
-
- <para>
- The <function>bend_search</function> handler is also called when
- the frontend server receives a SRU SearchRetrieveRequest.
- For SRU, a CQL query is usually provided by the client.
- The CQL query is available as part of <literal>Z_Query</literal>
- structure (note that CQL is now part of Z39.50 via an external).
- To support CQL in existing implementations that only do Type-1,
- we refer to the CQL-to-PQF tool described
- <link linkend="cql.to.pqf">here</link>.
- </para>
-
- <para>
- To maintain backwards compatibility, the frontend server
- of yaz always assume that error codes are BIB-1 diagnostics.
- For SRU operation, a Bib-1 diagnostic code is mapped to
- SRU diagnostic.
- </para>
-
- <synopsis>
-int (*bend_fetch) (void *handle, bend_fetch_rr *rr);
-
-typedef struct bend_fetch_rr {
- char *setname; /* set name */
- int number; /* record number */
- Z_ReferenceId *referenceId;/* reference ID */
- Odr_oid *request_format; /* format, transfer syntax (OID) */
- Z_RecordComposition *comp; /* Formatting instructions */
- ODR stream; /* encoding stream - memory source if req */
- ODR print; /* printing stream */
-
- char *basename; /* name of database that provided record */
- int len; /* length of record or -1 if structured */
- char *record; /* record */
- int last_in_set; /* is it? */
- Odr_oid *output_format; /* response format/syntax (OID) */
- int errcode; /* 0==success */
- char *errstring; /* system error string or NULL */
- int surrogate_flag; /* surrogate diagnostic */
- char *schema; /* string record schema input/output */
-} bend_fetch_rr;
- </synopsis>
-
- <para>
- The frontend server calls the <function>bend_fetch</function> handler
- when it needs database records to fulfill a Z39.50 Search Request, a
- Z39.50 Present Request or a SRU SearchRetrieveRequest.
- The <literal>setname</literal> is simply the name of the result set
- that holds the reference to the desired record.
- The <literal>number</literal> is the offset into the set (with 1
- being the first record in the set). The <literal>format</literal> field
- is the record format requested by the client (See
- <xref linkend="tools.oid"/>).
- A value of NULL for <literal>format</literal> indicates that the
- client did not request a specific format.
- The <literal>stream</literal> argument is an &odr; stream which
- should be used for allocating space for structured data records.
- The stream will be reset when all records have been assembled, and
- the response package has been transmitted.
- For unstructured data, the backend is responsible for maintaining a
- static or dynamic buffer for the record between calls.
- </para>
-
- <para>
- If a SRU SearchRetrieveRequest is received by the frontend server,
- the <literal>referenceId</literal> is NULL and the
- <literal>format</literal> (transfer syntax) is the OID for XML.
- The schema for SRU is stored in both the
- <literal>Z_RecordComposition</literal>
- structure and <literal>schema</literal> (simple string).
- </para>
-
- <para>
- In the structure, the <literal>basename</literal> is the name of the
- database that holds the
- record. <literal>len</literal> is the length of the record returned, in
- bytes, and <literal>record</literal> is a pointer to the record.
- <literal>last_in_set</literal> should be nonzero only if the record
- returned is the last one in the given result set.
- <literal>errcode</literal> and <literal>errstring</literal>, if
- given, will be interpreted as a global error pertaining to the
- set, and will be returned in a non-surrogate-diagnostic.
- If you wish to return the error as a surrogate-diagnostic
- (local error) you can do this by setting
- <literal>surrogate_flag</literal> to 1 also.
- </para>
-
- <para>
- If the <literal>len</literal> field has the value -1, then
- <literal>record</literal> is assumed to point to a constructed data
- type. The <literal>format</literal> field will be used to determine
- which encoder should be used to serialize the data.
- </para>
-
- <note>
- <para>
- If your backend generates structured records, it should use
- <function>odr_malloc()</function> on the provided stream for allocating
- data: This allows the frontend server to keep track of the record sizes.
- </para>
- </note>
-
- <para>
- The <literal>format</literal> field is mapped to an object identifier
- in the direct reference of the resulting EXTERNAL representation
- of the record.
- </para>
-
- <note>
- <para>
- The current version of &yaz; only supports the direct reference mode.
- </para>
- </note>
-
- <synopsis>
-int (*bend_present) (void *handle, bend_present_rr *rr);
-
-typedef struct {
- char *setname; /* set name */
- int start;
- int number; /* record number */
- Odr_oid *format; /* format, transfer syntax (OID) */
- Z_ReferenceId *referenceId;/* reference ID */
- Z_RecordComposition *comp; /* Formatting instructions */
- ODR stream; /* encoding stream - memory source if required */
- ODR print; /* printing stream */
- bend_request request;
- bend_association association;
-
- int hits; /* number of hits */
- int errcode; /* 0==OK */
- char *errstring; /* system error string or NULL */
-} bend_present_rr;
- </synopsis>
-
- <para>
- The <function>bend_present</function> handler is called when
- the server receives a Z39.50 Present Request.
- The <literal>setname</literal>,
- <literal>start</literal> and <literal>number</literal> is the
- name of the result set - start position - and number of records to
- be retrieved respectively. <literal>format</literal> and
- <literal>comp</literal> is the preferred transfer syntax and element
- specifications of the present request.
- </para>
- <para>
- Note that this is handler serves as a supplement for
- <function>bend_fetch</function> and need not to be defined in order to
- support search - and retrieve.
- </para>
-
- </sect2>
-
- <sect2 id="server.delete"><title>Delete</title>
-
- <para>
- For back-ends that supports delete of a result set only one handler
- must be defined.
- </para>
-
- <synopsis>
-int (*bend_delete)(void *handle, bend_delete_rr *rr);
-
-typedef struct bend_delete_rr {
- int function;
- int num_setnames;
- char **setnames;
- Z_ReferenceId *referenceId;
- int delete_status; /* status for the whole operation */
- int *statuses; /* status each set - indexed as setnames */
- ODR stream;
- ODR print;
-} bend_delete_rr;
- </synopsis>
-
- <note>
- <para>
- The delete set function definition is rather primitive, mostly because
- we have had no practical need for it as of yet. If someone wants
- to provide a full delete service, we'd be happy to add the
- extra parameters that are required. Are there clients out there
- that will actually delete sets they no longer need?
- </para>
- </note>
-
- </sect2>
-
- <sect2 id="server.scan"><title>Scan</title>
-
- <para>
- For servers that wish to offer the scan service one handler
- must be defined.
- </para>
-
- <synopsis>
-int (*bend_scan)(void *handle, bend_scan_rr *rr);
-
-typedef enum {
- BEND_SCAN_SUCCESS, /* ok */
- BEND_SCAN_PARTIAL /* not all entries could be found */
-} bend_scan_status;
-
-typedef struct bend_scan_rr {
- int num_bases; /* number of elements in databaselist */
- char **basenames; /* databases to search */
- Odr_oid *attributeset;
- Z_ReferenceId *referenceId; /* reference ID */
- Z_AttributesPlusTerm *term;
- ODR stream; /* encoding stream - memory source if required */
- ODR print; /* printing stream */
-
- int *step_size; /* step size */
- int term_position; /* desired index of term in result list/returned */
- int num_entries; /* number of entries requested/returned */
-
- /* scan term entries. The called handler does not have
- to allocate this. Size of entries is num_entries (see above) */
- struct scan_entry *entries;
- bend_scan_status status;
- int errcode;
- char *errstring;
- char *scanClause; /* CQL scan clause */
- char *setname; /* Scan in result set (NULL if omitted) */
-} bend_scan_rr;
- </synopsis>
- <para>
- This backend server handles both Z39.50 scan
- and SRU scan. In order for a handler to distinguish between SRU (CQL) scan
- Z39.50 Scan , it must check for a non-NULL value of
- <literal>scanClause</literal>.
- </para>
- <note>
- <para>
- if designed today, it would be a choice using a union or similar,
- but that would break binary compatibility with existing servers.
- </para>
- </note>
- </sect2>
- </sect1>
-
- <sect1 id="server.invocation"><title>Application Invocation</title>
-
- <para>
- The finished application has the following
- invocation syntax (by way of <function>statserv_main()</function>):
- </para>
-
- &gfs-synopsis;
-
- <para>
- The options are:
-
- &gfs-options;
-
- </para>
-
- <para>
- A listener specification consists of a transport mode followed by a
- colon (:) followed by a listener address. The transport mode is
- either <literal>tcp</literal>, <literal>unix:</literal> or
- <literal>ssl</literal>.
- </para>
-
- <para>
- For TCP and SSL, an address has the form
- </para>
-
- <synopsis>
- hostname | IP-number [: portnumber]
- </synopsis>
-
- <para>
- The port number defaults to 210 (standard Z39.50 port).
- </para>
-
- <para>
- For UNIX, the address is the filename of socket.
- </para>
-
- <para>
- For TCP/IP and SSL, the special hostname <literal>@</literal>
- (at sign) is mapped to the address <literal>INADDR_ANY</literal>,
- which causes the server to listen on any local interface.
- </para>
-
- <example id="server.example.running.unix"><title>Running the GFS on Unix</title>
- <para>
- Assuming the server application <replaceable>appname</replaceable> is
- started as root, the following will make it listen on port 210.
- The server will change identity to <literal>nobody</literal>
- and write its log to <filename>/var/log/app.log</filename>.
- <screen>
- application -l /var/log/app.log -u nobody tcp:@:210
- </screen>
- </para>
- <para>
- The server will accept Z39.50 requests and offer SRU service on port 210.
- </para>
- </example>
- <example id="server.example.apache.sru"><title>Setting up Apache as SRU Frontend</title>
- <para>
- If you use <ulink url="&url.apache;">Apache</ulink>
- as your public web server and want to offer HTTP port 80
- access to the YAZ server on 210, you can use the
- <ulink url="&url.apache.directive.proxypass;">
- <literal>ProxyPass</literal></ulink>
- directive.
- If you have virtual host
- <literal>srw.mydomain</literal> you can use the following directives
- in Apache's httpd.conf:
- <screen>
- <VirtualHost *>
- ErrorLog /home/srw/logs/error_log
- TransferLog /home/srw/logs/access_log
- ProxyPass / http://srw.mydomain:210/
- </VirtualHost>
- </screen>
- </para>
- <para>
- The above for the Apache 1.3 series.
- </para>
- </example>
- <example id="server.example.local.access">
- <title>Running a server with local access only</title>
- <para>
- Servers that is only being accessed from the local host should listen
- on UNIX file socket rather than a Internet socket. To listen on
- <filename>/tmp/mysocket</filename> start the server as follows:
- <screen>
- application unix:/tmp/mysocket
- </screen>
- </para>
- </example>
- </sect1>
- <sect1 id="server.vhosts"><title>GFS Configuration and Virtual Hosts</title>
- &gfs-virtual;
- </sect1>
- </chapter>
-
- <!-- Keep this comment at the end of the file
- Local variables:
- mode: sgml
- sgml-omittag:t
- sgml-shorttag:t
- sgml-minimize-attributes:nil
- sgml-always-quote-attributes:t
- sgml-indent-step:1
- sgml-indent-data:t
- sgml-parent-document: "yaz.xml"
- sgml-local-catalogs: nil
- sgml-namecase-general:t
- End:
- -->
--- /dev/null
+ <chapter id="server"><title>Generic server</title>
+ <sect1 id="server.introduction"><title>Introduction</title>
+
+ <para>
+ If you aren't into documentation, a good way to learn how the
+ back end interface works is to look at the <filename>backend.h</filename>
+ file. Then, look at the small dummy-server in
+ <filename>ztest/ztest.c</filename>. The <filename>backend.h</filename>
+ file also makes a good reference, once you've chewed your way through
+ the prose of this file.
+ </para>
+
+ <para>
+ If you have a database system that you would like to make available by
+ means of Z39.50 or SRU, &yaz; basically offers your two options. You
+ can use the APIs provided by the &asn;, &odr;, and &comstack;
+ modules to
+ create and decode PDUs, and exchange them with a client.
+ Using this low-level interface gives you access to all fields and
+ options of the protocol, and you can construct your server as close
+ to your existing database as you like.
+ It is also a fairly involved process, requiring
+ you to set up an event-handling mechanism, protocol state machine,
+ etc. To simplify server implementation, we have implemented a compact
+ and simple, but reasonably full-functioned server-frontend that will
+ handle most of the protocol mechanics, while leaving you to
+ concentrate on your database interface.
+ </para>
+
+ <note>
+ <para>
+ The backend interface was designed in anticipation of a specific
+ integration task, while still attempting to achieve some degree of
+ generality. We realize fully that there are points where the
+ interface can be improved significantly. If you have specific
+ functions or parameters that you think could be useful, send us a
+ mail (or better, sign on to the mailing list referred to in the
+ top-level README file). We will try to fit good suggestions into future
+ releases, to the extent that it can be done without requiring
+ too many structural changes in existing applications.
+ </para>
+ </note>
+
+ <note>
+ <para>
+ The &yaz; server does not support XCQL.
+ </para>
+ </note>
+ </sect1>
+
+ <sect1 id="server.frontend"><title>The Database Frontend</title>
+
+ <para>
+ We refer to this software as a generic database frontend. Your
+ database system is the <emphasis>backend database</emphasis>, and the
+ interface between the two is called the <emphasis>backend API</emphasis>.
+ The backend API consists of a small number of function handlers and
+ structure definitions. You are required to provide the
+ <function>main()</function> routine for the server (which can be
+ quite simple), as well as a set of handlers to match each of the
+ prototypes.
+ The interface functions that you write can use any mechanism you like
+ to communicate with your database system: You might link the whole
+ thing together with your database application and access it by
+ function calls; you might use IPC to talk to a database server
+ somewhere; or you might link with third-party software that handles
+ the communication for you (like a commercial database client library).
+ At any rate, the handlers will perform the tasks of:
+ </para>
+
+ <itemizedlist>
+
+ <listitem><para>
+ Initialization.
+ </para></listitem>
+
+ <listitem><para>
+ Searching.
+ </para></listitem>
+
+ <listitem><para>
+ Fetching records.
+ </para></listitem>
+
+ <listitem><para>
+ Scanning the database index (optional - if you wish to implement SCAN).
+ </para></listitem>
+
+ <listitem><para>
+ Extended Services (optional).
+ </para></listitem>
+
+ <listitem><para>
+ Result-Set Delete (optional).
+ </para></listitem>
+
+ <listitem><para>
+ Result-Set Sort (optional).
+ </para></listitem>
+
+ <listitem><para>
+ Return Explain for SRU (optional).
+ </para></listitem>
+
+ </itemizedlist>
+
+ <para>
+ (more functions will be added in time to support as much of
+ Z39.50-1995 as possible).
+ </para>
+
+ </sect1>
+ <sect1 id="server.backend"><title>The Backend API</title>
+
+ <para>
+ The header file that you need to use the interface are in the
+ <filename>include/yaz</filename> directory. It's called
+ <filename>backend.h</filename>. It will include other files from
+ the <filename>include/yaz</filename> directory, so you'll
+ probably want to use the -I option of your compiler to tell it
+ where to find the files. When you run
+ <literal>make</literal> in the top-level &yaz; directory,
+ everything you need to create your server is to link with the
+ <filename>lib/libyaz.la</filename> library.
+ </para>
+ </sect1>
+
+ <sect1 id="server.main"><title>Your main() Routine</title>
+
+ <para>
+ As mentioned, your <function>main()</function> routine can be quite brief.
+ If you want to initialize global parameters, or read global configuration
+ tables, this is the place to do it. At the end of the routine, you should
+ call the function
+ </para>
+
+ <synopsis>
+int statserv_main(int argc, char **argv,
+ bend_initresult *(*bend_init)(bend_initrequest *r),
+ void (*bend_close)(void *handle));
+ </synopsis>
+
+ <para>
+ The third and fourth arguments are pointers to handlers. Handler
+ <function>bend_init</function> is called whenever the server receives
+ an Initialize Request, so it serves as a Z39.50 session initializer. The
+ <function>bend_close</function> handler is called when the session is
+ closed.
+ </para>
+
+ <para>
+ <function>statserv_main</function> will establish listening sockets
+ according to the parameters given. When connection requests are received,
+ the event handler will typically <function>fork()</function> and
+ create a sub-process to handle a new connection.
+ Alternatively the server may be setup to create threads for each
+ connection.
+ If you do use global variables and forking, you should be aware, then,
+ that these cannot be shared between associations, unless you explicitly
+ disable forking by command line parameters.
+ </para>
+
+ <para>
+ The server provides a mechanism for controlling some of its behavior
+ without using command-line options. The function
+ </para>
+
+ <synopsis>
+ statserv_options_block *statserv_getcontrol(void);
+ </synopsis>
+
+ <para>
+ will return a pointer to a <literal>struct statserv_options_block</literal>
+ describing the current default settings of the server. The structure
+ contains these elements:
+
+ <variablelist>
+ <varlistentry><term>
+ <literal>int dynamic</literal></term><listitem><para>
+ A boolean value, which determines whether the server
+ will fork on each incoming request (TRUE), or not (FALSE). Default is
+ TRUE. This flag is only read by UNIX-based servers (WIN32 based servers
+ doesn't fork).
+ </para></listitem></varlistentry>
+
+ <varlistentry><term>
+ <literal>int threads</literal></term><listitem><para>
+ A boolean value, which determines whether the server
+ will create a thread on each incoming request (TRUE), or not (FALSE).
+ Default is FALSE. This flag is only read by UNIX-based servers
+ that offer POSIX Threads support.
+ WIN32-based servers always operate in threaded mode.
+ </para></listitem></varlistentry>
+
+ <varlistentry><term>
+ <literal>int inetd</literal></term><listitem><para>
+ A boolean value, which determines whether the server
+ will operates under a UNIX INET daemon (inetd). Default is FALSE.
+ </para></listitem></varlistentry>
+
+ <varlistentry><term>
+ <literal>char logfile[ODR_MAXNAME+1]</literal></term>
+ <listitem><para>File for diagnostic output ("": stderr).
+ </para></listitem></varlistentry>
+
+ <varlistentry><term>
+ <literal>char apdufile[ODR_MAXNAME+1]</literal></term>
+ <listitem><para>
+ Name of file for logging incoming and outgoing APDUs
+ ("": don't log APDUs, "-":
+ <literal>stderr</literal>).
+ </para></listitem></varlistentry>
+
+ <varlistentry><term>
+ <literal>char default_listen[1024]</literal></term>
+ <listitem><para>Same form as the command-line specification of
+ listener address. "": no default listener address.
+ Default is to listen at "tcp:@:9999". You can only
+ specify one default listener address in this fashion.
+ </para></listitem></varlistentry>
+
+ <varlistentry><term>
+ <literal>enum oid_proto default_proto;</literal></term>
+ <listitem><para>Either <literal>PROTO_Z3950</literal> or
+ <literal>PROTO_SR</literal>.
+ Default is <literal>PROTO_Z39_50</literal>.
+ </para></listitem></varlistentry>
+
+ <varlistentry><term>
+ <literal>int idle_timeout;</literal></term>
+ <listitem><para>Maximum session idle-time, in minutes. Zero indicates
+ no (infinite) timeout. Default is 15 minutes.
+ </para></listitem></varlistentry>
+
+ <varlistentry><term>
+ <literal>int maxrecordsize;</literal></term>
+ <listitem><para>Maximum permissible record (message) size. Default
+ is 1Mb. This amount of memory will only be allocated if a
+ client requests a very large amount of records in one operation
+ (or a big record).
+ Set it to a lower number if you are worried about resource
+ consumption on your host system.
+ </para></listitem></varlistentry>
+
+ <varlistentry><term>
+ <literal>char configname[ODR_MAXNAME+1]</literal></term>
+ <listitem><para>Passed to the backend when a new connection is received.
+ </para></listitem></varlistentry>
+
+ <varlistentry><term>
+ <literal>char setuid[ODR_MAXNAME+1]</literal></term>
+ <listitem><para>Set user id to the user specified, after binding
+ the listener addresses.
+ </para></listitem></varlistentry>
+
+ <varlistentry><term>
+ <literal>void (*bend_start)(struct statserv_options_block *p)</literal>
+ </term>
+ <listitem><para>Pointer to function which is called after the
+ command line options have been parsed - but before the server
+ starts listening.
+ For forked UNIX servers this handler is called in the mother
+ process; for threaded servers this handler is called in the
+ main thread.
+ The default value of this pointer is NULL in which case it
+ isn't invoked by the frontend server.
+ When the server operates as an NT service this handler is called
+ whenever the service is started.
+ </para></listitem></varlistentry>
+
+ <varlistentry><term>
+ <literal>void (*bend_stop)(struct statserv_options_block *p)</literal>
+ </term>
+ <listitem><para>Pointer to function which is called whenever the server
+ has stopped listening for incoming connections. This function pointer
+ has a default value of NULL in which case it isn't called.
+ When the server operates as an NT service this handler is called
+ whenever the service is stopped.
+ </para></listitem></varlistentry>
+
+ <varlistentry><term>
+ <literal>void *handle</literal></term>
+ <listitem><para>User defined pointer (default value NULL).
+ This is a per-server handle that can be used to specify "user-data".
+ Do not confuse this with the session-handle as returned by bend_init.
+ </para></listitem></varlistentry>
+
+ </variablelist>
+ </para>
+
+ <para>
+ The pointer returned by <literal>statserv_getcontrol</literal> points to
+ a static area. You are allowed to change the contents of the structure,
+ but the changes will not take effect before you call
+ </para>
+
+ <synopsis>
+void statserv_setcontrol(statserv_options_block *block);
+ </synopsis>
+
+ <note>
+ <para>
+ that you should generally update this structure before calling
+ <function>statserv_main()</function>.
+ </para>
+ </note>
+ </sect1>
+
+ <sect1 id="server.backendfunctions"><title>The Backend Functions</title>
+
+ <para>
+ For each service of the protocol, the backend interface declares one or
+ two functions. You are required to provide implementations of the
+ functions representing the services that you wish to implement.
+ </para>
+
+ <sect2 id="server.init"><title>Init</title>
+
+ <synopsis>
+bend_initresult (*bend_init)(bend_initrequest *r);
+ </synopsis>
+
+ <para>
+ This handler is called once for each new connection request, after
+ a new process/thread has been created, and an Initialize Request has
+ been received from the client. The pointer to the
+ <function>bend_init</function> handler is passed in the call to
+ <function>statserv_start</function>.
+ </para>
+
+ <para>
+ This handler is also called when operating in SRU mode - when
+ a connection has been made (even though SRU does not offer
+ this service).
+ </para>
+
+ <para>
+ Unlike previous versions of YAZ, the <function>bend_init</function> also
+ serves as a handler that defines the Z39.50 services that the backend
+ wish to support. Pointers to <emphasis>all</emphasis> service handlers,
+ including search - and fetch must be specified here in this handler.
+ </para>
+ <para>
+ The request - and result structures are defined as
+ </para>
+
+ <synopsis>
+typedef struct bend_initrequest
+{
+ /** \brief user/name/password to be read */
+ Z_IdAuthentication *auth;
+ /** \brief encoding stream (for results) */
+ ODR stream;
+ /** \brief printing stream */
+ ODR print;
+ /** \brief decoding stream (use stream for results) */
+ ODR decode;
+ /** \brief reference ID */
+ Z_ReferenceId *referenceId;
+ /** \brief peer address of client */
+ char *peer_name;
+
+ /** \brief character set and language negotiation
+
+ see include/yaz/z-charneg.h
+ */
+ Z_CharSetandLanguageNegotiation *charneg_request;
+
+ /** \brief character negotiation response */
+ Z_External *charneg_response;
+
+ /** \brief character set (encoding) for query terms
+
+ This is NULL by default. It should be set to the native character
+ set that the backend assumes for query terms */
+ char *query_charset;
+
+ /** \brief whehter query_charset also applies to recors
+
+ Is 0 (No) by default. Set to 1 (yes) if records is in the same
+ character set as queries. If in doubt, use 0 (No).
+ */
+ int records_in_same_charset;
+
+ char *implementation_id;
+ char *implementation_name;
+ char *implementation_version;
+
+ /** \brief Z39.50 sort handler */
+ int (*bend_sort)(void *handle, bend_sort_rr *rr);
+ /** \brief SRU/Z39.50 search handler */
+ int (*bend_search)(void *handle, bend_search_rr *rr);
+ /** \brief SRU/Z39.50 fetch handler */
+ int (*bend_fetch)(void *handle, bend_fetch_rr *rr);
+ /** \brief SRU/Z39.50 present handler */
+ int (*bend_present)(void *handle, bend_present_rr *rr);
+ /** \brief Z39.50 extended services handler */
+ int (*bend_esrequest) (void *handle, bend_esrequest_rr *rr);
+ /** \brief Z39.50 delete result set handler */
+ int (*bend_delete)(void *handle, bend_delete_rr *rr);
+ /** \brief Z39.50 scan handler */
+ int (*bend_scan)(void *handle, bend_scan_rr *rr);
+ /** \brief Z39.50 segment facility handler */
+ int (*bend_segment)(void *handle, bend_segment_rr *rr);
+ /** \brief SRU explain handler */
+ int (*bend_explain)(void *handle, bend_explain_rr *rr);
+ /** \brief SRU scan handler */
+ int (*bend_srw_scan)(void *handle, bend_scan_rr *rr);
+ /** \brief SRU record update handler */
+ int (*bend_srw_update)(void *handle, bend_update_rr *rr);
+
+ /** \brief whether named result sets are supported (0=disable, 1=enable) */
+ int named_result_sets;
+} bend_initrequest;
+
+typedef struct bend_initresult
+{
+ int errcode; /* 0==OK */
+ char *errstring; /* system error string or NULL */
+ void *handle; /* private handle to the backend module */
+} bend_initresult;
+ </synopsis>
+
+ <para>
+ In general, the server frontend expects that the
+ <literal>bend_*result</literal> pointer that you return is valid at
+ least until the next call to a <literal>bend_* function</literal>.
+ This applies to all of the functions described herein. The parameter
+ structure passed to you in the call belongs to the server frontend, and
+ you should not make assumptions about its contents after the current
+ function call has completed. In other words, if you want to retain any
+ of the contents of a request structure, you should copy them.
+ </para>
+
+ <para>
+ The <literal>errcode</literal> should be zero if the initialization of
+ the backend went well. Any other value will be interpreted as an error.
+ The <literal>errstring</literal> isn't used in the current version, but
+ one option would be to stick it in the initResponse as a VisibleString.
+ The <literal>handle</literal> is the most important parameter. It should
+ be set to some value that uniquely identifies the current session to
+ the backend implementation. It is used by the frontend server in any
+ future calls to a backend function.
+ The typical use is to set it to point to a dynamically allocated state
+ structure that is private to your backend module.
+ </para>
+
+ <para>
+ The <literal>auth</literal> member holds the authentication information
+ part of the Z39.50 Initialize Request. Interpret this if your serves
+ requires authentication.
+ </para>
+
+ <para>
+ The members <literal>peer_name</literal>,
+ <literal>implementation_id</literal>,
+ <literal>implementation_name</literal> and
+ <literal>implementation_version</literal> holds
+ DNS of client, ID of implementor, name
+ of client (Z39.50) implementation - and version.
+ </para>
+
+ <para>
+ The <literal>bend_</literal> - members are set to NULL when
+ <function>bend_init</function> is called. Modify the pointers by
+ setting them to point to backend functions.
+ </para>
+
+ </sect2>
+
+ <sect2 id="server.search.retrieve"><title>Search and Retrieve</title>
+
+ <para>We now describe the handlers that are required to support search -
+ and retrieve. You must support two functions - one for search - and one
+ for fetch (retrieval of one record). If desirable you can provide a
+ third handler which is called when a present request is received which
+ allows you to optimize retrieval of multiple-records.
+ </para>
+
+ <synopsis>
+int (*bend_search) (void *handle, bend_search_rr *rr);
+
+typedef struct {
+ char *setname; /* name to give to this set */
+ int replace_set; /* replace set, if it already exists */
+ int num_bases; /* number of databases in list */
+ char **basenames; /* databases to search */
+ Z_ReferenceId *referenceId;/* reference ID */
+ Z_Query *query; /* query structure */
+ ODR stream; /* encode stream */
+ ODR decode; /* decode stream */
+ ODR print; /* print stream */
+
+ bend_request request;
+ bend_association association;
+ int *fd;
+ int hits; /* number of hits */
+ int errcode; /* 0==OK */
+ char *errstring; /* system error string or NULL */
+ Z_OtherInformation *search_info; /* additional search info */
+ char *srw_sortKeys; /* holds SRU/SRW sortKeys info */
+ char *srw_setname; /* holds SRU/SRW generated resultsetID */
+ int *srw_setnameIdleTime; /* holds SRU/SRW life-time */
+ int estimated_hit_count; /* if hit count is estimated */
+ int partial_resultset; /* if result set is partial */
+} bend_search_rr;
+ </synopsis>
+
+ <para>
+ The <function>bend_search</function> handler is a fairly close
+ approximation of a protocol Z39.50 Search Request - and Response PDUs
+ The <literal>setname</literal> is the resultSetName from the protocol.
+ You are required to establish a mapping between the set name and whatever
+ your backend database likes to use.
+ Similarly, the <literal>replace_set</literal> is a boolean value
+ corresponding to the resultSetIndicator field in the protocol.
+ <literal>num_bases/basenames</literal> is a length of/array of character
+ pointers to the database names provided by the client.
+ The <literal>query</literal> is the full query structure as defined in
+ the protocol ASN.1 specification.
+ It can be either of the possible query types, and it's up to you to
+ determine if you can handle the provided query type.
+ Rather than reproduce the C interface here, we'll refer you to the
+ structure definitions in the file
+ <filename>include/yaz/z-core.h</filename>. If you want to look at the
+ attributeSetId OID of the RPN query, you can either match it against
+ your own internal tables, or you can use the <link linkend="tools.oid">
+ OID tools</link>.
+ </para>
+
+ <para>
+ The structure contains a number of hits, and an
+ <literal>errcode/errstring</literal> pair. If an error occurs
+ during the search, or if you're unhappy with the request, you should
+ set the errcode to a value from the BIB-1 diagnostic set. The value
+ will then be returned to the user in a nonsurrogate diagnostic record
+ in the response. The <literal>errstring</literal>, if provided, will
+ go in the addinfo field. Look at the protocol definition for the
+ defined error codes, and the suggested uses of the addinfo field.
+ </para>
+
+ <para>
+ The <function>bend_search</function> handler is also called when
+ the frontend server receives a SRU SearchRetrieveRequest.
+ For SRU, a CQL query is usually provided by the client.
+ The CQL query is available as part of <literal>Z_Query</literal>
+ structure (note that CQL is now part of Z39.50 via an external).
+ To support CQL in existing implementations that only do Type-1,
+ we refer to the CQL-to-PQF tool described
+ <link linkend="cql.to.pqf">here</link>.
+ </para>
+
+ <para>
+ To maintain backwards compatibility, the frontend server
+ of yaz always assume that error codes are BIB-1 diagnostics.
+ For SRU operation, a Bib-1 diagnostic code is mapped to
+ SRU diagnostic.
+ </para>
+
+ <synopsis>
+int (*bend_fetch) (void *handle, bend_fetch_rr *rr);
+
+typedef struct bend_fetch_rr {
+ char *setname; /* set name */
+ int number; /* record number */
+ Z_ReferenceId *referenceId;/* reference ID */
+ Odr_oid *request_format; /* format, transfer syntax (OID) */
+ Z_RecordComposition *comp; /* Formatting instructions */
+ ODR stream; /* encoding stream - memory source if req */
+ ODR print; /* printing stream */
+
+ char *basename; /* name of database that provided record */
+ int len; /* length of record or -1 if structured */
+ char *record; /* record */
+ int last_in_set; /* is it? */
+ Odr_oid *output_format; /* response format/syntax (OID) */
+ int errcode; /* 0==success */
+ char *errstring; /* system error string or NULL */
+ int surrogate_flag; /* surrogate diagnostic */
+ char *schema; /* string record schema input/output */
+} bend_fetch_rr;
+ </synopsis>
+
+ <para>
+ The frontend server calls the <function>bend_fetch</function> handler
+ when it needs database records to fulfill a Z39.50 Search Request, a
+ Z39.50 Present Request or a SRU SearchRetrieveRequest.
+ The <literal>setname</literal> is simply the name of the result set
+ that holds the reference to the desired record.
+ The <literal>number</literal> is the offset into the set (with 1
+ being the first record in the set). The <literal>format</literal> field
+ is the record format requested by the client (See
+ <xref linkend="tools.oid"/>).
+ A value of NULL for <literal>format</literal> indicates that the
+ client did not request a specific format.
+ The <literal>stream</literal> argument is an &odr; stream which
+ should be used for allocating space for structured data records.
+ The stream will be reset when all records have been assembled, and
+ the response package has been transmitted.
+ For unstructured data, the backend is responsible for maintaining a
+ static or dynamic buffer for the record between calls.
+ </para>
+
+ <para>
+ If a SRU SearchRetrieveRequest is received by the frontend server,
+ the <literal>referenceId</literal> is NULL and the
+ <literal>format</literal> (transfer syntax) is the OID for XML.
+ The schema for SRU is stored in both the
+ <literal>Z_RecordComposition</literal>
+ structure and <literal>schema</literal> (simple string).
+ </para>
+
+ <para>
+ In the structure, the <literal>basename</literal> is the name of the
+ database that holds the
+ record. <literal>len</literal> is the length of the record returned, in
+ bytes, and <literal>record</literal> is a pointer to the record.
+ <literal>last_in_set</literal> should be nonzero only if the record
+ returned is the last one in the given result set.
+ <literal>errcode</literal> and <literal>errstring</literal>, if
+ given, will be interpreted as a global error pertaining to the
+ set, and will be returned in a non-surrogate-diagnostic.
+ If you wish to return the error as a surrogate-diagnostic
+ (local error) you can do this by setting
+ <literal>surrogate_flag</literal> to 1 also.
+ </para>
+
+ <para>
+ If the <literal>len</literal> field has the value -1, then
+ <literal>record</literal> is assumed to point to a constructed data
+ type. The <literal>format</literal> field will be used to determine
+ which encoder should be used to serialize the data.
+ </para>
+
+ <note>
+ <para>
+ If your backend generates structured records, it should use
+ <function>odr_malloc()</function> on the provided stream for allocating
+ data: This allows the frontend server to keep track of the record sizes.
+ </para>
+ </note>
+
+ <para>
+ The <literal>format</literal> field is mapped to an object identifier
+ in the direct reference of the resulting EXTERNAL representation
+ of the record.
+ </para>
+
+ <note>
+ <para>
+ The current version of &yaz; only supports the direct reference mode.
+ </para>
+ </note>
+
+ <synopsis>
+int (*bend_present) (void *handle, bend_present_rr *rr);
+
+typedef struct {
+ char *setname; /* set name */
+ int start;
+ int number; /* record number */
+ Odr_oid *format; /* format, transfer syntax (OID) */
+ Z_ReferenceId *referenceId;/* reference ID */
+ Z_RecordComposition *comp; /* Formatting instructions */
+ ODR stream; /* encoding stream - memory source if required */
+ ODR print; /* printing stream */
+ bend_request request;
+ bend_association association;
+
+ int hits; /* number of hits */
+ int errcode; /* 0==OK */
+ char *errstring; /* system error string or NULL */
+} bend_present_rr;
+ </synopsis>
+
+ <para>
+ The <function>bend_present</function> handler is called when
+ the server receives a Z39.50 Present Request.
+ The <literal>setname</literal>,
+ <literal>start</literal> and <literal>number</literal> is the
+ name of the result set - start position - and number of records to
+ be retrieved respectively. <literal>format</literal> and
+ <literal>comp</literal> is the preferred transfer syntax and element
+ specifications of the present request.
+ </para>
+ <para>
+ Note that this is handler serves as a supplement for
+ <function>bend_fetch</function> and need not to be defined in order to
+ support search - and retrieve.
+ </para>
+
+ </sect2>
+
+ <sect2 id="server.delete"><title>Delete</title>
+
+ <para>
+ For back-ends that supports delete of a result set only one handler
+ must be defined.
+ </para>
+
+ <synopsis>
+int (*bend_delete)(void *handle, bend_delete_rr *rr);
+
+typedef struct bend_delete_rr {
+ int function;
+ int num_setnames;
+ char **setnames;
+ Z_ReferenceId *referenceId;
+ int delete_status; /* status for the whole operation */
+ int *statuses; /* status each set - indexed as setnames */
+ ODR stream;
+ ODR print;
+} bend_delete_rr;
+ </synopsis>
+
+ <note>
+ <para>
+ The delete set function definition is rather primitive, mostly because
+ we have had no practical need for it as of yet. If someone wants
+ to provide a full delete service, we'd be happy to add the
+ extra parameters that are required. Are there clients out there
+ that will actually delete sets they no longer need?
+ </para>
+ </note>
+
+ </sect2>
+
+ <sect2 id="server.scan"><title>Scan</title>
+
+ <para>
+ For servers that wish to offer the scan service one handler
+ must be defined.
+ </para>
+
+ <synopsis>
+int (*bend_scan)(void *handle, bend_scan_rr *rr);
+
+typedef enum {
+ BEND_SCAN_SUCCESS, /* ok */
+ BEND_SCAN_PARTIAL /* not all entries could be found */
+} bend_scan_status;
+
+typedef struct bend_scan_rr {
+ int num_bases; /* number of elements in databaselist */
+ char **basenames; /* databases to search */
+ Odr_oid *attributeset;
+ Z_ReferenceId *referenceId; /* reference ID */
+ Z_AttributesPlusTerm *term;
+ ODR stream; /* encoding stream - memory source if required */
+ ODR print; /* printing stream */
+
+ int *step_size; /* step size */
+ int term_position; /* desired index of term in result list/returned */
+ int num_entries; /* number of entries requested/returned */
+
+ /* scan term entries. The called handler does not have
+ to allocate this. Size of entries is num_entries (see above) */
+ struct scan_entry *entries;
+ bend_scan_status status;
+ int errcode;
+ char *errstring;
+ char *scanClause; /* CQL scan clause */
+ char *setname; /* Scan in result set (NULL if omitted) */
+} bend_scan_rr;
+ </synopsis>
+ <para>
+ This backend server handles both Z39.50 scan
+ and SRU scan. In order for a handler to distinguish between SRU (CQL) scan
+ Z39.50 Scan , it must check for a non-NULL value of
+ <literal>scanClause</literal>.
+ </para>
+ <note>
+ <para>
+ if designed today, it would be a choice using a union or similar,
+ but that would break binary compatibility with existing servers.
+ </para>
+ </note>
+ </sect2>
+ </sect1>
+
+ <sect1 id="server.invocation"><title>Application Invocation</title>
+
+ <para>
+ The finished application has the following
+ invocation syntax (by way of <function>statserv_main()</function>):
+ </para>
+
+ &gfs-synopsis;
+
+ <para>
+ The options are:
+
+ &gfs-options;
+
+ </para>
+
+ <para>
+ A listener specification consists of a transport mode followed by a
+ colon (:) followed by a listener address. The transport mode is
+ either <literal>tcp</literal>, <literal>unix:</literal> or
+ <literal>ssl</literal>.
+ </para>
+
+ <para>
+ For TCP and SSL, an address has the form
+ </para>
+
+ <synopsis>
+ hostname | IP-number [: portnumber]
+ </synopsis>
+
+ <para>
+ The port number defaults to 210 (standard Z39.50 port).
+ </para>
+
+ <para>
+ For UNIX, the address is the filename of socket.
+ </para>
+
+ <para>
+ For TCP/IP and SSL, the special hostname <literal>@</literal>
+ (at sign) is mapped to the address <literal>INADDR_ANY</literal>,
+ which causes the server to listen on any local interface.
+ </para>
+
+ <example id="server.example.running.unix"><title>Running the GFS on Unix</title>
+ <para>
+ Assuming the server application <replaceable>appname</replaceable> is
+ started as root, the following will make it listen on port 210.
+ The server will change identity to <literal>nobody</literal>
+ and write its log to <filename>/var/log/app.log</filename>.
+ <screen>
+ application -l /var/log/app.log -u nobody tcp:@:210
+ </screen>
+ </para>
+ <para>
+ The server will accept Z39.50 requests and offer SRU service on port 210.
+ </para>
+ </example>
+ <example id="server.example.apache.sru"><title>Setting up Apache as SRU Frontend</title>
+ <para>
+ If you use <ulink url="&url.apache;">Apache</ulink>
+ as your public web server and want to offer HTTP port 80
+ access to the YAZ server on 210, you can use the
+ <ulink url="&url.apache.directive.proxypass;">
+ <literal>ProxyPass</literal></ulink>
+ directive.
+ If you have virtual host
+ <literal>srw.mydomain</literal> you can use the following directives
+ in Apache's httpd.conf:
+ <screen>
+ <VirtualHost *>
+ ErrorLog /home/srw/logs/error_log
+ TransferLog /home/srw/logs/access_log
+ ProxyPass / http://srw.mydomain:210/
+ </VirtualHost>
+ </screen>
+ </para>
+ <para>
+ The above for the Apache 1.3 series.
+ </para>
+ </example>
+ <example id="server.example.local.access">
+ <title>Running a server with local access only</title>
+ <para>
+ Servers that is only being accessed from the local host should listen
+ on UNIX file socket rather than a Internet socket. To listen on
+ <filename>/tmp/mysocket</filename> start the server as follows:
+ <screen>
+ application unix:/tmp/mysocket
+ </screen>
+ </para>
+ </example>
+ </sect1>
+ <sect1 id="server.vhosts"><title>GFS Configuration and Virtual Hosts</title>
+ &gfs-virtual;
+ </sect1>
+ </chapter>
+
+ <!-- Keep this comment at the end of the file
+ Local variables:
+ mode: sgml
+ sgml-omittag:t
+ sgml-shorttag:t
+ sgml-minimize-attributes:nil
+ sgml-always-quote-attributes:t
+ sgml-indent-step:1
+ sgml-indent-data:t
+ sgml-parent-document: "yaz.xml"
+ sgml-local-catalogs: nil
+ sgml-namecase-general:t
+ End:
+ -->
&chap-introduction;
&chap-installation;
&chap-zoom;
- &chap-frontend;
+ &chap-server;
&chap-asn;
&chap-soap;
&chap-tools;