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