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