Resolve host for database just before search
[pazpar2-moved-to-github.git] / src / pazpar2_config.c
1 /* This file is part of Pazpar2.
2    Copyright (C) 2006-2011 Index Data
3
4 Pazpar2 is free software; you can redistribute it and/or modify it under
5 the terms of the GNU General Public License as published by the Free
6 Software Foundation; either version 2, or (at your option) any later
7 version.
8
9 Pazpar2 is distributed in the hope that it will be useful, but WITHOUT ANY
10 WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17
18 */
19
20 #if HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23
24 #include <string.h>
25 #include <assert.h>
26
27 #include <libxml/parser.h>
28 #include <libxml/tree.h>
29
30 #include <yaz/yaz-util.h>
31 #include <yaz/nmem.h>
32 #include <yaz/snprintf.h>
33 #include <yaz/tpath.h>
34 #include <yaz/xml_include.h>
35
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #if HAVE_UNISTD_H
39 #include <unistd.h>
40 #endif
41 #include "ppmutex.h"
42 #include "incref.h"
43 #include "pazpar2_config.h"
44 #include "settings.h"
45 #include "eventl.h"
46 #include "http.h"
47
48 struct conf_config
49 {
50     NMEM nmem; /* for conf_config and servers memory */
51     struct conf_server *servers;
52     
53     int no_threads;
54     WRBUF confdir;
55     iochan_man_t iochan_man;
56     database_hosts_t database_hosts;
57 };
58
59 static void conf_metadata_assign(NMEM nmem, 
60                                  struct conf_metadata * metadata,
61                                  const char *name,
62                                  enum conf_metadata_type type,
63                                  enum conf_metadata_merge merge,
64                                  enum conf_setting_type setting,
65                                  int brief,
66                                  int termlist,
67                                  int rank,
68                                  int sortkey_offset,
69                                  enum conf_metadata_mergekey mt,
70                                  const char *facetrule)
71 {
72     assert(nmem && metadata && name);
73     
74     metadata->name = nmem_strdup(nmem, name);
75
76     metadata->type = type;
77
78     // enforcing that type_year is always range_merge
79     if (metadata->type == Metadata_type_year)
80         metadata->merge = Metadata_merge_range;
81     else
82         metadata->merge = merge;    
83
84     metadata->setting = setting;
85     metadata->brief = brief;   
86     metadata->termlist = termlist;
87     metadata->rank = rank;    
88     metadata->sortkey_offset = sortkey_offset;
89     metadata->mergekey = mt;
90     metadata->facetrule = nmem_strdup_null(nmem, facetrule);
91 }
92
93
94 static void conf_sortkey_assign(NMEM nmem, 
95                                 struct conf_sortkey * sortkey,
96                                 const char *name,
97                                 enum conf_sortkey_type type)
98 {
99     assert(nmem && sortkey && name);
100     
101     sortkey->name = nmem_strdup(nmem, name);
102     sortkey->type = type;
103 }
104
105 static struct conf_service *service_init(struct conf_server *server,
106                                          int num_metadata, int num_sortkeys,
107                                          const char *service_id)
108 {
109     struct conf_service * service = 0;
110     NMEM nmem = nmem_create();
111
112     service = nmem_malloc(nmem, sizeof(struct conf_service));
113     service->mutex = 0;
114     service->ref_count = 1;
115     service->nmem = nmem;
116     service->next = 0;
117     service->databases = 0;
118     service->server = server;
119     service->session_timeout = 60; /* default session timeout */
120     service->z3950_session_timeout = 180;
121     service->z3950_operation_timeout = 30;
122
123     service->charsets = 0;
124
125     service->id = service_id ? nmem_strdup(nmem, service_id) : 0;
126     service->num_metadata = num_metadata;
127     service->metadata = 0;
128     if (service->num_metadata)
129         service->metadata 
130             = nmem_malloc(nmem, 
131                           sizeof(struct conf_metadata) * service->num_metadata);
132     service->num_sortkeys = num_sortkeys;
133     service->sortkeys = 0;
134     if (service->num_sortkeys)
135         service->sortkeys 
136             = nmem_malloc(nmem, 
137                           sizeof(struct conf_sortkey) * service->num_sortkeys);
138     service->dictionary = 0;
139     return service; 
140 }
141
142 static struct conf_metadata* conf_service_add_metadata(
143     struct conf_service *service,
144     int field_id,
145     const char *name,
146     enum conf_metadata_type type,
147     enum conf_metadata_merge merge,
148     enum conf_setting_type setting,
149     int brief,
150     int termlist,
151     int rank,
152     int sortkey_offset,
153     enum conf_metadata_mergekey mt,
154     const char *facetrule)
155 {
156     struct conf_metadata * md = 0;
157
158     if (!service || !service->metadata || !service->num_metadata
159         || field_id < 0  || !(field_id < service->num_metadata))
160         return 0;
161
162     md = service->metadata + field_id;
163     conf_metadata_assign(service->nmem, md, name, type, merge, setting,
164                          brief, termlist, rank, sortkey_offset,
165                          mt, facetrule);
166     return md;
167 }
168
169 static struct conf_sortkey * conf_service_add_sortkey(
170     struct conf_service *service,
171     int field_id,
172     const char *name,
173     enum conf_sortkey_type type)
174 {
175     struct conf_sortkey * sk = 0;
176
177     if (!service || !service->sortkeys || !service->num_sortkeys
178         || field_id < 0 || !(field_id < service->num_sortkeys))
179         return 0;
180
181     //sk = &((service->sortkeys)[field_id]);
182     sk = service->sortkeys + field_id;
183     conf_sortkey_assign(service->nmem, sk, name, type);
184
185     return sk;
186 }
187
188 int conf_service_metadata_field_id(struct conf_service *service,
189                                    const char * name)
190 {
191     int i = 0;
192
193     if (!service || !service->metadata || !service->num_metadata)
194         return -1;
195     
196     for (i = 0; i < service->num_metadata; i++)
197         if (!strcmp(name, (service->metadata[i]).name))
198             return i;
199     return -1;
200 }
201
202 int conf_service_sortkey_field_id(struct conf_service *service,
203                                   const char * name)
204 {
205     int i = 0;
206
207     if (!service || !service->sortkeys || !service->num_sortkeys)
208         return -1;
209
210     for (i = 0; i < service->num_sortkeys; i++)
211         if (!strcmp(name, (service->sortkeys[i]).name))
212             return i;
213     return -1;
214 }
215
216 static void conf_dir_path(struct conf_config *config, WRBUF w, const char *src)
217 {
218     if (config->confdir && wrbuf_len(config->confdir) > 0 &&
219         !yaz_is_abspath(src))
220     {
221         wrbuf_printf(w, "%s/%s", wrbuf_cstr(config->confdir), src);
222     }
223     else
224         wrbuf_puts(w, src);
225 }
226
227 void service_destroy(struct conf_service *service)
228 {
229     if (service)
230     {
231         if (!pazpar2_decref(&service->ref_count, service->mutex))
232         {
233             pp2_charset_fact_destroy(service->charsets);
234             yaz_mutex_destroy(&service->mutex);
235             nmem_destroy(service->nmem);
236         }
237     }
238 }
239
240 void service_incref(struct conf_service *service)
241 {
242     yaz_log(YLOG_LOG, "service_incref. p=%p cnt=%d", service,
243             service->ref_count);
244     pazpar2_incref(&service->ref_count, service->mutex);
245 }
246
247 static int parse_metadata(struct conf_service *service, xmlNode *n,
248                           int *md_node, int *sk_node)
249 {
250     enum conf_metadata_type type = Metadata_type_generic;
251     enum conf_metadata_merge merge = Metadata_merge_no;
252     enum conf_setting_type setting = Metadata_setting_no;
253     enum conf_metadata_mergekey mergekey_type = Metadata_mergekey_no;
254     int brief = 0;
255     int termlist = 0;
256     int rank = 0;
257     int sortkey_offset = 0;
258     xmlChar *xml_name = 0;
259     xmlChar *xml_brief = 0;
260     xmlChar *xml_sortkey = 0;
261     xmlChar *xml_merge = 0;
262     xmlChar *xml_type = 0;
263     xmlChar *xml_termlist = 0;
264     xmlChar *xml_rank = 0;
265     xmlChar *xml_setting = 0;
266     xmlChar *xml_mergekey = 0;
267     xmlChar *xml_icu_chain = 0;
268     struct _xmlAttr *attr;
269     for (attr = n->properties; attr; attr = attr->next)
270     {
271         if (!xmlStrcmp(attr->name, BAD_CAST "name") &&
272             attr->children && attr->children->type == XML_TEXT_NODE)
273             xml_name = attr->children->content;
274         else if (!xmlStrcmp(attr->name, BAD_CAST "brief") &&
275                  attr->children && attr->children->type == XML_TEXT_NODE)
276             xml_brief = attr->children->content;
277         else if (!xmlStrcmp(attr->name, BAD_CAST "sortkey") &&
278                  attr->children && attr->children->type == XML_TEXT_NODE)
279             xml_sortkey = attr->children->content;
280         else if (!xmlStrcmp(attr->name, BAD_CAST "merge") &&
281                  attr->children && attr->children->type == XML_TEXT_NODE)
282             xml_merge = attr->children->content;
283         else if (!xmlStrcmp(attr->name, BAD_CAST "type") &&
284                  attr->children && attr->children->type == XML_TEXT_NODE)
285             xml_type = attr->children->content;
286         else if (!xmlStrcmp(attr->name, BAD_CAST "termlist") &&
287                  attr->children && attr->children->type == XML_TEXT_NODE)
288             xml_termlist = attr->children->content;
289         else if (!xmlStrcmp(attr->name, BAD_CAST "rank") &&
290                  attr->children && attr->children->type == XML_TEXT_NODE)
291             xml_rank = attr->children->content;
292         else if (!xmlStrcmp(attr->name, BAD_CAST "setting") &&
293                  attr->children && attr->children->type == XML_TEXT_NODE)
294             xml_setting = attr->children->content;
295         else if (!xmlStrcmp(attr->name, BAD_CAST "mergekey") &&
296                  attr->children && attr->children->type == XML_TEXT_NODE)
297             xml_mergekey = attr->children->content;
298         else if (!xmlStrcmp(attr->name, BAD_CAST "facetrule") &&
299                  attr->children && attr->children->type == XML_TEXT_NODE)
300             xml_icu_chain = attr->children->content;
301         else
302         {
303             yaz_log(YLOG_FATAL, "Unknown metadata attribute '%s'", attr->name);
304             return -1;
305         }
306     }
307     
308     // now do the parsing logic
309     if (!xml_name)
310     {
311         yaz_log(YLOG_FATAL, "Must specify name in metadata element");
312         return -1;
313     }
314     if (xml_brief)
315     {
316         if (!strcmp((const char *) xml_brief, "yes"))
317             brief = 1;
318         else if (strcmp((const char *) xml_brief, "no"))
319         {
320             yaz_log(YLOG_FATAL, "metadata/brief must be yes or no");
321             return -1;
322         }
323     }
324     
325     if (xml_termlist)
326     {
327         if (!strcmp((const char *) xml_termlist, "yes"))
328             termlist = 1;
329         else if (strcmp((const char *) xml_termlist, "no"))
330         {
331             yaz_log(YLOG_FATAL, "metadata/termlist must be yes or no");
332             return -1;
333         }
334     }
335     
336     if (xml_rank)
337         rank = atoi((const char *) xml_rank);
338
339     if (xml_type)
340     {
341         if (!strcmp((const char *) xml_type, "generic"))
342             type = Metadata_type_generic;
343         else if (!strcmp((const char *) xml_type, "year"))
344             type = Metadata_type_year;
345         else if (!strcmp((const char *) xml_type, "date"))
346             type = Metadata_type_date;
347         else
348         {
349             yaz_log(YLOG_FATAL, 
350                     "Unknown value for metadata/type: %s", xml_type);
351             return -1;
352         }
353     }
354     
355     if (xml_merge)
356     {
357         if (!strcmp((const char *) xml_merge, "no"))
358             merge = Metadata_merge_no;
359         else if (!strcmp((const char *) xml_merge, "unique"))
360             merge = Metadata_merge_unique;
361         else if (!strcmp((const char *) xml_merge, "longest"))
362             merge = Metadata_merge_longest;
363         else if (!strcmp((const char *) xml_merge, "range"))
364             merge = Metadata_merge_range;
365         else if (!strcmp((const char *) xml_merge, "all"))
366             merge = Metadata_merge_all;
367         else
368         {
369             yaz_log(YLOG_FATAL, 
370                     "Unknown value for metadata/merge: %s", xml_merge);
371             return -1;
372         }
373     }
374     
375     if (xml_setting)
376     {
377         if (!strcmp((const char *) xml_setting, "no"))
378             setting = Metadata_setting_no;
379         else if (!strcmp((const char *) xml_setting, "postproc"))
380             setting = Metadata_setting_postproc;
381         else if (!strcmp((const char *) xml_setting, "parameter"))
382             setting = Metadata_setting_parameter;
383         else
384         {
385             yaz_log(YLOG_FATAL,
386                     "Unknown value for medadata/setting: %s", xml_setting);
387             return -1;
388         }
389     }
390     
391     // add a sortkey if so specified
392     if (xml_sortkey && strcmp((const char *) xml_sortkey, "no"))
393     {
394         enum conf_sortkey_type sk_type;
395         if (merge == Metadata_merge_no)
396         {
397             yaz_log(YLOG_FATAL, 
398                     "Can't specify sortkey on a non-merged field");
399             return -1;
400         }
401         if (!strcmp((const char *) xml_sortkey, "numeric"))
402             sk_type = Metadata_sortkey_numeric;
403         else if (!strcmp((const char *) xml_sortkey, "skiparticle"))
404             sk_type = Metadata_sortkey_skiparticle;
405         else
406         {
407             yaz_log(YLOG_FATAL,
408                     "Unknown sortkey in metadata element: %s", 
409                     xml_sortkey);
410             return -1;
411         }
412         sortkey_offset = *sk_node;
413         
414         conf_service_add_sortkey(service, *sk_node,
415                                  (const char *) xml_name, sk_type);        
416         (*sk_node)++;
417     }
418     else
419         sortkey_offset = -1;
420     
421     if (xml_mergekey)
422     {
423         if (!strcmp((const char *) xml_mergekey, "required"))
424             mergekey_type = Metadata_mergekey_required;
425         else if (!strcmp((const char *) xml_mergekey, "optional"))
426             mergekey_type = Metadata_mergekey_optional;
427         else if (!strcmp((const char *) xml_mergekey, "no"))
428             mergekey_type = Metadata_mergekey_no;
429         else
430         {
431             yaz_log(YLOG_FATAL, "Unknown value for mergekey: %s", xml_mergekey);
432             return -1;
433         }
434     }
435
436     // metadata known, assign values
437     conf_service_add_metadata(service, *md_node,
438                               (const char *) xml_name,
439                               type, merge, setting,
440                               brief, termlist, rank, sortkey_offset,
441                               mergekey_type, (const char *) xml_icu_chain);
442     (*md_node)++;
443     return 0;
444 }
445
446 static struct conf_service *service_create_static(struct conf_server *server,
447                                                   xmlNode *node,
448                                                   const char *service_id)
449 {
450     xmlNode *n;
451     int md_node = 0;
452     int sk_node = 0;
453
454     struct conf_service *service = 0;
455     int num_metadata = 0;
456     int num_sortkeys = 0;
457     int got_settings = 0;
458     
459     // count num_metadata and num_sortkeys
460     for (n = node->children; n; n = n->next)
461         if (n->type == XML_ELEMENT_NODE && !strcmp((const char *)
462                                                    n->name, "metadata"))
463         {
464             xmlChar *sortkey = xmlGetProp(n, (xmlChar *) "sortkey");
465             num_metadata++;
466             if (sortkey && strcmp((const char *) sortkey, "no"))
467                 num_sortkeys++;
468             xmlFree(sortkey);
469         }
470
471     service = service_init(server, num_metadata, num_sortkeys, service_id);
472
473     for (n = node->children; n; n = n->next)
474     {
475         if (n->type != XML_ELEMENT_NODE)
476             continue;
477         if (!strcmp((const char *) n->name, "timeout"))
478         {
479             xmlChar *src = xmlGetProp(n, (xmlChar *) "session");
480             if (src)
481             {
482                 service->session_timeout = atoi((const char *) src);
483                 xmlFree(src);
484                 if (service->session_timeout < 9)
485                 {
486                     yaz_log(YLOG_FATAL, "session timeout out of range");
487                     return 0;
488                 }
489             }
490             src = xmlGetProp(n, (xmlChar *) "z3950_operation");
491             if (src)
492             {
493                 service->z3950_operation_timeout = atoi((const char *) src);
494                 xmlFree(src);
495                 if (service->z3950_session_timeout < 9)
496                 {
497                     yaz_log(YLOG_FATAL, "Z39.50 operation timeout out of range");
498                     return 0;
499                 }
500             }
501             src = xmlGetProp(n, (xmlChar *) "z3950_session");
502             if (src)
503             {
504                 service->z3950_session_timeout = atoi((const char *) src);
505                 xmlFree(src);
506                 if (service->z3950_session_timeout < 9)
507                 {
508                     yaz_log(YLOG_FATAL, "Z39.50 session timeout out of range");
509                     return 0;
510                 }
511             }
512         }
513         else if (!strcmp((const char *) n->name, "settings"))
514             got_settings++;
515         else if (!strcmp((const char *) n->name, "icu_chain"))
516         {
517             if (!service->charsets)
518                 service->charsets = pp2_charset_fact_create();
519             if (pp2_charset_fact_define(service->charsets, n, 0))
520             {
521                 yaz_log(YLOG_FATAL, "ICU chain definition error");
522                 return 0;
523             }
524         }
525         else if (!strcmp((const char *) n->name, "relevance")
526                  || !strcmp((const char *) n->name, "sort")
527                  || !strcmp((const char *) n->name, "mergekey")
528                  || !strcmp((const char *) n->name, "facet"))
529
530         {
531             if (!service->charsets)
532                 service->charsets = pp2_charset_fact_create();
533             if (pp2_charset_fact_define(service->charsets,
534                                         n->children, (const char *) n->name))
535             {
536                 yaz_log(YLOG_FATAL, "ICU chain definition error");
537                 return 0;
538             }
539         }
540         else if (!strcmp((const char *) n->name, (const char *) "metadata"))
541         {
542             if (parse_metadata(service, n, &md_node, &sk_node))
543                 return 0;
544         }
545         else
546         {
547             yaz_log(YLOG_FATAL, "Bad element: %s", n->name);
548             return 0;
549         }
550     }
551     if (got_settings)
552     {
553         int pass;
554         /* metadata has been read.. Consider now settings */
555         init_settings(service);
556         for (pass = 1; pass <= 2; pass++)
557         {
558             for (n = node->children; n; n = n->next)
559             {
560                 if (n->type != XML_ELEMENT_NODE)
561                     continue;
562                 if (!strcmp((const char *) n->name, "settings"))
563                 {
564                     xmlChar *src = xmlGetProp(n, (xmlChar *) "src");
565                     if (src)
566                     {
567                         WRBUF w = wrbuf_alloc();
568                         conf_dir_path(server->config, w, (const char *) src);
569                         settings_read_file(service, wrbuf_cstr(w), pass);
570                         wrbuf_destroy(w);
571                         xmlFree(src);
572                     }
573                     else
574                     {
575                         settings_read_node(service, n, pass);
576                     }
577                 }
578             }
579         }
580     }
581     return service;
582 }
583
584 static void inherit_server_settings(struct conf_service *s)
585 {
586     struct conf_server *server = s->server;
587     if (!s->dictionary) /* service has no config settings ? */
588     {
589         if (server->settings_fname)
590         {
591             /* inherit settings from server */
592             init_settings(s);
593             settings_read_file(s, server->settings_fname, 1);
594             settings_read_file(s, server->settings_fname, 2);
595         }
596         else
597         {
598             yaz_log(YLOG_WARN, "service '%s' has no settings",
599                     s->id ? s->id : "unnamed");
600             init_settings(s);
601         }
602     }
603     
604     /* use relevance/sort/mergekey/facet from server if not defined
605        for this service.. */
606     if (!s->charsets)
607     {
608         if (server->charsets)
609         {
610             s->charsets = server->charsets;
611             pp2_charset_fact_incref(s->charsets);
612         }
613         else
614         {
615             s->charsets = pp2_charset_fact_create();
616         }
617     }
618 }
619
620 struct conf_service *service_create(struct conf_server *server,
621                                     xmlNode *node)
622 {
623     struct conf_service *service = service_create_static(server,
624                                                          node, 0);
625     if (service)
626     {
627         inherit_server_settings(service);
628         assert(service->mutex == 0);
629         pazpar2_mutex_create(&service->mutex, "conf");
630     }
631     return service;
632 }
633
634 static struct conf_server *server_create(struct conf_config *config,
635                                          NMEM nmem, xmlNode *node)
636 {
637     xmlNode *n;
638     struct conf_server *server = nmem_malloc(nmem, sizeof(struct conf_server));
639     xmlChar *server_id = xmlGetProp(node, (xmlChar *) "id");
640
641     server->host = 0;
642     server->port = 0;
643     server->proxy_host = 0;
644     server->proxy_port = 0;
645     server->myurl = 0;
646     server->service = 0;
647     server->config = config;
648     server->next = 0;
649     server->charsets = 0;
650     server->http_server = 0;
651     server->iochan_man = 0;
652     server->database_hosts = config->database_hosts;
653     server->settings_fname = 0;
654
655     if (server_id)
656     {
657         server->server_id = nmem_strdup(nmem, (const char *)server_id);
658         xmlFree(server_id);
659     }
660     else
661         server->server_id = 0;
662     for (n = node->children; n; n = n->next)
663     {
664         if (n->type != XML_ELEMENT_NODE)
665             continue;
666         if (!strcmp((const char *) n->name, "listen"))
667         {
668             xmlChar *port = xmlGetProp(n, (xmlChar *) "port");
669             xmlChar *host = xmlGetProp(n, (xmlChar *) "host");
670             if (port)
671                 server->port = atoi((const char *) port);
672             if (host)
673                 server->host = nmem_strdup(nmem, (const char *) host);
674             xmlFree(port);
675             xmlFree(host);
676         }
677         else if (!strcmp((const char *) n->name, "proxy"))
678         {
679             xmlChar *port = xmlGetProp(n, (xmlChar *) "port");
680             xmlChar *host = xmlGetProp(n, (xmlChar *) "host");
681             xmlChar *myurl = xmlGetProp(n, (xmlChar *) "myurl");
682             if (port)
683                 server->proxy_port = atoi((const char *) port);
684             if (host)
685                 server->proxy_host = nmem_strdup(nmem, (const char *) host);
686             if (myurl)
687                 server->myurl = nmem_strdup(nmem, (const char *) myurl);
688             xmlFree(port);
689             xmlFree(host);
690             xmlFree(myurl);
691         }
692         else if (!strcmp((const char *) n->name, "settings"))
693         {
694             xmlChar *src = xmlGetProp(n, (xmlChar *) "src");
695             WRBUF w;
696             if (!src)
697             {
698                 yaz_log(YLOG_FATAL, "Missing src attribute for settings");
699                 return 0;
700             }
701             if (server->settings_fname)
702             {
703                 xmlFree(src);
704                 yaz_log(YLOG_FATAL, "Can't repeat 'settings'");
705                 return 0;
706             }
707             w = wrbuf_alloc();
708             conf_dir_path(config, w, (const char *) src);
709             server->settings_fname = nmem_strdup(nmem, wrbuf_cstr(w));
710             wrbuf_destroy(w);
711             xmlFree(src);
712         }
713         else if (!strcmp((const char *) n->name, "icu_chain"))
714         {
715             if (!server->charsets)
716                 server->charsets = pp2_charset_fact_create();
717             if (pp2_charset_fact_define(server->charsets, n, 0))
718             {
719                 yaz_log(YLOG_FATAL, "ICU chain definition error");
720                 return 0;
721             }
722         }
723         else if (!strcmp((const char *) n->name, "relevance")
724                  || !strcmp((const char *) n->name, "sort")
725                  || !strcmp((const char *) n->name, "mergekey")
726                  || !strcmp((const char *) n->name, "facet"))
727         {
728             if (!server->charsets)
729                 server->charsets = pp2_charset_fact_create();
730             if (pp2_charset_fact_define(server->charsets,
731                                         n->children, (const char *) n->name))
732             {
733                 yaz_log(YLOG_FATAL, "ICU chain definition error");
734                 return 0;
735             }            
736         }            
737         else if (!strcmp((const char *) n->name, "service"))
738         {
739             char *service_id = (char *)
740                 xmlGetProp(n, (xmlChar *) "id");
741
742             struct conf_service **sp = &server->service;
743             for (; *sp; sp = &(*sp)->next)
744                 if ((*sp)->id && service_id &&
745                     0 == strcmp((*sp)->id, service_id))
746                 {
747                     yaz_log(YLOG_FATAL, "Duplicate service: %s", service_id);
748                     break;
749                 }
750                 else if (!(*sp)->id && !service_id)
751                 {
752                     yaz_log(YLOG_FATAL, "Duplicate unnamed service");
753                     break;
754                 }
755
756             if (*sp)  /* service already exist */
757             {
758                 xmlFree(service_id);
759                 return 0;
760             }
761             else
762             {
763                 struct conf_service *s = service_create_static(server, n,
764                                                                service_id);
765                 xmlFree(service_id);
766                 if (!s)
767                     return 0;
768                 *sp = s;
769             }
770         }
771         else
772         {
773             yaz_log(YLOG_FATAL, "Bad element: %s", n->name);
774             return 0;
775         }
776     }
777     if (server->service)
778     {
779         struct conf_service *s;
780         for (s = server->service; s; s = s->next)
781             inherit_server_settings(s);
782     }
783     return server;
784 }
785
786 WRBUF conf_get_fname(struct conf_config *config, const char *fname)
787 {
788     WRBUF w = wrbuf_alloc();
789
790     conf_dir_path(config, w, fname);
791     return w;
792 }
793
794 struct conf_service *locate_service(struct conf_server *server,
795                                     const char *service_id)
796 {
797     struct conf_service *s = server->service;
798     for (; s; s = s->next)
799         if (s->id && service_id && 0 == strcmp(s->id, service_id))
800             break;
801         else if (!s->id && !service_id)
802             break;
803     if (s)
804         service_incref(s);
805     return s;
806 }
807
808 void info_services(struct conf_server *server, WRBUF w)
809 {
810     struct conf_service *s = server->service;
811     wrbuf_puts(w, " <services>\n");
812     for (; s; s = s->next)
813     {
814         wrbuf_puts(w, "  <service");
815         if (s->id)
816         {
817             wrbuf_puts(w, " id=\"");
818             wrbuf_xmlputs(w, s->id);
819             wrbuf_puts(w, "\"");
820         }
821         wrbuf_puts(w, "/>");
822
823         wrbuf_puts(w, "\n");
824     }
825     wrbuf_puts(w, " </services>\n");
826 }
827
828 static int parse_config(struct conf_config *config, xmlNode *root)
829 {
830     xmlNode *n;
831
832     for (n = root->children; n; n = n->next)
833     {
834         if (n->type != XML_ELEMENT_NODE)
835             continue;
836         if (!strcmp((const char *) n->name, "server"))
837         {
838             struct conf_server *tmp = server_create(config, config->nmem, n);
839             if (!tmp)
840                 return -1;
841             tmp->next = config->servers;
842             config->servers = tmp;
843         }
844         else if (!strcmp((const char *) n->name, "threads"))
845         {
846             xmlChar *number = xmlGetProp(n, (xmlChar *) "number");
847             if (number)
848             {
849                 config->no_threads = atoi((const char *) number);
850                 xmlFree(number);
851             }
852         }
853         else if (!strcmp((const char *) n->name, "targetprofiles"))
854         {
855             yaz_log(YLOG_FATAL, "targetprofiles unsupported here. Must be part of service");
856             return -1;
857
858         }
859         else
860         {
861             yaz_log(YLOG_FATAL, "Bad element: %s", n->name);
862             return -1;
863         }
864     }
865     return 0;
866 }
867
868 struct conf_config *config_create(const char *fname, int verbose)
869 {
870     xmlDoc *doc = xmlParseFile(fname);
871     xmlNode *n;
872     const char *p;
873     int r;
874     NMEM nmem = nmem_create();
875     struct conf_config *config = nmem_malloc(nmem, sizeof(struct conf_config));
876
877     xmlSubstituteEntitiesDefault(1);
878     xmlLoadExtDtdDefaultValue = 1;
879     if (!doc)
880     {
881         yaz_log(YLOG_FATAL, "Failed to read %s", fname);
882         nmem_destroy(nmem);
883         return 0;
884     }
885
886     config->nmem = nmem;
887     config->servers = 0;
888     config->no_threads = 0;
889     config->iochan_man = 0;
890     config->database_hosts = database_hosts_create();
891
892     config->confdir = wrbuf_alloc();
893     if ((p = strrchr(fname, 
894 #ifdef WIN32
895                      '\\'
896 #else
897                      '/'
898 #endif
899              )))
900     {
901         int len = p - fname;
902         wrbuf_write(config->confdir, fname, len);
903     }
904     wrbuf_puts(config->confdir, "");
905     
906     n = xmlDocGetRootElement(doc);
907     r = yaz_xml_include_simple(n, wrbuf_cstr(config->confdir));
908     if (r == 0) /* OK */
909     {
910         if (verbose)
911         {
912             yaz_log(YLOG_LOG, "Configuration %s after include processing",
913                     fname);
914 #if LIBXML_VERSION >= 20600
915             xmlDocFormatDump(yaz_log_file(), doc, 0);
916 #else
917             xmlDocDump(yaz_log_file(), doc);
918 #endif
919         }
920         r = parse_config(config, n);
921     }
922     xmlFreeDoc(doc);
923
924     if (r)
925     {
926         config_destroy(config);
927         return 0;
928     }
929     return config;
930 }
931
932 void server_destroy(struct conf_server *server)
933 {
934     struct conf_service *s = server->service;
935     while (s)
936     {
937         struct conf_service *s_next = s->next;
938         service_destroy(s);
939         s = s_next;
940     }
941     pp2_charset_fact_destroy(server->charsets);
942     yaz_log(YLOG_LOG, "server_destroy server=%p", server);
943     http_server_destroy(server->http_server);
944 }
945
946 void config_destroy(struct conf_config *config)
947 {
948     if (config)
949     {
950         struct conf_server *server = config->servers;
951         iochan_man_destroy(&config->iochan_man);    
952         while (server)
953         {
954             struct conf_server *s_next = server->next;
955             server_destroy(server);
956             server = s_next;
957             database_hosts_destroy(&config->database_hosts);
958         }
959         wrbuf_destroy(config->confdir);
960         nmem_destroy(config->nmem);
961     }
962 }
963
964 void config_stop_listeners(struct conf_config *conf)
965 {
966     struct conf_server *ser;
967     for (ser = conf->servers; ser; ser = ser->next)
968         http_close_server(ser);
969 }
970
971 void config_process_events(struct conf_config *conf)
972 {
973     struct conf_server *ser;
974     
975     for (ser = conf->servers; ser; ser = ser->next)
976     {
977         struct conf_service *s = ser->service;
978
979         for (;s ; s = s->next)
980         {
981             assert(s->mutex == 0);
982             pazpar2_mutex_create(&s->mutex, "service");
983         }
984         http_mutex_init(ser);
985     }
986     iochan_man_events(conf->iochan_man);
987 }
988
989 int config_start_listeners(struct conf_config *conf,
990                            const char *listener_override,
991                            const char *record_fname)
992 {
993     struct conf_server *ser;
994
995     conf->iochan_man = iochan_man_create(conf->no_threads);
996     for (ser = conf->servers; ser; ser = ser->next)
997     {
998         WRBUF w = wrbuf_alloc();
999         int r;
1000
1001         ser->iochan_man = conf->iochan_man;
1002         if (listener_override)
1003         {
1004             wrbuf_puts(w, listener_override);
1005             listener_override = 0; /* only first server is overriden */
1006         }
1007         else
1008         {
1009             if (ser->host)
1010                 wrbuf_puts(w, ser->host);
1011             if (ser->port)
1012             {
1013                 if (wrbuf_len(w))
1014                     wrbuf_puts(w, ":");
1015                 wrbuf_printf(w, "%d", ser->port);
1016             }
1017         }
1018         r = http_init(wrbuf_cstr(w), ser, record_fname);
1019         wrbuf_destroy(w);
1020         if (r)
1021             return -1;
1022
1023         w = wrbuf_alloc();
1024         if (ser->proxy_host || ser->proxy_port)
1025         {
1026             if (ser->proxy_host)
1027                 wrbuf_puts(w, ser->proxy_host);
1028             if (ser->proxy_port)
1029             {
1030                 if (wrbuf_len(w))
1031                     wrbuf_puts(w, ":");
1032                 wrbuf_printf(w, "%d", ser->proxy_port);
1033             }
1034         }
1035         if (wrbuf_len(w))
1036             http_set_proxyaddr(wrbuf_cstr(w), ser);
1037         wrbuf_destroy(w);
1038     }
1039     return 0;
1040 }
1041
1042 /*
1043  * Local variables:
1044  * c-basic-offset: 4
1045  * c-file-style: "Stroustrup"
1046  * indent-tabs-mode: nil
1047  * End:
1048  * vim: shiftwidth=4 tabstop=8 expandtab
1049  */
1050