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