1 /* $Id: srw-gateway.c,v 1.1 2003-01-06 08:20:28 adam Exp $
2 Copyright (C) 2002-2003
5 This file is part of the YAZ toolkit.
13 * TTL for targets. Separate thread for cleanup.
14 * External target config and aliases.
17 /* note that soapH.h defines _REENTRANT so we check for it here */
25 #include <yaz/srw-util.h>
26 #include <yaz/xmalloc.h>
29 #include <yaz/options.h>
30 #include <yaz/wrbuf.h>
37 long m_expiry_sec; /* date of creation */
59 static cql_transform_t cql_transform_handle = 0;
60 static struct target *target_list = 0;
62 static pthread_mutex_t target_mutex = PTHREAD_MUTEX_INITIALIZER;
63 #define mylock(x) pthread_mutex_lock(x)
64 #define myunlock(x) pthread_mutex_unlock(x)
70 #define ERROR_NO_TARGET -1
71 #define ERROR_BAD_CQL 10
73 static int diag_bib1_to_srw (int code)
148 static int searchRetrieve(void *userinfo,
151 struct xcql__operandType *xQuery,
152 xsd__string *sortKeys,
153 struct xsort__xSortKeysType *xSortKeys,
154 xsd__integer *startRecord,
155 xsd__integer *maximumRecords,
156 xsd__string *recordSchema,
157 xsd__string *recordPacking,
158 struct zs__searchRetrieveResponse *res);
159 static int explain (void *userinfo,
161 struct zs__explainResponse *explainResponse);
163 struct target *target_use (const char *action, const char *query,
164 const char *schema, struct tset **set,
165 struct srw_prop *prop)
168 struct target *l = 0;
170 struct tset **ssp = 0;
174 gettimeofday(&tv, 0);
177 if (strlen(action) >= 80)
178 action = "localhost:210";
179 if (strchr(action, '/') || strchr(action, ':'))
180 strcpy (name, action);
183 strcpy (name, "localhost/");
185 strcat (name, "Default");
187 strcat (name, action);
190 /* See if we have the target and the same query */
192 for (l = target_list; l; l = l->next)
193 if (!l->m_in_use && !strcmp (l->m_name, name))
195 struct tset *s = l->m_sets;
196 for (; s; s = s->m_next)
197 if (!strcmp(s->m_query, query) &&
198 !strcmp (s->m_schema, schema))
205 /* OK, see if we have the target, then.. */
206 for (l = target_list; l; l = l->next)
207 if (!strcmp (l->m_name, name) && !l->m_in_use)
209 struct tset *s = l->m_sets;
210 for (; s; s = s->m_next)
211 /* if m_expiry_sec is 0, the condition is true below */
212 if (s->m_expiry_sec < now)
215 s->m_query = xstrdup("");
217 s->m_schema = xstrdup("");
218 ZOOM_resultset_destroy(s->m_r);
227 /* allright. Have to make a new one */
228 l = xmalloc (sizeof(*l));
229 l->m_name = xstrdup (name);
233 l->next = target_list;
236 for (ssp = &l->m_sets; *ssp; ssp = &(*ssp)->m_next)
238 *ssp = xmalloc(sizeof(**ssp));
240 (*ssp)->m_query = xstrdup("");
241 (*ssp)->m_schema = xstrdup("");
243 (*ssp)->m_expiry_sec = 0;
244 (*ssp)->m_idle_time = (no_sets >= prop->max_sets ? 0 : prop->idle_time);
249 static void target_destroy (struct target *t)
253 mylock(&target_mutex);
255 for (tp = &target_list; *tp; tp = &(*tp)->next)
258 struct tset *s = t->m_sets;
261 struct tset *s_next = s->m_next;
264 ZOOM_resultset_destroy (s->m_r);
271 ZOOM_connection_destroy (t->m_c);
277 myunlock(&target_mutex);
280 static void target_leave (struct target *l)
282 mylock(&target_mutex);
287 struct tset *s = l->m_sets;
288 for (; s; s = s->m_next)
289 yaz_log(LOG_LOG, " set %s q=%s",
290 (s->m_r ? ZOOM_resultset_option_get(s->m_r,"setname"):""),
293 myunlock(&target_mutex);
296 static void standalone(struct soap *soap, const char *host, int port,
297 int max_thr, struct srw_prop *properties)
299 struct soap **soap_thr = malloc (sizeof(*soap_thr) * max_thr);
301 pthread_t *tid = malloc (sizeof(pthread_t) * max_thr);
308 m = soap_bind(soap, 0, port, 100);
311 yaz_log (LOG_WARN|LOG_ERRNO, "Cannot bind to %d", port);
315 for (i = 0; i<max_thr; i++)
320 for (i = 0; i<max_thr; i++)
322 s = soap_accept(soap);
328 soap_thr[i] = soap_new();
338 if (max_thr > 1) /* static mode for max_thr <= 1 */
339 pthread_join(tid[i], 0);
341 soap_end(soap_thr[i]);
345 sprintf (fname, "srw.recv.%05d.log", cno);
347 soap_set_recv_logfile(soap_thr[i], fname);
349 sprintf (fname, "srw.sent.%05d.log", cno);
351 soap_set_sent_logfile(soap_thr[i], fname);
353 sprintf (fname, "srw.test.%05d.log", cno);
355 soap_set_test_logfile(soap_thr[i], fname);
357 yaz_log (LOG_LOG, "starting session %d %ld.%ld.%ld.%ld", cno,
358 (long) (soap->ip>>24) & 0xff,
359 (long) (soap->ip>>16) & 0xff,
360 (long) (soap->ip>>8) & 0xff,
361 (long) soap->ip & 0xff);
363 soap_thr[i]->encodingStyle = 0;
364 soap_thr[i]->socket = s;
365 soap_thr[i]->user = properties;
368 yaz_srw_serve(soap_thr[i], properties,
369 searchRetrieve, explain); /* static mode .. */
371 pthread_create(&tid[i], 0, (void*(*)(void*))soap_serve,
374 yaz_srw_serve(soap_thr[i], properties,
375 searchRetrieve, explain); /* static mode .. */
385 static void reconnect (struct target *t)
389 for (s = t->m_sets; s; s = s->m_next)
391 ZOOM_resultset_destroy(s->m_r);
394 ZOOM_connection_destroy (t->m_c);
396 t->m_c = ZOOM_connection_create (0);
397 ZOOM_connection_connect (t->m_c, t->m_name, 0);
400 int explain (void *userinfo,
402 struct zs__explainResponse *explainResponse)
404 explainResponse->Explain =
406 " <!-- not implemented -->\n"
411 int fetchone(struct soap *soap, struct srw_prop *properties,
412 ZOOM_record zrec, const char *schema,
413 char **rec_data, char **rec_schema)
417 const char *xml_rec = ZOOM_record_get(zrec, "xml", &xml_len);
422 if (!strcmp(schema, "MARC21"))
424 *rec_data = soap_malloc (soap, xml_len+1);
425 memcpy (*rec_data, xml_rec, xml_len);
426 (*rec_data)[xml_len] = 0;
427 *rec_schema = "http://www.loc.gov/marcxml/";
429 else if ((res = xslt_map (properties->maps, "MARC21",
430 schema, xml_rec, xml_len)))
432 int len = xslt_map_result_len(res);
433 char *buf = xslt_map_result_buf(res);
435 *rec_data = soap_malloc (soap, len+1);
436 memcpy (*rec_data, buf, len);
437 (*rec_data)[len] = 0;
439 *rec_schema = soap_malloc(soap,
440 strlen(xslt_map_result_schema(res)) + 1);
441 strcpy(*rec_schema, xslt_map_result_schema(res));
447 *rec_data = soap_malloc(soap, strlen(schema)+1);
448 strcpy(*rec_data, schema);
454 int searchRetrieve(void *userinfo,
457 struct xcql__operandType *xQuery,
458 xsd__string *sortKeys,
459 struct xsort__xSortKeysType *xSortKeys,
460 xsd__integer *startRecord,
461 xsd__integer *maximumRecords,
462 xsd__string *recordSchema,
463 xsd__string *recordPacking,
464 struct zs__searchRetrieveResponse *res)
466 const char *msg = 0, *addinfo = 0;
467 const char *schema = recordSchema ? *recordSchema : "";
468 struct target *t = 0;
471 struct srw_prop *properties = (struct srw_prop*) userinfo;
472 WRBUF wr_log = wrbuf_alloc();
479 yaz_log (LOG_LOG, "HTTP: %s", soap->endpoint);
482 const char *cp = strstr(soap->endpoint, "//");
484 cp = cp+2; /* skip method// */
487 cp = strstr(cp, "/");
496 memcpy (zurl, cp, len);
502 const char *cp = getenv("PATH_INFO");
503 if (cp && cp[0] && cp[1])
511 memcpy (zurl, cp, len);
517 CQL_parser cql_parser = cql_parser_create();
518 int r = cql_parser_string(cql_parser, *query);
522 yaz_log (LOG_LOG, "cql failed: %s", *query);
523 error = ERROR_BAD_CQL;
527 struct cql_node *tree = cql_parser_result(cql_parser);
528 error = cql_transform_buf (cql_transform_handle, tree,
529 pqf_buf, sizeof(pqf_buf));
531 cql_transform_error(cql_transform_handle, &addinfo);
532 cql_parser_destroy(cql_parser);
533 yaz_log (LOG_LOG, "cql OK: %s", *query);
538 struct cql_node *tree = xcql_to_cqlnode(xQuery);
539 yaz_log (LOG_LOG, "xcql");
540 cql_transform_buf (cql_transform_handle, tree,
541 pqf_buf, sizeof(pqf_buf));
542 cql_node_destroy(tree);
546 mylock(&target_mutex);
547 t = target_use (zurl, *pqf_buf ? pqf_buf : 0, schema, &s, properties);
548 myunlock(&target_mutex);
551 if (!error && !t->m_c)
554 if (ZOOM_connection_error (t->m_c, &msg, &addinfo))
556 yaz_log (LOG_LOG, "%s: connect failed", t->m_name);
557 error = ERROR_NO_TARGET;
560 yaz_log (LOG_LOG, "%s: connect ok", t->m_name);
562 if (!error && t->m_c && *pqf_buf)
564 if (properties->optimize_level <=1 ||
565 strcmp (pqf_buf, s->m_query) ||
566 strcmp (schema, s->m_schema))
568 /* not the same query: remove result set .. */
569 ZOOM_resultset_destroy (s->m_r);
574 /* same query: retrieve (instead of doing search) */
575 if (maximumRecords && *maximumRecords > 0)
577 int start = startRecord ? *startRecord : 1;
578 yaz_log (LOG_LOG, "%s: present start=%d count=%d pqf=%s",
579 t->m_name, start, *maximumRecords, pqf_buf);
580 wrbuf_printf (wr_log, "%s: present start=%d count=%d pqf=%s",
581 t->m_name, start, *maximumRecords, pqf_buf);
582 ZOOM_resultset_records (s->m_r, 0, start-1, *maximumRecords);
583 error = ZOOM_connection_error (t->m_c, &msg, &addinfo);
584 if (error == ZOOM_ERROR_CONNECTION_LOST ||
585 error == ZOOM_ERROR_CONNECT)
588 if ((error = ZOOM_connection_error (t->m_c, &msg,
591 yaz_log (LOG_LOG, "%s: connect failed", t->m_name);
592 error = ERROR_NO_TARGET;
597 yaz_log (LOG_LOG, "%s: present failed bib1-code=%d",
599 error = diag_bib1_to_srw(error);
604 yaz_log (LOG_LOG, "%s: matched search pqf=%s",
606 wrbuf_printf (wr_log, "%s: matched search pqf=%s",
610 if (!error && !s->m_r)
611 { /* no result set usable. We must search ... */
613 for (pass = 0; pass < 2; pass++)
616 int start = startRecord ? *startRecord : 1;
617 int count = maximumRecords ? *maximumRecords : 0;
619 sprintf (val, "%d", start-1);
620 ZOOM_connection_option_set (t->m_c, "start", val);
622 sprintf (val, "%d", count);
623 ZOOM_connection_option_set (t->m_c, "count", val);
625 ZOOM_connection_option_set (t->m_c, "preferredRecordSyntax",
629 s->m_query = xstrdup (pqf_buf);
632 s->m_schema = xstrdup (schema);
634 yaz_log (LOG_LOG, "%s: search start=%d count=%d pqf=%s",
635 t->m_name, start, count, pqf_buf);
637 wrbuf_printf (wr_log, "%s: search start=%d count=%d pqf=%s",
638 t->m_name, start, count, pqf_buf);
640 s->m_r = ZOOM_connection_search_pqf (t->m_c, s->m_query);
642 error = ZOOM_connection_error (t->m_c, &msg, &addinfo);
645 if (error != ZOOM_ERROR_CONNECTION_LOST &&
646 error != ZOOM_ERROR_CONNECT)
648 yaz_log (LOG_LOG, "%s: search failed bib1-code=%d",
650 error = diag_bib1_to_srw(error);
653 yaz_log (LOG_LOG, "%s: reconnect (search again)", t->m_name);
658 error = ZOOM_connection_error (t->m_c, &msg, &addinfo);
662 error = ERROR_NO_TARGET;
669 if (!error && t->m_c && s->m_r)
671 yaz_log (LOG_LOG, "%s: %d hits", t->m_name,
672 ZOOM_resultset_size(s->m_r));
673 res->numberOfRecords = ZOOM_resultset_size(s->m_r);
678 int offset = startRecord ? *startRecord -1 : 0;
679 res->records.record =
680 soap_malloc(soap, sizeof(*res->records.record) *
683 for (i = 0; i < *maximumRecords; i++)
686 char *rec_schema = 0;
687 ZOOM_record zrec = ZOOM_resultset_record (s->m_r, offset + i);
694 error = fetchone(soap, properties, zrec, schema,
695 &rec_data, &rec_schema);
701 res->records.record[j] =
702 soap_malloc(soap, sizeof(**res->records.record));
703 res->records.record[j]->recordData = rec_data;
704 res->records.record[j]->recordSchema = rec_schema;
707 res->records.__sizeRecords = j;
710 res->numberOfRecords = 0;
716 ZOOM_resultset_destroy (s->m_r);
719 if (error == ERROR_NO_TARGET)
722 ZOOM_connection_destroy (t->m_c);
727 res->diagnostics.__sizeDiagnostics = 1;
728 res->diagnostics.diagnostic =
729 soap_malloc (soap, sizeof(*res->diagnostics.diagnostic));
730 res->diagnostics.diagnostic[0] =
731 soap_malloc (soap, sizeof(**res->diagnostics.diagnostic));
733 res->diagnostics.diagnostic[0]->code = error;
736 res->diagnostics.diagnostic[0]->details =
737 soap_malloc (soap, strlen(addinfo) + 1);
738 strcpy (res->diagnostics.diagnostic[0]->details, addinfo);
741 res->diagnostics.diagnostic[0]->details = 0;
749 const char *setname = ZOOM_resultset_option_get(s->m_r, "setname");
750 if (strcmp(setname, "default") && s->m_idle_time)
752 res->resultSetId = soap_malloc(soap, strlen(setname));
753 strcpy(res->resultSetId, setname);
754 res->resultSetIdleTime = s->m_idle_time;
755 gettimeofday(&tv, 0);
756 s->m_expiry_sec = res->resultSetIdleTime + tv.tv_sec + 2;
765 if (properties->optimize_level > 0)
770 wrbuf_free(wr_log, 1);
771 if (error == ERROR_NO_TARGET)
772 return soap_receiver_fault(soap, "Cannot connect to Z39.50 target", 0);
776 int main(int argc, char **argv)
783 const char *host = 0;
784 struct srw_prop properties;
786 properties.optimize_level = 2;
787 properties.idle_time = 300;
788 properties.max_sets = 30;
791 while ((ret = options("dO:T:l:hVp:s:x:i:", argv, argc, &arg)) != -2)
799 properties.optimize_level = atoi(arg);
802 no_threads = atoi(arg);
803 if (no_threads < 1 || no_threads > 200)
807 if (!properties.maps)
809 properties.maps = xslt_maps_create();
810 if (xslt_maps_file(properties.maps, arg))
812 fprintf (stderr, "maps file %s could not be opened\n",
819 yaz_log_init_file(arg);
822 puts ("Version: $Id: srw-gateway.c,v 1.1 2003-01-06 08:20:28 adam Exp $"
829 if (cql_transform_handle == 0)
830 cql_transform_handle = cql_transform_open_fname(arg);
833 properties.max_sets = atoi(arg);
836 properties.idle_time = atoi(arg);
839 printf ("srw-gateway [options] <port>\n");
840 printf (" port port for standalone service; If port is omitted, CGI is used.\n");
841 printf (" -O n optimize level. >= 1 cache connections, >=2 cache result sets.\n");
843 printf (" -T n number of threads.\n");
845 printf (" -T unsupported in this version.\n");
847 printf (" -l file log to file (instead of stderr).\n");
848 printf (" -p file PQF properties.\n");
849 printf (" -s file schema maps.\n");
850 printf (" -i time idle time.\n");
851 printf (" -x sets live sets.\n");
852 printf (" -V show version.\n");
855 fprintf (stderr, "srw-gateway: bad option -%s ; use -h for help\n",
862 if (!cql_transform_handle)
863 cql_transform_handle = cql_transform_open_fname("pqf.properties");
864 if (!properties.maps)
866 properties.maps = xslt_maps_create();
867 xslt_maps_file(properties.maps, "maps.xml");
869 soap.encodingStyle = 0;
870 if (port == 0 && getenv("QUERY_STRING"))
872 properties.optimize_level = 0;
874 yaz_log_init_file("srw.log");
875 yaz_log (LOG_LOG, "CGI begin");
877 soap.user = &properties;
879 yaz_srw_serve(&soap, &properties, searchRetrieve, explain);
882 yaz_log (LOG_LOG, "CGI end");
886 if (!cql_transform_handle)
888 fprintf(stderr, "no properties file; use option -p to specify\n");
891 yaz_log (LOG_LOG, "standalone service on port %d", port);
895 standalone(&soap, host, port, no_threads, &properties);
899 fprintf(stderr, "srw-gateway: no port specified. Use -h for help\n");
901 xslt_maps_free(properties.maps);