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