3f3e03b891630b97e5d371b3c4dd3427efc8e3b5
[pazpar2-moved-to-github.git] / src / pazpar2_config.c
1 /* This file is part of Pazpar2.
2    Copyright (C) 2006-2010 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 "incref.h"
42 #include "pazpar2_config.h"
43 #include "settings.h"
44 #include "eventl.h"
45 #include "http.h"
46
47 struct conf_config
48 {
49     NMEM nmem; /* for conf_config and servers memory */
50     struct conf_server *servers;
51     
52     int no_threads;
53     WRBUF confdir;
54     iochan_man_t iochan_man;
55     database_hosts_t database_hosts;
56 };
57
58
59 static char *parse_settings(struct conf_config *config,
60                             NMEM nmem, xmlNode *node);
61
62 static struct conf_targetprofiles *parse_targetprofiles(NMEM nmem,
63                                                         xmlNode *node);
64
65 static void conf_metadata_assign(NMEM nmem, 
66                                  struct conf_metadata * metadata,
67                                  const char *name,
68                                  enum conf_metadata_type type,
69                                  enum conf_metadata_merge merge,
70                                  enum conf_setting_type setting,
71                                  int brief,
72                                  int termlist,
73                                  int rank,
74                                  int sortkey_offset,
75                                  enum conf_metadata_mergekey mt)
76 {
77     assert(nmem && metadata && name);
78     
79     metadata->name = nmem_strdup(nmem, name);
80
81     metadata->type = type;
82
83     // enforcing that type_year is always range_merge
84     if (metadata->type == Metadata_type_year)
85         metadata->merge = Metadata_merge_range;
86     else
87         metadata->merge = merge;    
88
89     metadata->setting = setting;
90     metadata->brief = brief;   
91     metadata->termlist = termlist;
92     metadata->rank = rank;    
93     metadata->sortkey_offset = sortkey_offset;
94     metadata->mergekey = mt;
95 }
96
97
98 static void conf_sortkey_assign(NMEM nmem, 
99                                 struct conf_sortkey * sortkey,
100                                 const char *name,
101                                 enum conf_sortkey_type type)
102 {
103     assert(nmem && sortkey && name);
104     
105     sortkey->name = nmem_strdup(nmem, name);
106     sortkey->type = type;
107 }
108
109
110 static struct conf_service *service_init(struct conf_server *server,
111                                          int num_metadata, int num_sortkeys,
112                                          const char *service_id)
113 {
114     struct conf_service * service = 0;
115     NMEM nmem = nmem_create();
116
117     service = nmem_malloc(nmem, sizeof(struct conf_service));
118     service->mutex = 0;
119     service->ref_count = 1;
120     service->nmem = nmem;
121     service->next = 0;
122     service->settings = 0;
123     service->databases = 0;
124     service->targetprofiles = 0;
125     service->server = server;
126     service->session_timeout = 60; /* default session timeout */
127     service->z3950_session_timeout = 180;
128     service->z3950_operation_timeout = 30;
129
130     service->relevance_pct = 0;
131     service->sort_pct = 0;
132     service->mergekey_pct = 0;
133
134     service->id = service_id ? nmem_strdup(nmem, service_id) : 0;
135     service->num_metadata = num_metadata;
136     service->metadata = 0;
137     if (service->num_metadata)
138       service->metadata 
139           = nmem_malloc(nmem, 
140                         sizeof(struct conf_metadata) * service->num_metadata);
141     service->num_sortkeys = num_sortkeys;
142     service->sortkeys = 0;
143     if (service->num_sortkeys)
144         service->sortkeys 
145             = nmem_malloc(nmem, 
146                           sizeof(struct conf_sortkey) * service->num_sortkeys);
147     service->dictionary = 0;
148     return service; 
149 }
150
151 static struct conf_metadata* conf_service_add_metadata(
152     struct conf_service *service,
153     int field_id,
154     const char *name,
155     enum conf_metadata_type type,
156     enum conf_metadata_merge merge,
157     enum conf_setting_type setting,
158     int brief,
159     int termlist,
160     int rank,
161     int sortkey_offset,
162     enum conf_metadata_mergekey mt)
163 {
164     struct conf_metadata * md = 0;
165
166     if (!service || !service->metadata || !service->num_metadata
167         || field_id < 0  || !(field_id < service->num_metadata))
168         return 0;
169
170     md = service->metadata + field_id;
171     conf_metadata_assign(service->nmem, md, name, type, merge, setting,
172                          brief, termlist, rank, sortkey_offset,
173                          mt);
174     return md;
175 }
176
177
178 static struct conf_sortkey * conf_service_add_sortkey(
179     struct conf_service *service,
180     int field_id,
181     const char *name,
182     enum conf_sortkey_type type)
183 {
184     struct conf_sortkey * sk = 0;
185
186     if (!service || !service->sortkeys || !service->num_sortkeys
187         || field_id < 0  || !(field_id < service->num_sortkeys))
188         return 0;
189
190     //sk = &((service->sortkeys)[field_id]);
191     sk = service->sortkeys + field_id;
192     conf_sortkey_assign(service->nmem, sk, name, type);
193
194     return sk;
195 }
196
197
198 int conf_service_metadata_field_id(struct conf_service *service,
199                                    const char * name)
200 {
201     int i = 0;
202
203     if (!service || !service->metadata || !service->num_metadata)
204         return -1;
205
206     for(i = 0; i < service->num_metadata; i++) {
207         if (!strcmp(name, (service->metadata[i]).name))
208             return i;
209     }
210    
211     return -1;
212 }
213
214
215 int conf_service_sortkey_field_id(struct conf_service *service,
216                                   const char * name)
217 {
218     int i = 0;
219
220     if (!service || !service->sortkeys || !service->num_sortkeys)
221         return -1;
222
223     for(i = 0; i < service->num_sortkeys; i++) {
224         if (!strcmp(name, (service->sortkeys[i]).name))
225             return i;
226     }
227    
228     return -1;
229 }
230
231 static void conf_dir_path(struct conf_config *config, WRBUF w, const char *src)
232 {
233     if (config->confdir && wrbuf_len(config->confdir) > 0 &&
234         !yaz_is_abspath(src))
235     {
236         wrbuf_printf(w, "%s/%s", wrbuf_cstr(config->confdir), src);
237     }
238     else
239         wrbuf_puts(w, src);
240 }
241
242 void service_destroy(struct conf_service *service)
243 {
244     if (service)
245     {
246         if (!pazpar2_decref(&service->ref_count, service->mutex))
247         {
248             pp2_charset_destroy(service->relevance_pct);
249             pp2_charset_destroy(service->sort_pct);
250             pp2_charset_destroy(service->mergekey_pct);
251             yaz_mutex_destroy(&service->mutex);
252             nmem_destroy(service->nmem);
253         }
254     }
255 }
256
257 void service_incref(struct conf_service *service)
258 {
259     yaz_log(YLOG_LOG, "service_incref. p=%p cnt=%d", service,
260             service->ref_count);
261     pazpar2_incref(&service->ref_count, service->mutex);
262 }
263
264 static int parse_metadata(struct conf_service *service, xmlNode *n,
265                           int *md_node, int *sk_node)
266 {
267     xmlChar *xml_name = xmlGetProp(n, (xmlChar *) "name");
268     xmlChar *xml_brief = xmlGetProp(n, (xmlChar *) "brief");
269     xmlChar *xml_sortkey = xmlGetProp(n, (xmlChar *) "sortkey");
270     xmlChar *xml_merge = xmlGetProp(n, (xmlChar *) "merge");
271     xmlChar *xml_type = xmlGetProp(n, (xmlChar *) "type");
272     xmlChar *xml_termlist = xmlGetProp(n, (xmlChar *) "termlist");
273     xmlChar *xml_rank = xmlGetProp(n, (xmlChar *) "rank");
274     xmlChar *xml_setting = xmlGetProp(n, (xmlChar *) "setting");
275     xmlChar *xml_mergekey = xmlGetProp(n, (xmlChar *) "mergekey");
276     
277     enum conf_metadata_type type = Metadata_type_generic;
278     enum conf_metadata_merge merge = Metadata_merge_no;
279     enum conf_setting_type setting = Metadata_setting_no;
280     enum conf_sortkey_type sk_type = Metadata_sortkey_relevance;
281     enum conf_metadata_mergekey mergekey_type = Metadata_mergekey_no;
282     int brief = 0;
283     int termlist = 0;
284     int rank = 0;
285     int sortkey_offset = 0;
286     
287     // now do the parsing logic
288     if (!xml_name)
289     {
290         yaz_log(YLOG_FATAL, "Must specify name in metadata element");
291         return -1;
292     }
293     if (xml_brief)
294     {
295         if (!strcmp((const char *) xml_brief, "yes"))
296             brief = 1;
297         else if (strcmp((const char *) xml_brief, "no"))
298         {
299             yaz_log(YLOG_FATAL, "metadata/brief must be yes or no");
300             return -1;
301         }
302     }
303     else
304         brief = 0;
305     
306     if (xml_termlist)
307     {
308         if (!strcmp((const char *) xml_termlist, "yes"))
309             termlist = 1;
310         else if (strcmp((const char *) xml_termlist, "no"))
311         {
312             yaz_log(YLOG_FATAL, "metadata/termlist must be yes or no");
313             return -1;
314         }
315     }
316     else
317         termlist = 0;
318     
319     if (xml_rank)
320         rank = atoi((const char *) xml_rank);
321     else
322         rank = 0;
323     
324     if (xml_type)
325     {
326         if (!strcmp((const char *) xml_type, "generic"))
327             type = Metadata_type_generic;
328         else if (!strcmp((const char *) xml_type, "year"))
329             type = Metadata_type_year;
330         else if (!strcmp((const char *) xml_type, "date"))
331             type = Metadata_type_date;
332         else
333         {
334             yaz_log(YLOG_FATAL, 
335                     "Unknown value for metadata/type: %s", xml_type);
336             return -1;
337         }
338     }
339     else
340         type = Metadata_type_generic;
341     
342     if (xml_merge)
343     {
344         if (!strcmp((const char *) xml_merge, "no"))
345             merge = Metadata_merge_no;
346         else if (!strcmp((const char *) xml_merge, "unique"))
347             merge = Metadata_merge_unique;
348         else if (!strcmp((const char *) xml_merge, "longest"))
349             merge = Metadata_merge_longest;
350         else if (!strcmp((const char *) xml_merge, "range"))
351             merge = Metadata_merge_range;
352         else if (!strcmp((const char *) xml_merge, "all"))
353             merge = Metadata_merge_all;
354         else
355         {
356             yaz_log(YLOG_FATAL, 
357                     "Unknown value for metadata/merge: %s", xml_merge);
358             return -1;
359         }
360     }
361     else
362         merge = Metadata_merge_no;
363     
364     if (xml_setting)
365     {
366         if (!strcmp((const char *) xml_setting, "no"))
367             setting = Metadata_setting_no;
368         else if (!strcmp((const char *) xml_setting, "postproc"))
369             setting = Metadata_setting_postproc;
370         else if (!strcmp((const char *) xml_setting, "parameter"))
371             setting = Metadata_setting_parameter;
372         else
373         {
374             yaz_log(YLOG_FATAL,
375                     "Unknown value for medadata/setting: %s", xml_setting);
376             return -1;
377         }
378     }
379     
380     // add a sortkey if so specified
381     if (xml_sortkey && strcmp((const char *) xml_sortkey, "no"))
382     {
383         if (merge == Metadata_merge_no)
384         {
385             yaz_log(YLOG_FATAL, 
386                     "Can't specify sortkey on a non-merged field");
387             return -1;
388         }
389         if (!strcmp((const char *) xml_sortkey, "numeric"))
390             sk_type = Metadata_sortkey_numeric;
391         else if (!strcmp((const char *) xml_sortkey, "skiparticle"))
392             sk_type = Metadata_sortkey_skiparticle;
393         else
394         {
395             yaz_log(YLOG_FATAL,
396                     "Unknown sortkey in metadata element: %s", 
397                     xml_sortkey);
398             return -1;
399         }
400         sortkey_offset = *sk_node;
401         
402         conf_service_add_sortkey(service, *sk_node,
403                                  (const char *) xml_name, sk_type);
404         
405         (*sk_node)++;
406     }
407     else
408         sortkey_offset = -1;
409     
410     if (xml_mergekey)
411     {
412         if (!strcmp((const char *) xml_mergekey, "required"))
413             mergekey_type = Metadata_mergekey_required;
414         else if (!strcmp((const char *) xml_mergekey, "optional"))
415             mergekey_type = Metadata_mergekey_optional;
416         else if (!strcmp((const char *) xml_mergekey, "no"))
417             mergekey_type = Metadata_mergekey_no;
418         else
419         {
420             yaz_log(YLOG_FATAL, "Unknown value for mergekey: %s", xml_mergekey);
421             return -1;
422         }
423     }
424     
425     
426     // metadata known, assign values
427     conf_service_add_metadata(service, *md_node,
428                               (const char *) xml_name,
429                               type, merge, setting,
430                               brief, termlist, rank, sortkey_offset,
431                               mergekey_type);
432     
433     xmlFree(xml_name);
434     xmlFree(xml_brief);
435     xmlFree(xml_sortkey);
436     xmlFree(xml_merge);
437     xmlFree(xml_type);
438     xmlFree(xml_termlist);
439     xmlFree(xml_rank);
440     xmlFree(xml_setting);
441     xmlFree(xml_mergekey);
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, (const char *) "targetprofiles"))
516         {
517             if (service->targetprofiles)
518             {
519                 yaz_log(YLOG_FATAL, "Can't repeat targetprofiles");
520                 return 0;
521             }
522             if (!(service->targetprofiles = 
523                   parse_targetprofiles(service->nmem, n)))
524                 return 0;
525         }
526         else if (!strcmp((const char *) n->name, "relevance"))
527         {
528             if (service->relevance_pct)
529             {
530                 yaz_log(YLOG_LOG, "relevance may not repeat in service");
531                 return 0;
532             }
533             else
534             {
535                 service->relevance_pct = pp2_charset_create_xml(n);
536                 if (!service->relevance_pct)
537                     return 0;
538             }
539         }
540         else if (!strcmp((const char *) n->name, "sort"))
541         {
542             if (service->sort_pct)
543             {
544                 yaz_log(YLOG_LOG, "sort may not repeat in service");
545                 return 0;
546             }
547             else
548             {
549                 service->sort_pct = pp2_charset_create_xml(n);
550                 if (!service->sort_pct)
551                     return 0;
552             }
553         }
554         else if (!strcmp((const char *) n->name, "mergekey"))
555         {
556             if (service->mergekey_pct)
557             {
558                 yaz_log(YLOG_LOG, "mergekey may not repeat in service");
559                 return 0;
560             }
561             else
562             {
563                 service->mergekey_pct = pp2_charset_create_xml(n);
564                 if (!service->mergekey_pct)
565                     return 0;
566             }
567         }
568         else if (!strcmp((const char *) n->name, (const char *) "metadata"))
569         {
570             if (parse_metadata(service, n, &md_node, &sk_node))
571                 return 0;
572         }
573         else
574         {
575             yaz_log(YLOG_FATAL, "Bad element: %s", n->name);
576             return 0;
577         }
578     }
579     if (got_settings)
580     {
581         int pass;
582         /* metadata has been read.. Consider now settings */
583         init_settings(service);
584         for (pass = 1; pass <= 2; pass++)
585         {
586             for (n = node->children; n; n = n->next)
587             {
588                 if (n->type != XML_ELEMENT_NODE)
589                     continue;
590                 if (!strcmp((const char *) n->name, "settings"))
591                 {
592                     xmlChar *src = xmlGetProp(n, (xmlChar *) "src");
593                     if (src)
594                     {
595                         WRBUF w = wrbuf_alloc();
596                         conf_dir_path(server->config, w, (const char *) src);
597                         settings_read_file(service, wrbuf_cstr(w), pass);
598                         wrbuf_destroy(w);
599                         xmlFree(src);
600                     }
601                     else
602                     {
603                         settings_read_node(service, n, pass);
604                     }
605                 }
606             }
607         }
608     }
609     return service;
610 }
611
612 static char *parse_settings(struct conf_config *config,
613                             NMEM nmem, xmlNode *node)
614 {
615     xmlChar *src = xmlGetProp(node, (xmlChar *) "src");
616     char *r;
617
618     if (src)
619     {
620         WRBUF w = wrbuf_alloc();
621         conf_dir_path(config, w, (const char *) src);
622         r = nmem_strdup(nmem, wrbuf_cstr(w));
623         wrbuf_destroy(w);
624     }
625     else
626     {
627         yaz_log(YLOG_FATAL, "Must specify src in targetprofile");
628         return 0;
629     }
630     xmlFree(src);
631     return r;
632 }
633
634 static void inherit_server_settings(struct conf_service *s)
635 {
636     struct conf_server *server = s->server;
637     if (!s->dictionary) /* service has no config settings ? */
638     {
639         if (server->server_settings)
640         {
641             /* inherit settings from server */
642             init_settings(s);
643             settings_read_file(s, server->server_settings, 1);
644             settings_read_file(s, server->server_settings, 2);
645         }
646         else
647         {
648             yaz_log(YLOG_WARN, "service '%s' has no settings",
649                     s->id ? s->id : "unnamed");
650             init_settings(s);
651         }
652     }
653     
654     /* use relevance/sort/mergekey from server if not defined
655        for this service.. */
656     if (!s->relevance_pct)
657     {
658         if (server->relevance_pct)
659         {
660             s->relevance_pct = server->relevance_pct;
661             pp2_charset_incref(s->relevance_pct);
662         }
663         else
664             s->relevance_pct = pp2_charset_create(0);
665     }
666     
667     if (!s->sort_pct)
668     {
669         if (server->sort_pct)
670         {
671             s->sort_pct = server->sort_pct;
672             pp2_charset_incref(s->sort_pct);
673         }
674         else
675             s->sort_pct = pp2_charset_create(0);
676     }
677     
678     if (!s->mergekey_pct)
679     {
680         if (server->mergekey_pct)
681         {
682             s->mergekey_pct = server->mergekey_pct;
683             pp2_charset_incref(s->mergekey_pct);
684         }
685         else
686             s->mergekey_pct = pp2_charset_create(0);
687     }
688 }
689
690 struct conf_service *service_create(struct conf_server *server,
691                                     xmlNode *node)
692 {
693     struct conf_service *service = service_create_static(server,
694                                                          node, 0);
695     if (service)
696     {
697         inherit_server_settings(service);
698         resolve_databases(service);
699         assert(service->mutex == 0);
700         yaz_mutex_create(&service->mutex);
701     }
702     return service;
703 }
704
705 static struct conf_server *server_create(struct conf_config *config,
706                                          NMEM nmem, xmlNode *node)
707 {
708     xmlNode *n;
709     struct conf_server *server = nmem_malloc(nmem, sizeof(struct conf_server));
710     xmlChar *server_id = xmlGetProp(node, (xmlChar *) "id");
711
712     server->host = 0;
713     server->port = 0;
714     server->proxy_host = 0;
715     server->proxy_port = 0;
716     server->myurl = 0;
717     server->service = 0;
718     server->config = config;
719     server->next = 0;
720     server->relevance_pct = 0;
721     server->sort_pct = 0;
722     server->mergekey_pct = 0;
723     server->server_settings = 0;
724     server->http_server = 0;
725     server->iochan_man = 0;
726     server->database_hosts = 0;
727
728     if (server_id)
729     {
730         server->server_id = nmem_strdup(nmem, (const char *)server_id);
731         xmlFree(server_id);
732     }
733     else
734         server->server_id = 0;
735     for (n = node->children; n; n = n->next)
736     {
737         if (n->type != XML_ELEMENT_NODE)
738             continue;
739         if (!strcmp((const char *) n->name, "listen"))
740         {
741             xmlChar *port = xmlGetProp(n, (xmlChar *) "port");
742             xmlChar *host = xmlGetProp(n, (xmlChar *) "host");
743             if (port)
744                 server->port = atoi((const char *) port);
745             if (host)
746                 server->host = nmem_strdup(nmem, (const char *) host);
747             xmlFree(port);
748             xmlFree(host);
749         }
750         else if (!strcmp((const char *) n->name, "proxy"))
751         {
752             xmlChar *port = xmlGetProp(n, (xmlChar *) "port");
753             xmlChar *host = xmlGetProp(n, (xmlChar *) "host");
754             xmlChar *myurl = xmlGetProp(n, (xmlChar *) "myurl");
755             if (port)
756                 server->proxy_port = atoi((const char *) port);
757             if (host)
758                 server->proxy_host = nmem_strdup(nmem, (const char *) host);
759             if (myurl)
760                 server->myurl = nmem_strdup(nmem, (const char *) myurl);
761             xmlFree(port);
762             xmlFree(host);
763             xmlFree(myurl);
764         }
765         else if (!strcmp((const char *) n->name, "settings"))
766         {
767             if (server->server_settings)
768             {
769                 yaz_log(YLOG_FATAL, "Can't repeat 'settings'");
770                 return 0;
771             }
772             if (!(server->server_settings = parse_settings(config, nmem, n)))
773                 return 0;
774         }
775         else if (!strcmp((const char *) n->name, "relevance"))
776         {
777             server->relevance_pct = pp2_charset_create_xml(n);
778             if (!server->relevance_pct)
779                 return 0;
780         }
781         else if (!strcmp((const char *) n->name, "sort"))
782         {
783             server->sort_pct = pp2_charset_create_xml(n);
784             if (!server->sort_pct)
785                 return 0;
786         }
787         else if (!strcmp((const char *) n->name, "mergekey"))
788         {
789             server->mergekey_pct = pp2_charset_create_xml(n);
790             if (!server->mergekey_pct)
791                 return 0;
792         }
793         else if (!strcmp((const char *) n->name, "service"))
794         {
795             char *service_id = (char *)
796                 xmlGetProp(n, (xmlChar *) "id");
797
798             struct conf_service **sp = &server->service;
799             for (; *sp; sp = &(*sp)->next)
800                 if ((*sp)->id && service_id &&
801                     0 == strcmp((*sp)->id, service_id))
802                 {
803                     yaz_log(YLOG_FATAL, "Duplicate service: %s", service_id);
804                     break;
805                 }
806                 else if (!(*sp)->id && !service_id)
807                 {
808                     yaz_log(YLOG_FATAL, "Duplicate unnamed service");
809                     break;
810                 }
811
812             if (*sp)  /* service already exist */
813             {
814                 xmlFree(service_id);
815                 return 0;
816             }
817             else
818             {
819                 struct conf_service *s = service_create_static(server, n,
820                                                                service_id);
821                 xmlFree(service_id);
822                 if (!s)
823                     return 0;
824                 *sp = s;
825             }
826         }
827         else
828         {
829             yaz_log(YLOG_FATAL, "Bad element: %s", n->name);
830             return 0;
831         }
832     }
833     if (server->service)
834     {
835         struct conf_service *s;
836         for (s = server->service; s; s = s->next)
837             inherit_server_settings(s);
838     }
839     return server;
840 }
841
842 WRBUF conf_get_fname(struct conf_service *service, const char *fname)
843 {
844     struct conf_config *config = service->server->config;
845     WRBUF w = wrbuf_alloc();
846
847     conf_dir_path(config, w, fname);
848     return w;
849 }
850
851 static struct conf_targetprofiles *parse_targetprofiles(NMEM nmem,
852                                                         xmlNode *node)
853 {
854     struct conf_targetprofiles *r = nmem_malloc(nmem, sizeof(*r));
855     xmlChar *type = xmlGetProp(node, (xmlChar *) "type");
856     xmlChar *src = xmlGetProp(node, (xmlChar *) "src");
857
858     memset(r, 0, sizeof(*r));
859
860     if (type)
861     {
862         if (!strcmp((const char *) type, "local"))
863             r->type = Targetprofiles_local;
864         else
865         {
866             yaz_log(YLOG_FATAL, "Unknown targetprofile type");
867             return 0;
868         }
869     }
870     else
871     {
872         yaz_log(YLOG_FATAL, "Must specify type for targetprofile");
873         return 0;
874     }
875
876     if (src)
877         r->src = nmem_strdup(nmem, (const char *) src);
878     else
879     {
880         yaz_log(YLOG_FATAL, "Must specify src in targetprofile");
881         return 0;
882     }
883     xmlFree(type);
884     xmlFree(src);
885     return r;
886 }
887
888 struct conf_service *locate_service(struct conf_server *server,
889                                     const char *service_id)
890 {
891     struct conf_service *s = server->service;
892     for (; s; s = s->next)
893         if (s->id && service_id && 0 == strcmp(s->id, service_id))
894             break;
895         else if (!s->id && !service_id)
896             break;
897     if (s)
898         service_incref(s);
899     return s;
900 }
901
902
903 static int parse_config(struct conf_config *config, xmlNode *root)
904 {
905     xmlNode *n;
906
907     for (n = root->children; n; n = n->next)
908     {
909         if (n->type != XML_ELEMENT_NODE)
910             continue;
911         if (!strcmp((const char *) n->name, "server"))
912         {
913             struct conf_server *tmp = server_create(config, config->nmem, n);
914             if (!tmp)
915                 return -1;
916             tmp->next = config->servers;
917             config->servers = tmp;
918         }
919         else if (!strcmp((const char *) n->name, "threads"))
920         {
921             xmlChar *number = xmlGetProp(n, (xmlChar *) "number");
922             if (number)
923             {
924                 config->no_threads = atoi((const char *) number);
925                 xmlFree(number);
926             }
927         }
928         else if (!strcmp((const char *) n->name, "targetprofiles"))
929         {
930             yaz_log(YLOG_FATAL, "targetprofiles unsupported here. Must be part of service");
931             return -1;
932
933         }
934         else
935         {
936             yaz_log(YLOG_FATAL, "Bad element: %s", n->name);
937             return -1;
938         }
939     }
940     return 0;
941 }
942
943 struct conf_config *config_create(const char *fname, int verbose)
944 {
945     xmlDoc *doc = xmlParseFile(fname);
946     xmlNode *n;
947     const char *p;
948     int r;
949     NMEM nmem = nmem_create();
950     struct conf_config *config = nmem_malloc(nmem, sizeof(struct conf_config));
951
952     xmlSubstituteEntitiesDefault(1);
953     xmlLoadExtDtdDefaultValue = 1;
954     if (!doc)
955     {
956         yaz_log(YLOG_FATAL, "Failed to read %s", fname);
957         nmem_destroy(nmem);
958         return 0;
959     }
960
961     config->nmem = nmem;
962     config->servers = 0;
963     config->no_threads = 0;
964     config->iochan_man = 0;
965
966     config->confdir = wrbuf_alloc();
967     if ((p = strrchr(fname, 
968 #ifdef WIN32
969                      '\\'
970 #else
971                      '/'
972 #endif
973              )))
974     {
975         int len = p - fname;
976         wrbuf_write(config->confdir, fname, len);
977     }
978     wrbuf_puts(config->confdir, "");
979     
980     n = xmlDocGetRootElement(doc);
981     r = yaz_xml_include_simple(n, wrbuf_cstr(config->confdir));
982     if (r == 0) /* OK */
983     {
984         if (verbose)
985         {
986             yaz_log(YLOG_LOG, "Configuration %s after include processing",
987                     fname);
988 #if LIBXML_VERSION >= 20600
989             xmlDocFormatDump(yaz_log_file(), doc, 0);
990 #else
991             xmlDocDump(yaz_log_file(), doc);
992 #endif
993         }
994         r = parse_config(config, n);
995     }
996     xmlFreeDoc(doc);
997
998     if (r)
999     {
1000         config_destroy(config);
1001         return 0;
1002     }
1003     return config;
1004 }
1005
1006 void server_destroy(struct conf_server *server)
1007 {
1008     struct conf_service *s = server->service;
1009     while (s)
1010     {
1011         struct conf_service *s_next = s->next;
1012         service_destroy(s);
1013         s = s_next;
1014     }
1015     pp2_charset_destroy(server->relevance_pct);
1016     pp2_charset_destroy(server->sort_pct);
1017     pp2_charset_destroy(server->mergekey_pct);
1018     yaz_log(YLOG_LOG, "server_destroy server=%p", server);
1019     http_server_destroy(server->http_server);
1020 }
1021
1022 void config_destroy(struct conf_config *config)
1023 {
1024     if (config)
1025     {
1026         struct conf_server *server = config->servers;
1027         iochan_man_destroy(&config->iochan_man);    
1028         while (server)
1029         {
1030             struct conf_server *s_next = server->next;
1031             server_destroy(server);
1032             server = s_next;
1033         }
1034         database_hosts_destroy(&config->database_hosts);
1035
1036         wrbuf_destroy(config->confdir);
1037         nmem_destroy(config->nmem);
1038     }
1039 }
1040
1041 void config_stop_listeners(struct conf_config *conf)
1042 {
1043     struct conf_server *ser;
1044     for (ser = conf->servers; ser; ser = ser->next)
1045         http_close_server(ser);
1046 }
1047
1048 void config_process_events(struct conf_config *conf)
1049 {
1050     struct conf_server *ser;
1051     
1052     conf->database_hosts = database_hosts_create();
1053     for (ser = conf->servers; ser; ser = ser->next)
1054     {
1055         struct conf_service *s = ser->service;
1056
1057         ser->database_hosts = conf->database_hosts;
1058
1059         for (;s ; s = s->next)
1060         {
1061             resolve_databases(s);
1062             assert(s->mutex == 0);
1063             yaz_mutex_create(&s->mutex);
1064         }
1065         http_mutex_init(ser);
1066     }
1067     iochan_man_events(conf->iochan_man);    
1068 }
1069
1070 int config_start_listeners(struct conf_config *conf,
1071                            const char *listener_override)
1072 {
1073     struct conf_server *ser;
1074
1075     conf->iochan_man = iochan_man_create(conf->no_threads);
1076     for (ser = conf->servers; ser; ser = ser->next)
1077     {
1078         WRBUF w = wrbuf_alloc();
1079         int r;
1080
1081         ser->iochan_man = conf->iochan_man;
1082         if (listener_override)
1083         {
1084             wrbuf_puts(w, listener_override);
1085             listener_override = 0; /* only first server is overriden */
1086         }
1087         else
1088         {
1089             if (ser->host)
1090                 wrbuf_puts(w, ser->host);
1091             if (ser->port)
1092             {
1093                 if (wrbuf_len(w))
1094                     wrbuf_puts(w, ":");
1095                 wrbuf_printf(w, "%d", ser->port);
1096             }
1097         }
1098         r = http_init(wrbuf_cstr(w), ser);
1099         wrbuf_destroy(w);
1100         if (r)
1101             return -1;
1102
1103         w = wrbuf_alloc();
1104         if (ser->proxy_host || ser->proxy_port)
1105         {
1106             if (ser->proxy_host)
1107                 wrbuf_puts(w, ser->proxy_host);
1108             if (ser->proxy_port)
1109             {
1110                 if (wrbuf_len(w))
1111                     wrbuf_puts(w, ":");
1112                 wrbuf_printf(w, "%d", ser->proxy_port);
1113             }
1114         }
1115         if (wrbuf_len(w))
1116             http_set_proxyaddr(wrbuf_cstr(w), ser);
1117         wrbuf_destroy(w);
1118     }
1119     return 0;
1120 }
1121
1122 /*
1123  * Local variables:
1124  * c-basic-offset: 4
1125  * c-file-style: "Stroustrup"
1126  * indent-tabs-mode: nil
1127  * End:
1128  * vim: shiftwidth=4 tabstop=8 expandtab
1129  */
1130