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