This update completes the factoring out of database management into database.c,
[pazpar2-moved-to-github.git] / src / database.c
1 /* $Id: database.c,v 1.1 2007-03-15 16:55:34 quinn Exp $ */
2
3 #include <libxml/parser.h>
4 #include <libxml/tree.h>
5 #include <libxslt/xslt.h>
6 #include <libxslt/transform.h>
7 #include <libxslt/xsltutils.h>
8 #include <assert.h>
9
10 #include "pazpar2.h"
11 #include "config.h"
12 #include "http.h"
13 #include "zeerex.h"
14
15 static struct host *hosts = 0;  // The hosts we know about 
16 static struct database *databases = 0; // The databases we know about
17 static NMEM nmem = 0;
18
19 // This needs to be extended with selection criteria
20 static struct conf_retrievalprofile *database_retrievalprofile(const char *id)
21 {
22     if (!config)
23     {
24         yaz_log(YLOG_FATAL, "Must load configuration (-f)");
25         exit(1);
26     }
27     if (!config->retrievalprofiles)
28     {
29         yaz_log(YLOG_FATAL, "No retrieval profiles defined");
30     }
31     return config->retrievalprofiles;
32 }
33
34 static struct conf_queryprofile *database_queryprofile(const char *id)
35 {
36     return (struct conf_queryprofile*) 1;
37 }
38
39 static xmlDoc *get_explain_xml(const char *id)
40 {
41     char *dir;
42     char path[256];
43     char ide[256];
44     if (!config || !config->targetprofiles)
45     {
46         yaz_log(YLOG_WARN, "Config must be loaded and specify targetprofiles");
47         return 0;
48     }
49     if (config->targetprofiles->type != Targetprofiles_local)
50     {
51         yaz_log(YLOG_FATAL, "Only supports local type");
52         return 0;
53     }
54     dir = config->targetprofiles->src;
55     urlencode(id, ide);
56     sprintf(path, "%s/%s", dir, ide);
57     yaz_log(YLOG_LOG, "Path: %s", path);
58     return xmlParseFile(path);
59 }
60
61 // Create a new host structure for hostport
62 static struct host *create_host(const char *hostport)
63 {
64     struct addrinfo *addrinfo, hints;
65     struct host *host;
66     char *port;
67     char ipport[128];
68     unsigned char addrbuf[4];
69     int res;
70
71     host = xmalloc(sizeof(struct host));
72     host->hostport = xstrdup(hostport);
73     host->connections = 0;
74
75     if ((port = strchr(hostport, ':')))
76         *(port++) = '\0';
77     else
78         port = "210";
79
80     hints.ai_flags = 0;
81     hints.ai_family = PF_INET;
82     hints.ai_socktype = SOCK_STREAM;
83     hints.ai_protocol = IPPROTO_TCP;
84     hints.ai_addrlen = 0;
85     hints.ai_addr = 0;
86     hints.ai_canonname = 0;
87     hints.ai_next = 0;
88     // This is not robust code. It assumes that getaddrinfo always
89     // returns AF_INET address.
90     if ((res = getaddrinfo(hostport, port, &hints, &addrinfo)))
91     {
92         yaz_log(YLOG_WARN, "Failed to resolve %s: %s", hostport, gai_strerror(res));
93         xfree(host->hostport);
94         xfree(host);
95         return 0;
96     }
97     assert(addrinfo->ai_family == PF_INET);
98     memcpy(addrbuf, &((struct sockaddr_in*)addrinfo->ai_addr)->sin_addr.s_addr, 4);
99     sprintf(ipport, "%u.%u.%u.%u:%s",
100             addrbuf[0], addrbuf[1], addrbuf[2], addrbuf[3], port);
101     host->ipport = xstrdup(ipport);
102     freeaddrinfo(addrinfo);
103     host->next = hosts;
104     hosts = host;
105     return host;
106 }
107
108 static struct host *find_host(const char *hostport)
109 {
110     struct host *p;
111     for (p = hosts; p; p = p->next)
112         if (!strcmp(p->hostport, hostport))
113             return p;
114     return create_host(hostport);
115 }
116
117 static struct database *load_database(const char *id)
118 {
119     xmlDoc *doc = get_explain_xml(id);
120     struct zr_explain *explain;
121     struct conf_retrievalprofile *retrieval;
122     struct conf_queryprofile *query;
123     struct database *db;
124     struct host *host;
125     char hostport[256];
126     char *dbname;
127
128     if (!nmem)
129         nmem = nmem_create();
130     if (doc)
131     {
132         explain = zr_read_xml(nmem, xmlDocGetRootElement(doc));
133         if (!explain)
134             return 0;
135     }
136     if (!(retrieval = database_retrievalprofile(id)) ||
137             !(query = database_queryprofile(id)))
138     {
139         xmlFree(doc);
140         return 0;
141     }
142     if (strlen(id) > 255)
143         return 0;
144     strcpy(hostport, id);
145     if ((dbname = strchr(hostport, '/')))
146         *(dbname++) = '\0';
147     else
148         dbname = "Default";
149     if (!(host = find_host(hostport)))
150         return 0;
151     db = nmem_malloc(nmem, sizeof(*db));
152     memset(db, 0, sizeof(*db));
153     db->host = host;
154     db->url = nmem_strdup(nmem, id);
155     db->name = dbname;
156     db->databases = xmalloc(2 * sizeof(char *));
157     db->databases[0] = nmem_strdup(nmem, dbname);
158     db->databases[1] = 0;
159     db->errors = 0;
160     db->explain = explain;
161     db->qprofile = query;
162     db->rprofile = retrieval;
163     db->next = databases;
164     databases = db;
165
166     return db;
167 }
168
169 // Return a database structure by ID. Load and add to list if necessary
170 // new==1 just means we know it's not in the list
171 struct database *find_database(const char *id, int new)
172 {
173     struct database *p;
174     if (!new)
175     {
176         for (p = databases; p; p = p->next)
177             if (!strcmp(p->url, id))
178                 return p;
179     }
180     return load_database(id);
181 }
182
183 // Needs to be extended with criteria
184 // Cycles through databases, calling a handler function on each.
185 int grep_databases(void *context, void (*fun)(void *context, struct database *db))
186 {
187     struct database *p;
188     int i;
189
190     for (p = databases; p; p = p->next)
191     {
192         (*fun)(context, p);
193         i++;
194     }
195     return i;
196 }
197
198 // This function will most likely vanish when a proper target profile mechanism is
199 // introduced.
200 void load_simpletargets(const char *fn)
201 {
202     FILE *f = fopen(fn, "r");
203     char line[256];
204
205     if (!f)
206     {
207         yaz_log(YLOG_WARN|YLOG_ERRNO, "open %s", fn);
208         exit(1);
209     }
210
211     while (fgets(line, 255, f))
212     {
213         char *url;
214         char *name;
215
216         if (strncmp(line, "target ", 7))
217             continue;
218         line[strlen(line) - 1] = '\0';
219
220         if ((name = strchr(line, ';')))
221             *(name++) = '\0';
222
223         url = line + 7;
224
225         if (!find_database(url, 0))
226             yaz_log(YLOG_WARN, "Unable to load database %s", url);
227     }
228     fclose(f);
229 }
230
231
232 /*
233  * Local variables:
234  * c-basic-offset: 4
235  * indent-tabs-mode: nil
236  * End:
237  * vim: shiftwidth=4 tabstop=8 expandtab
238  */