Character set negotiation updates
[idzebra-moved-to-github.git] / index / zserver.c
1 /*
2  * Copyright (C) 1995-2002, Index Data 
3  * All rights reserved.
4  *
5  * $Id: zserver.c,v 1.89 2002-07-25 13:06:43 adam Exp $
6  */
7
8 #include <stdio.h>
9 #include <assert.h>
10 #include <fcntl.h>
11 #ifdef WIN32
12 #include <io.h>
13 #include <process.h>
14 #else
15 #include <unistd.h>
16 #endif
17
18 #include <yaz/data1.h>
19 #ifdef ASN_COMPILED
20 #include <yaz/ill.h>
21 #endif
22
23 #include "zserver.h"
24
25 static int bend_sort (void *handle, bend_sort_rr *rr);
26 static int bend_delete (void *handle, bend_delete_rr *rr);
27 static int bend_esrequest (void *handle, bend_esrequest_rr *rr);
28 static int bend_segment (void *handle, bend_segment_rr *rr);
29 static int bend_search (void *handle, bend_search_rr *r);
30 static int bend_fetch (void *handle, bend_fetch_rr *r);
31 static int bend_scan (void *handle, bend_scan_rr *r);
32
33 bend_initresult *bend_init (bend_initrequest *q)
34 {
35     bend_initresult *r = (bend_initresult *)
36         odr_malloc (q->stream, sizeof(*r));
37     ZebraHandle zh;
38     struct statserv_options_block *sob;
39     char *user = NULL;
40     char *passwd = NULL;
41
42     r->errcode = 0;
43     r->errstring = 0;
44     q->bend_sort = bend_sort;
45     q->bend_delete = bend_delete;
46     q->bend_esrequest = bend_esrequest;
47     q->bend_segment = bend_segment;
48     q->bend_search = bend_search;
49     q->bend_fetch = bend_fetch;
50     q->bend_scan = bend_scan;
51
52 #if ZMBOL
53     q->implementation_name = "Z'mbol Information Server";
54     q->implementation_version = "Z'mbol " ZEBRAVER;
55 #else
56     q->implementation_name = "Zebra Information Server";
57     q->implementation_version = "Zebra " ZEBRAVER;
58 #endif
59
60     logf (LOG_DEBUG, "bend_init");
61
62     sob = statserv_getcontrol ();
63     if (!(zh = zebra_open (sob->handle)))
64     {
65         yaz_log (LOG_FATAL, "Failed to open config `%s'", sob->configname);
66         r->errcode = 1;
67         return r;
68     }
69     if (q->auth)
70     {
71         if (q->auth->which == Z_IdAuthentication_open)
72         {
73             char *openpass = xstrdup (q->auth->u.open);
74             char *cp = strchr (openpass, '/');
75             if (cp)
76             {
77                 *cp = '\0';
78                 user = nmem_strdup (odr_getmem (q->stream), openpass);
79                 passwd = nmem_strdup (odr_getmem (q->stream), cp+1);
80             }
81             xfree (openpass);
82         }
83     }
84     if (zebra_auth (zh, user, passwd))
85     {
86         r->errcode = 222;
87         r->errstring = user;
88         zebra_close (zh);
89         return r;
90     }
91     r->handle = zh;
92     if (q->charneg_request) /* characater set and langauge negotiation? */
93     {
94         char **charsets = 0;
95         int num_charsets;
96         char **langs = 0;
97         int num_langs = 0;
98         int selected = 0;
99         int i;
100
101         NMEM nmem = nmem_create ();
102         yaz_log (LOG_LOG, "character set and language negotiation");
103
104         yaz_get_proposal_charneg (nmem, q->charneg_request,
105                                   &charsets, &num_charsets,
106                                   &langs, &num_langs, &selected);
107         for (i = 0; i < num_charsets; i++)
108         {
109             yaz_log (LOG_LOG, "charset %d %s", i, charsets[i]);
110             
111             if (odr_set_charset (q->decode, "UTF-8", charsets[i]) == 0)
112             {
113                 odr_set_charset (q->stream, charsets[i], "UTF-8");
114                 if (selected)
115                     zebra_record_encoding (zh, charsets[i]);
116                 q->charneg_response =
117                     yaz_set_response_charneg (q->stream, charsets[i],
118                                               0, selected);
119                 break;
120             }
121         }
122         nmem_destroy (nmem);
123     }
124     return r;
125 }
126
127 static void search_terms (ZebraHandle zh, bend_search_rr *r)
128 {
129     int count;
130     int no_terms;
131     int i;
132     int type;
133     struct Z_External *ext;
134     Z_SearchInfoReport *sr;
135
136     /* get no of terms for result set */
137     no_terms = zebra_resultSetTerms (zh, r->setname, 0, 0, 0, 0, 0);
138     if (!no_terms)
139         return;
140
141     r->search_info = odr_malloc (r->stream, sizeof(*r->search_info));
142
143     r->search_info->num_elements = 1;
144     r->search_info->list =
145         odr_malloc (r->stream, sizeof(*r->search_info->list));
146     r->search_info->list[0] =
147         odr_malloc (r->stream, sizeof(**r->search_info->list));
148     r->search_info->list[0]->category = 0;
149     r->search_info->list[0]->which = Z_OtherInfo_externallyDefinedInfo;
150     ext = odr_malloc (r->stream, sizeof(*ext));
151     r->search_info->list[0]->information.externallyDefinedInfo = ext;
152     ext->direct_reference =
153         yaz_oidval_to_z3950oid (r->stream, CLASS_USERINFO, VAL_SEARCHRES1);
154     ext->indirect_reference = 0;
155     ext->descriptor = 0;
156     ext->which = Z_External_searchResult1;
157     sr = odr_malloc (r->stream, sizeof(Z_SearchInfoReport));
158     ext->u.searchResult1 = sr;
159     sr->num = no_terms;
160     sr->elements = odr_malloc (r->stream, sr->num *
161                                sizeof(*sr->elements));
162     for (i = 0; i<no_terms; i++)
163     {
164         Z_Term *term;
165         char outbuf[1024];
166         size_t len = sizeof(outbuf);
167         zebra_resultSetTerms (zh, r->setname, i,
168                               &count, &type, outbuf, &len);
169         
170         sr->elements[i] = odr_malloc (r->stream, sizeof(**sr->elements));
171         sr->elements[i]->subqueryId = 0;
172         sr->elements[i]->fullQuery = odr_malloc (r->stream, 
173                                                  sizeof(bool_t));
174         *sr->elements[i]->fullQuery = 0;
175         sr->elements[i]->subqueryExpression = 
176             odr_malloc (r->stream, sizeof(Z_QueryExpression));
177         sr->elements[i]->subqueryExpression->which = 
178             Z_QueryExpression_term;
179         sr->elements[i]->subqueryExpression->u.term =
180             odr_malloc (r->stream, sizeof(Z_QueryExpressionTerm));
181         term = odr_malloc (r->stream, sizeof(Z_Term));
182         sr->elements[i]->subqueryExpression->u.term->queryTerm = term;
183         switch (type)
184         {
185         case Z_Term_characterString:
186             yaz_log (LOG_LOG, "term as characterString");
187             term->which = Z_Term_characterString;
188             term->u.characterString = odr_strdup (r->stream, outbuf);
189             break;
190         case Z_Term_general:
191             yaz_log (LOG_LOG, "term as general");
192             term->which = Z_Term_general;
193             term->u.general = odr_malloc (r->stream, sizeof(*term->u.general));
194             term->u.general->size = term->u.general->len = len;
195             term->u.general->buf = odr_malloc (r->stream, len);
196             memcpy (term->u.general->buf, outbuf, len);
197             break;
198         default:
199             term->which = Z_Term_general;
200             term->u.null = odr_nullval();
201         }
202         sr->elements[i]->subqueryExpression->u.term->termComment = 0;
203         sr->elements[i]->subqueryInterpretation = 0;
204         sr->elements[i]->subqueryRecommendation = 0;
205         sr->elements[i]->subqueryCount = odr_intdup (r->stream, count);
206         sr->elements[i]->subqueryWeight = 0;
207         sr->elements[i]->resultsByDB = 0;
208     }
209 }
210
211 int bend_search (void *handle, bend_search_rr *r)
212 {
213     ZebraHandle zh = (ZebraHandle) handle;
214
215     r->hits = 0;
216     r->errcode = 0;
217     r->errstring = NULL;
218     
219     if (zebra_select_databases (zh, r->num_bases,
220                                 (const char **) r->basenames))
221     {
222         zebra_result (zh, &r->errcode, &r->errstring);
223         return 0;
224     }
225     logf (LOG_LOG, "ResultSet '%s'", r->setname);
226     switch (r->query->which)
227     {
228     case Z_Query_type_1: case Z_Query_type_101:
229         zebra_search_rpn (zh, r->decode, r->stream, r->query->u.type_1,
230                           r->setname, &r->hits);
231         zebra_result (zh, &r->errcode, &r->errstring);
232         if (!r->errcode)
233             search_terms (zh, r);
234         break;
235     case Z_Query_type_2:
236         r->errcode = 107;
237         r->errstring = "type-2";
238         break;
239     default:
240         r->errcode = 107;
241     }
242     return 0;
243 }
244
245
246 int bend_fetch (void *handle, bend_fetch_rr *r)
247 {
248     ZebraHandle zh = (ZebraHandle) handle;
249     ZebraRetrievalRecord retrievalRecord;
250
251     retrievalRecord.position = r->number;
252     
253     r->last_in_set = 0;
254     zebra_records_retrieve (zh, r->stream, r->setname, r->comp,
255                             r->request_format, 1, &retrievalRecord);
256     zebra_result (zh, &r->errcode, &r->errstring);
257     /*  non Surrogate Diagnostic OR Surrogate Diagnostic */
258     if (r->errcode == 0 && retrievalRecord.errCode)
259     {
260         r->surrogate_flag = 1;
261         r->errcode = retrievalRecord.errCode;
262         r->errstring = retrievalRecord.errString;
263         r->basename = retrievalRecord.base;
264     }
265     else if (r->errcode == 0)        /* Database Record */
266     {
267         r->errcode = 0;
268         r->basename = retrievalRecord.base;
269         r->record = retrievalRecord.buf;
270         r->len = retrievalRecord.len;
271         r->output_format = retrievalRecord.format;
272     }
273     return 0;
274 }
275
276 static int bend_scan (void *handle, bend_scan_rr *r)
277 {
278     ZebraScanEntry *entries;
279     ZebraHandle zh = (ZebraHandle) handle;
280     int is_partial, i;
281
282     if (zebra_select_databases (zh, r->num_bases, 
283                                 (const char **) r->basenames))
284     {
285         zebra_result (zh, &r->errcode, &r->errstring);
286         return 0;
287     }
288     r->entries = (struct scan_entry *)
289         odr_malloc (r->stream, sizeof(*r->entries) * r->num_entries);
290     zebra_scan (zh, r->stream, r->term,
291                 r->attributeset,
292                 &r->term_position,
293                 &r->num_entries, &entries, &is_partial);
294     if (is_partial)
295         r->status = BEND_SCAN_PARTIAL;
296     else
297         r->status = BEND_SCAN_SUCCESS;
298     for (i = 0; i < r->num_entries; i++)
299     {
300         r->entries[i].term = entries[i].term;
301         r->entries[i].occurrences = entries[i].occurrences;
302     }
303     zebra_result (zh, &r->errcode, &r->errstring);
304     return 0;
305 }
306
307 void bend_close (void *handle)
308 {
309     zebra_close ((ZebraHandle) handle);
310     xmalloc_trav("bend_close");
311     nmem_print_list();
312 }
313
314 int bend_sort (void *handle, bend_sort_rr *rr)
315 {
316     ZebraHandle zh = (ZebraHandle) handle;
317
318     zebra_sort (zh, rr->stream,
319                 rr->num_input_setnames, (const char **) rr->input_setnames,
320                 rr->output_setname, rr->sort_sequence, &rr->sort_status);
321     zebra_result (zh, &rr->errcode, &rr->errstring);
322     return 0;
323 }
324
325 int bend_delete (void *handle, bend_delete_rr *rr)
326 {
327     ZebraHandle zh = (ZebraHandle) handle;
328
329     rr->delete_status = zebra_deleleResultSet(zh, rr->function,
330                                               rr->num_setnames, rr->setnames,
331                                               rr->statuses);
332     return 0;
333 }
334
335 static int es_admin_request (ZebraHandle zh, Z_AdminEsRequest *r)
336 {
337     switch (r->toKeep->which)
338     {
339     case Z_ESAdminOriginPartToKeep_reIndex:
340         yaz_log(LOG_LOG, "adm-reindex");
341         break;
342     case Z_ESAdminOriginPartToKeep_truncate:
343         yaz_log(LOG_LOG, "adm-truncate");
344         break;
345     case Z_ESAdminOriginPartToKeep_drop:
346         yaz_log(LOG_LOG, "adm-drop");
347         break;
348     case Z_ESAdminOriginPartToKeep_create:
349         yaz_log(LOG_LOG, "adm-create");
350         zebra_admin_create (zh, r->toKeep->databaseName);
351         break;
352     case Z_ESAdminOriginPartToKeep_import:
353         yaz_log(LOG_LOG, "adm-import");
354         zebra_admin_import_begin (zh, r->toKeep->databaseName);
355         break;
356     case Z_ESAdminOriginPartToKeep_refresh:
357         yaz_log(LOG_LOG, "adm-refresh");
358         break;
359     case Z_ESAdminOriginPartToKeep_commit:
360         yaz_log(LOG_LOG, "adm-commit");
361         break;
362     case Z_ESAdminOriginPartToKeep_shutdown:
363         yaz_log(LOG_LOG, "shutdown");
364         zebra_admin_shutdown(zh);
365         break;
366     case Z_ESAdminOriginPartToKeep_start:
367         yaz_log(LOG_LOG, "start");
368         zebra_admin_start(zh);
369         break;
370     default:
371         yaz_log(LOG_LOG, "unknown admin");
372     }
373     if (r->toKeep->databaseName)
374     {
375         yaz_log(LOG_LOG, "database %s", r->toKeep->databaseName);
376     }
377     return 0;
378 }
379
380 static int es_admin (ZebraHandle zh, Z_Admin *r)
381 {
382     switch (r->which)
383     {
384     case Z_Admin_esRequest:
385         es_admin_request (zh, r->u.esRequest);
386         break;
387     case Z_Admin_taskPackage:
388         yaz_log (LOG_LOG, "adm taskpackage (unhandled)");
389         break;
390     default:
391         break;
392     }
393
394     return 0;
395 }
396
397 int bend_segment (void *handle, bend_segment_rr *rr)
398 {
399     ZebraHandle zh = (ZebraHandle) handle;
400     Z_Segment *segment = rr->segment;
401
402     if (segment->num_segmentRecords)
403         zebra_admin_import_segment (zh, rr->segment);
404     else
405         zebra_admin_import_end (zh);
406     return 0;
407 }
408
409 int bend_esrequest (void *handle, bend_esrequest_rr *rr)
410 {
411     ZebraHandle zh = (ZebraHandle) handle;
412     
413     yaz_log(LOG_LOG, "function: %d", *rr->esr->function);
414     if (rr->esr->packageName)
415         yaz_log(LOG_LOG, "packagename: %s", rr->esr->packageName);
416     yaz_log(LOG_LOG, "Waitaction: %d", *rr->esr->waitAction);
417
418     if (!rr->esr->taskSpecificParameters)
419     {
420         yaz_log (LOG_WARN, "No task specific parameters");
421     }
422     else if (rr->esr->taskSpecificParameters->which == Z_External_ESAdmin)
423     {
424         es_admin (zh, rr->esr->taskSpecificParameters->u.adminService);
425
426         zebra_result (zh, &rr->errcode, &rr->errstring);
427     }
428     else if (rr->esr->taskSpecificParameters->which == Z_External_itemOrder)
429     {
430         Z_ItemOrder *it = rr->esr->taskSpecificParameters->u.itemOrder;
431         yaz_log (LOG_LOG, "Received ItemOrder");
432         switch (it->which)
433         {
434 #ifdef ASN_COMPILED
435         case Z_IOItemOrder_esRequest:
436 #else
437         case Z_ItemOrder_esRequest:
438 #endif
439         {
440             Z_IORequest *ir = it->u.esRequest;
441             Z_IOOriginPartToKeep *k = ir->toKeep;
442             Z_IOOriginPartNotToKeep *n = ir->notToKeep;
443             
444             if (k && k->contact)
445             {
446                 if (k->contact->name)
447                     yaz_log(LOG_LOG, "contact name %s", k->contact->name);
448                 if (k->contact->phone)
449                     yaz_log(LOG_LOG, "contact phone %s", k->contact->phone);
450                 if (k->contact->email)
451                     yaz_log(LOG_LOG, "contact email %s", k->contact->email);
452             }
453             if (k->addlBilling)
454             {
455                 yaz_log(LOG_LOG, "Billing info (not shown)");
456             }
457             
458             if (n->resultSetItem)
459             {
460                 yaz_log(LOG_LOG, "resultsetItem");
461                 yaz_log(LOG_LOG, "setId: %s", n->resultSetItem->resultSetId);
462                 yaz_log(LOG_LOG, "item: %d", *n->resultSetItem->item);
463             }
464 #ifdef ASN_COMPILED
465             if (n->itemRequest)
466             {
467                 Z_External *r = (Z_External*) n->itemRequest;
468                 ILL_ItemRequest *item_req = 0;
469                 ILL_Request *ill_req = 0;
470                 if (r->direct_reference)
471                 {
472                     oident *ent = oid_getentbyoid(r->direct_reference);
473                     if (ent)
474                         yaz_log(LOG_LOG, "OID %s", ent->desc);
475                     if (ent && ent->value == VAL_ISO_ILL_1)
476                     {
477                         yaz_log (LOG_LOG, "ItemRequest");
478                         if (r->which == ODR_EXTERNAL_single)
479                         {
480                             odr_setbuf(rr->decode,
481                                        r->u.single_ASN1_type->buf,
482                                        r->u.single_ASN1_type->len, 0);
483                             
484                             if (!ill_ItemRequest (rr->decode, &item_req, 0, 0))
485                             {
486                                 yaz_log (LOG_LOG,
487                                     "Couldn't decode ItemRequest %s near %d",
488                                        odr_errmsg(odr_geterror(rr->decode)),
489                                        odr_offset(rr->decode));
490                                 yaz_log(LOG_LOG, "PDU dump:");
491                                 odr_dumpBER(yaz_log_file(),
492                                      r->u.single_ASN1_type->buf,
493                                      r->u.single_ASN1_type->len);
494                             }
495                             if (rr->print)
496                             {
497                                 ill_ItemRequest (rr->print, &item_req, 0,
498                                     "ItemRequest");
499                                 odr_reset (rr->print);
500                             }
501                         }
502                         if (!item_req && r->which == ODR_EXTERNAL_single)
503                         {
504                             yaz_log (LOG_LOG, "ILLRequest");
505                             odr_setbuf(rr->decode,
506                                        r->u.single_ASN1_type->buf,
507                                        r->u.single_ASN1_type->len, 0);
508                             
509                             if (!ill_Request (rr->decode, &ill_req, 0, 0))
510                             {
511                                 yaz_log (LOG_LOG,
512                                     "Couldn't decode ILLRequest %s near %d",
513                                        odr_errmsg(odr_geterror(rr->decode)),
514                                        odr_offset(rr->decode));
515                                 yaz_log(LOG_LOG, "PDU dump:");
516                                 odr_dumpBER(yaz_log_file(),
517                                      r->u.single_ASN1_type->buf,
518                                      r->u.single_ASN1_type->len);
519                             }
520                             if (rr->print)
521                             {
522                                 ill_Request (rr->print, &ill_req, 0,
523                                     "ILLRequest");
524                                 odr_reset (rr->print);
525                             }
526                         }
527                     }
528                 }
529                 if (item_req)
530                 {
531                     yaz_log (LOG_LOG, "ILL protocol version = %d",
532                              *item_req->protocol_version_num);
533                 }
534             }
535 #endif
536         }
537         break;
538         }
539     }
540     else if (rr->esr->taskSpecificParameters->which == Z_External_update)
541     {
542         Z_IUUpdate *up = rr->esr->taskSpecificParameters->u.update;
543         yaz_log (LOG_LOG, "Received DB Update");
544         if (up->which == Z_IUUpdate_esRequest)
545         {
546             Z_IUUpdateEsRequest *esRequest = up->u.esRequest;
547             Z_IUOriginPartToKeep *toKeep = esRequest->toKeep;
548             Z_IUSuppliedRecords *notToKeep = esRequest->notToKeep;
549             
550             yaz_log (LOG_LOG, "action");
551             if (toKeep->action)
552             {
553                 switch (*toKeep->action)
554                 {
555                 case Z_IUOriginPartToKeep_recordInsert:
556                     yaz_log (LOG_LOG, "recordInsert");
557                     break;
558                 case Z_IUOriginPartToKeep_recordReplace:
559                     yaz_log (LOG_LOG, "recordUpdate");
560                     break;
561                 case Z_IUOriginPartToKeep_recordDelete:
562                     yaz_log (LOG_LOG, "recordDelete");
563                     break;
564                 case Z_IUOriginPartToKeep_elementUpdate:
565                     yaz_log (LOG_LOG, "elementUpdate");
566                     break;
567                 case Z_IUOriginPartToKeep_specialUpdate:
568                     yaz_log (LOG_LOG, "specialUpdate");
569                     break;
570                 case Z_ESAdminOriginPartToKeep_shutdown:
571                     yaz_log (LOG_LOG, "shutDown");
572                     break;
573                 case Z_ESAdminOriginPartToKeep_start:
574                     yaz_log (LOG_LOG, "start");
575                     break;
576                 default:
577                     yaz_log (LOG_LOG, " unknown (%d)", *toKeep->action);
578                 }
579             }
580             if (toKeep->databaseName)
581             {
582                 yaz_log (LOG_LOG, "database: %s", toKeep->databaseName);
583                 if (!strcmp(toKeep->databaseName, "fault"))
584                 {
585                     rr->errcode = 109;
586                     rr->errstring = toKeep->databaseName;
587                 }
588                 if (!strcmp(toKeep->databaseName, "accept"))
589                     rr->errcode = -1;
590             }
591             if (notToKeep)
592             {
593                 int i;
594                 for (i = 0; i < notToKeep->num; i++)
595                 {
596                     Z_External *rec = notToKeep->elements[i]->record;
597
598                     if (rec->direct_reference)
599                     {
600                         struct oident *oident;
601                         oident = oid_getentbyoid(rec->direct_reference);
602                         if (oident)
603                             yaz_log (LOG_LOG, "record %d type %s", i,
604                                      oident->desc);
605                     }
606                     switch (rec->which)
607                     {
608                     case Z_External_sutrs:
609                         if (rec->u.octet_aligned->len > 170)
610                             yaz_log (LOG_LOG, "%d bytes:\n%.168s ...",
611                                      rec->u.sutrs->len,
612                                      rec->u.sutrs->buf);
613                         else
614                             yaz_log (LOG_LOG, "%d bytes:\n%s",
615                                      rec->u.sutrs->len,
616                                      rec->u.sutrs->buf);
617                         break;
618                     case Z_External_octet:
619                         if (rec->u.octet_aligned->len > 170)
620                             yaz_log (LOG_LOG, "%d bytes:\n%.168s ...",
621                                      rec->u.octet_aligned->len,
622                                      rec->u.octet_aligned->buf);
623                         else
624                             yaz_log (LOG_LOG, "%d bytes\n%s",
625                                      rec->u.octet_aligned->len,
626                                      rec->u.octet_aligned->buf);
627                     }
628                 }
629             }
630         }
631     }
632     else
633     {
634         yaz_log (LOG_WARN, "Unknown Extended Service(%d)",
635                  rr->esr->taskSpecificParameters->which);
636         
637     }
638     return 0;
639 }
640
641 static void bend_start (struct statserv_options_block *sob)
642 {
643 #ifdef WIN32
644     
645 #else
646     if (!sob->inetd) 
647     {
648         char *pidfile = "zebrasrv.pid";
649         int fd = creat (pidfile, 0666);
650         if (fd == -1)
651             logf (LOG_WARN|LOG_ERRNO, "creat %s", pidfile);
652         else
653         {
654             char pidstr[30];
655         
656             sprintf (pidstr, "%ld", (long) getpid ());
657             write (fd, pidstr, strlen(pidstr));
658             close (fd);
659         }
660     }
661 #endif
662     if (sob->handle)
663         zebra_stop((ZebraService) sob->handle);
664     sob->handle = zebra_start(sob->configname);
665 }
666
667 static void bend_stop(struct statserv_options_block *sob)
668 {
669 #ifdef WIN32
670
671 #else
672     if (!sob->inetd) 
673         unlink ("zebrasrv.pid");
674 #endif
675     if (sob->handle)
676     {
677         ZebraService service = sob->handle;
678         zebra_stop(service);
679     }
680 }
681
682 int main (int argc, char **argv)
683 {
684     struct statserv_options_block *sob;
685
686     sob = statserv_getcontrol ();
687     strcpy (sob->configname, "zebra.cfg");
688     sob->bend_start = bend_start;
689     sob->bend_stop = bend_stop;
690
691     statserv_setcontrol (sob);
692
693     return statserv_main (argc, argv, bend_init, bend_close);
694 }