Perform getaddrinfo in separate thread for a given struct host.
[pazpar2-moved-to-github.git] / src / database.c
1 /* $Id: database.c,v 1.23 2007-04-21 12:00:54 adam Exp $
2    Copyright (c) 2006-2007, Index Data.
3
4 This file is part of Pazpar2.
5
6 Pazpar2 is free software; you can redistribute it and/or modify it under
7 the terms of the GNU General Public License as published by the Free
8 Software Foundation; either version 2, or (at your option) any later
9 version.
10
11 Pazpar2 is distributed in the hope that it will be useful, but WITHOUT ANY
12 WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with Pazpar2; see the file LICENSE.  If not, write to the
18 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
19 02111-1307, USA.
20  */
21
22 #include <libxml/parser.h>
23 #include <libxml/tree.h>
24 #include <libxslt/xslt.h>
25 #include <libxslt/transform.h>
26 #include <libxslt/xsltutils.h>
27 #include <assert.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30
31 #include "pazpar2.h"
32 #include "config.h"
33 #include "settings.h"
34 #include "http.h"
35 #include "zeerex.h"
36
37 #include <sys/types.h>
38 #include <sys/socket.h>
39 #include <netdb.h>
40 #include <netinet/in.h>
41
42 static struct host *hosts = 0;  // The hosts we know about 
43 static struct database *databases = 0; // The databases we know about
44 static NMEM nmem = 0;
45
46 static xmlDoc *get_explain_xml(const char *id)
47 {
48     struct stat st;
49     char *dir;
50     char path[256];
51     char ide[256];
52     if (!config || !config->targetprofiles)
53     {
54         yaz_log(YLOG_WARN, "Config must be loaded and specify targetprofiles");
55         return 0;
56     }
57     if (config->targetprofiles->type != Targetprofiles_local)
58     {
59         yaz_log(YLOG_FATAL, "Only supports local type");
60         return 0;
61     }
62     dir = config->targetprofiles->src;
63     urlencode(id, ide);
64     sprintf(path, "%s/%s", dir, ide);
65     if (!stat(path, &st))
66         return xmlParseFile(path);
67     else
68         return 0;
69 }
70
71 // Create a new host structure for hostport
72 static struct host *create_host(const char *hostport)
73 {
74     struct host *host;
75
76     host = xmalloc(sizeof(struct host));
77     host->hostport = xstrdup(hostport);
78     host->connections = 0;
79     host->ipport = 0;
80
81     if (host_getaddrinfo(host))
82     {
83         xfree(host->hostport);
84         xfree(host);
85         return 0;
86     }
87     host->next = hosts;
88     hosts = host;
89     return host;
90 }
91
92 static struct host *find_host(const char *hostport)
93 {
94     struct host *p;
95     for (p = hosts; p; p = p->next)
96         if (!strcmp(p->hostport, hostport))
97             return p;
98     return create_host(hostport);
99 }
100
101 static struct database *load_database(const char *id)
102 {
103     xmlDoc *doc = 0;
104     struct zr_explain *explain = 0;
105     struct database *db;
106     struct host *host;
107     char hostport[256];
108     char *dbname;
109
110     yaz_log(YLOG_LOG, "New database: %s", id);
111     if (!nmem)
112         nmem = nmem_create();
113
114     if (config && config->targetprofiles 
115         && (doc = get_explain_xml(id)))
116     {
117         explain = zr_read_xml(nmem, xmlDocGetRootElement(doc));
118         if (!explain)
119             return 0;
120     }
121
122     if (strlen(id) > 255)
123         return 0;
124     strcpy(hostport, id);
125     if ((dbname = strchr(hostport, '/')))
126         *(dbname++) = '\0';
127     else
128         dbname = "Default";
129     if (!(host = find_host(hostport)))
130         return 0;
131     db = nmem_malloc(nmem, sizeof(*db));
132     memset(db, 0, sizeof(*db));
133     db->host = host;
134     db->url = nmem_strdup(nmem, id);
135     db->databases = xmalloc(2 * sizeof(char *));
136     db->databases[0] = nmem_strdup(nmem, dbname);
137     db->databases[1] = 0;
138     db->errors = 0;
139     db->explain = explain;
140     db->settings = 0;
141     db->next = databases;
142     databases = db;
143
144     return db;
145 }
146
147 // Return a database structure by ID. Load and add to list if necessary
148 // new==1 just means we know it's not in the list
149 struct database *find_database(const char *id, int new)
150 {
151     struct database *p;
152     if (!new)
153     {
154         for (p = databases; p; p = p->next)
155             if (!strcmp(p->url, id))
156                 return p;
157     }
158     return load_database(id);
159 }
160
161 // This whole session_grep database thing should be moved to pazpar2.c
162
163 int match_zurl(const char *zurl, const char *pattern)
164 {
165     if (!strcmp(pattern, "*"))
166         return 1;
167     else if (!strncmp(pattern, "*/", 2))
168     {
169         char *db = strchr(zurl, '/');
170         if (!db)
171             return 0;
172         if (!strcmp(pattern + 2, db))
173             return 1;
174         else
175             return 0;
176     }
177     else if (!strcmp(pattern, zurl))
178         return 1;
179     else
180         return 0;
181 }
182
183 // This will be generalized at some point
184 static int match_criterion(struct setting **settings, struct database_criterion *c)
185 {
186     int offset = settings_offset(c->name);
187     struct database_criterion_value *v;
188
189     if (offset < 0)
190     {
191         yaz_log(YLOG_WARN, "Criterion not found: %s", c->name);
192         return 0;
193     }
194     if (!settings[offset])
195         return 0;
196     for (v = c->values; v; v = v->next)
197     {
198         if (offset == PZ_ID)
199         {
200             if (match_zurl(settings[offset]->value, v->value))
201                 break;
202         }
203         else 
204         {
205             if (!strcmp(settings[offset]->value, v->value))
206                 break;
207         }
208     }
209     if (v)
210         return 1;
211     else
212         return 0;
213 }
214
215 int database_match_criteria(struct setting **settings, struct database_criterion *cl)
216 {
217     for (; cl; cl = cl->next)
218         if (!match_criterion(settings, cl))
219             break;
220     if (cl) // one of the criteria failed to match -- skip this db
221         return 0;
222     else
223         return 1;
224 }
225
226 // Cycles through databases, calling a handler function on the ones for
227 // which all criteria matched.
228 int session_grep_databases(struct session *se, struct database_criterion *cl,
229         void (*fun)(void *context, struct session_database *db))
230 {
231     struct session_database *p;
232     int i = 0;
233
234     for (p = se->databases; p; p = p->next)
235     {
236         if (p->settings && p->settings[PZ_ALLOW] && *p->settings[PZ_ALLOW]->value == '0')
237             continue;
238         if (database_match_criteria(p->settings, cl))
239         {
240             (*fun)(se, p);
241             i++;
242         }
243     }
244     return i;
245 }
246
247 int grep_databases(void *context, struct database_criterion *cl,
248         void (*fun)(void *context, struct database *db))
249 {
250     struct database *p;
251     int i = 0;
252
253     for (p = databases; p; p = p->next)
254         if (database_match_criteria(p->settings, cl))
255         {
256             (*fun)(context, p);
257             i++;
258         }
259     return i;
260 }
261
262 // This function will most likely vanish when a proper target profile mechanism is
263 // introduced.
264 void load_simpletargets(const char *fn)
265 {
266     FILE *f = fopen(fn, "r");
267     char line[256];
268
269     if (!f)
270     {
271         yaz_log(YLOG_WARN|YLOG_ERRNO, "open %s", fn);
272         exit(1);
273     }
274
275     while (fgets(line, 255, f))
276     {
277         char *url;
278         char *name;
279         struct database *db;
280
281         if (strncmp(line, "target ", 7))
282             continue;
283         line[strlen(line) - 1] = '\0';
284
285         if ((name = strchr(line, ';')))
286             *(name++) = '\0';
287
288         url = line + 7;
289
290         if (!(db = find_database(url, 0)))
291             yaz_log(YLOG_WARN, "Unable to load database %s", url);
292     }
293     fclose(f);
294 }
295
296
297 /*
298  * Local variables:
299  * c-basic-offset: 4
300  * indent-tabs-mode: nil
301  * End:
302  * vim: shiftwidth=4 tabstop=8 expandtab
303  */