YAZ  5.23.1
statserv.c
Go to the documentation of this file.
1 /* This file is part of the YAZ toolkit.
2  * Copyright (C) Index Data
3  * See the file LICENSE for details.
4  */
5 
11 #if HAVE_CONFIG_H
12 #include <config.h>
13 #endif
14 
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 
19 #ifdef WIN32
20 #include <process.h>
21 #include <winsock.h>
22 #include <direct.h>
23 #endif
24 
25 #include <yaz/sc.h>
26 #include <yaz/tpath.h>
27 
28 #if HAVE_SYS_TYPES_H
29 #include <sys/types.h>
30 #endif
31 #if HAVE_SYS_WAIT_H
32 #include <sys/wait.h>
33 #endif
34 #if HAVE_UNISTD_H
35 #include <unistd.h>
36 #endif
37 #if HAVE_PWD_H
38 #include <pwd.h>
39 #endif
40 
41 #if YAZ_HAVE_XML2
42 #include <libxml/parser.h>
43 #include <libxml/tree.h>
44 #include <libxml/xinclude.h>
45 #endif
46 
47 #if YAZ_POSIX_THREADS
48 #include <pthread.h>
49 #endif
50 
51 #include <fcntl.h>
52 #include <signal.h>
53 #include <errno.h>
54 
55 #include <yaz/comstack.h>
56 #include <yaz/tcpip.h>
57 #include <yaz/options.h>
58 #include <yaz/errno.h>
59 #ifdef USE_XTIMOSI
60 #include <yaz/xmosi.h>
61 #endif
62 #include <yaz/log.h>
63 #include "eventl.h"
64 #include "session.h"
65 #include <yaz/statserv.h>
66 #include <yaz/daemon.h>
67 #include <yaz/yaz-iconv.h>
68 
69 static IOCHAN pListener = NULL;
70 
71 static char gfs_root_dir[FILENAME_MAX+1];
72 static struct gfs_server *gfs_server_list = 0;
73 static struct gfs_listen *gfs_listen_list = 0;
74 static NMEM gfs_nmem = 0;
75 
76 static char *me = "statserver"; /* log prefix */
77 static char *programname="statserver"; /* full program name */
78 #ifdef WIN32
79 DWORD current_control_tls;
80 static int init_control_tls = 0;
81 #elif YAZ_POSIX_THREADS
82 static pthread_key_t current_control_tls;
83 static int init_control_tls = 0;
84 #else
86 #endif
87 
88 /*
89  * default behavior.
90  */
91 #define STAT_DEFAULT_LOG_LEVEL "server,session,request"
92 
93 int check_options(int argc, char **argv);
95  1, /* dynamic mode */
96  0, /* threaded mode */
97  0, /* one shot (single session) */
98  "", /* no PDUs */
99  "", /* diagnostic output to stderr */
100  PROTO_Z3950, /* default application protocol */
101  900, /* idle timeout (seconds) */
102  64*1024*1024, /* maximum PDU size (approx.) to allow */
103  "default-config", /* configuration name to pass to backend */
104  "", /* set user id */
105  0, /* bend_start handler */
106  0, /* bend_stop handler */
107  check_options, /* Default routine, for checking the run-time arguments */
109  "",
110  0, /* default value for inet deamon */
111  0, /* handle (for service, etc) */
112  0, /* bend_init handle */
113  0, /* bend_close handle */
114 #ifdef WIN32
115  "Z39.50 Server", /* NT Service Name */
116  "Server", /* NT application Name */
117  "", /* NT Service Dependencies */
118  "Z39.50 Server", /* NT Service Display Name */
119 #endif /* WIN32 */
120  "", /* PID fname */
121  0, /* background daemon */
122  "", /* SSL certificate filename */
123  "", /* XML config filename */
124  1 /* keepalive */
125 };
126 
127 static int max_sessions = 0;
128 
129 static int logbits_set = 0;
130 static int log_session = 0; /* one-line logs for session */
131 static int log_sessiondetail = 0; /* more detailed stuff */
132 static int log_server = 0;
133 
135 static void get_logbits(int force)
136 { /* needs to be called after parsing cmd-line args that can set loglevels!*/
137  if (force || !logbits_set)
138  {
139  logbits_set = 1;
140  log_session = yaz_log_module_level("session");
141  log_sessiondetail = yaz_log_module_level("sessiondetail");
142  log_server = yaz_log_module_level("server");
143  }
144 }
145 
146 
147 static int add_listener(char *where, int listen_id);
148 
149 #if YAZ_HAVE_XML2
150 static xmlDocPtr xml_config_doc = 0;
151 #endif
152 
153 #if YAZ_HAVE_XML2
154 static xmlNodePtr xml_config_get_root(void)
155 {
156  xmlNodePtr ptr = 0;
157  if (xml_config_doc)
158  {
159  ptr = xmlDocGetRootElement(xml_config_doc);
160  if (!ptr || ptr->type != XML_ELEMENT_NODE ||
161  strcmp((const char *) ptr->name, "yazgfs"))
162  {
163  yaz_log(YLOG_WARN, "Bad/missing root element for config %s",
164  control_block.xml_config);
165  return 0;
166  }
167  }
168  return ptr;
169 }
170 #endif
171 
172 #if YAZ_HAVE_XML2
173 static char *nmem_dup_xml_content(NMEM n, xmlNodePtr ptr)
174 {
175  unsigned char *cp;
176  xmlNodePtr p;
177  int len = 1; /* start with 1, because of trailing 0 */
178  unsigned char *str;
179  int first = 1; /* whitespace lead flag .. */
180  /* determine length */
181  for (p = ptr; p; p = p->next)
182  {
183  if (p->type == XML_TEXT_NODE)
184  len += xmlStrlen(p->content);
185  }
186  /* now allocate for the string */
187  str = (unsigned char *) nmem_malloc(n, len);
188  *str = '\0'; /* so we can use strcat */
189  for (p = ptr; p; p = p->next)
190  {
191  if (p->type == XML_TEXT_NODE)
192  {
193  cp = p->content;
194  if (first)
195  {
196  while(*cp && yaz_isspace(*cp))
197  cp++;
198  if (*cp)
199  first = 0; /* reset if we got non-whitespace out */
200  }
201  strcat((char *)str, (const char *)cp); /* append */
202  }
203  }
204  /* remove trailing whitespace */
205  cp = strlen((const char *)str) + str;
206  while (cp != str && yaz_isspace(cp[-1]))
207  cp--;
208  *cp = '\0';
209  /* return resulting string */
210  return (char *) str;
211 }
212 #endif
213 
214 #if YAZ_HAVE_XML2
215 static struct gfs_server * gfs_server_new(const char *id)
216 {
217  struct gfs_server *n = (struct gfs_server *)
218  nmem_malloc(gfs_nmem, sizeof(*n));
219  memcpy(&n->cb, &control_block, sizeof(control_block));
220  n->next = 0;
221  n->host = 0;
222  n->listen_ref = 0;
223  n->cql_transform = 0;
224  n->ccl_transform = 0;
225  n->server_node_ptr = 0;
226  n->directory = 0;
227  n->docpath = 0;
228  n->stylesheet = 0;
229  n->client_query_charset = 0;
230  n->id = nmem_strdup_null(gfs_nmem, id);
232  return n;
233 }
234 #endif
235 
236 #if YAZ_HAVE_XML2
237 static struct gfs_listen * gfs_listen_new(const char *id,
238  const char *address)
239 {
240  struct gfs_listen *n = (struct gfs_listen *)
241  nmem_malloc(gfs_nmem, sizeof(*n));
242  if (id)
243  n->id = nmem_strdup(gfs_nmem, id);
244  else
245  n->id = 0;
246  n->next = 0;
247  n->address = nmem_strdup(gfs_nmem, address);
248  return n;
249 }
250 #endif
251 
252 static void gfs_server_chdir(struct gfs_server *gfs)
253 {
254  if (gfs_root_dir[0])
255  {
256  if (chdir(gfs_root_dir))
258  }
259  if (gfs->directory)
260  {
261  if (chdir(gfs->directory))
262  yaz_log(YLOG_WARN|YLOG_ERRNO, "chdir %s",
263  gfs->directory);
264  }
265 }
266 
267 int control_association(association *assoc, const char *host, int force_open)
268 {
269  char vhost[128], *cp;
270  if (host)
271  {
272  strncpy(vhost, host, 127);
273  vhost[127] = '\0';
274  cp = strchr(vhost, ':');
275  if (cp)
276  *cp = '\0';
277  host = vhost;
278  }
279  assoc->server = 0;
280  if (control_block.xml_config[0])
281  {
282  struct gfs_server *gfs;
283  for (gfs = gfs_server_list; gfs; gfs = gfs->next)
284  {
285  int listen_match = 0;
286  int host_match = 0;
287  if ( !gfs->host || (host && gfs->host && !strcmp(host, gfs->host)))
288  host_match = 1;
289  if (!gfs->listen_ref)
290  listen_match = 1;
291  else
292  {
293  int i;
294  for (i = 0; gfs->listen_ref[i] != -1; i++)
295  if (gfs->listen_ref[i] == assoc->client_chan->chan_id)
296  listen_match = 1;
297  }
298  if (listen_match && host_match)
299  {
300  if (force_open ||
301  (assoc->last_control != &gfs->cb && assoc->backend))
302  {
304  if (assoc->backend && assoc->init)
305  {
306  gfs_server_chdir(gfs);
307  (assoc->last_control->bend_close)(assoc->backend);
308  }
309  assoc->backend = 0;
310  xfree(assoc->init);
311  assoc->init = 0;
312  }
313  assoc->server = gfs;
314  assoc->last_control = &gfs->cb;
315  statserv_setcontrol(&gfs->cb);
316 
317  gfs_server_chdir(gfs);
318  break;
319  }
320  }
321  if (!gfs)
322  {
324  assoc->last_control = 0;
325  return 0;
326  }
327  }
328  else
329  {
330  statserv_setcontrol(&control_block);
331  assoc->last_control = &control_block;
332  }
333  yaz_log(YLOG_DEBUG, "server select: config=%s",
334  assoc->last_control->configname);
335 
339  return 1;
340 }
341 
342 #if YAZ_HAVE_XML2
343 static void xml_config_read(const char *base_path)
344 {
345  struct gfs_server **gfsp = &gfs_server_list;
346  struct gfs_listen **gfslp = &gfs_listen_list;
347  xmlNodePtr ptr = xml_config_get_root();
348 
349  if (!ptr)
350  return;
351  for (ptr = ptr->children; ptr; ptr = ptr->next)
352  {
353  struct _xmlAttr *attr;
354  if (ptr->type != XML_ELEMENT_NODE)
355  continue;
356  attr = ptr->properties;
357  if (!strcmp((const char *) ptr->name, "listen"))
358  {
359  /*
360  <listen id="listenerid">tcp:@:9999</listen>
361  */
362  const char *id = 0;
363  const char *address =
364  nmem_dup_xml_content(gfs_nmem, ptr->children);
365  for ( ; attr; attr = attr->next)
366  if (!xmlStrcmp(attr->name, BAD_CAST "id")
367  && attr->children && attr->children->type == XML_TEXT_NODE)
368  id = nmem_dup_xml_content(gfs_nmem, attr->children);
369  if (address)
370  {
371  *gfslp = gfs_listen_new(id, address);
372  gfslp = &(*gfslp)->next;
373  *gfslp = 0; /* make listener list consistent for search */
374  }
375  }
376  else if (!strcmp((const char *) ptr->name, "server"))
377  {
378  xmlNodePtr ptr_server = ptr;
379  xmlNodePtr ptr;
380  const char *listenref = 0;
381  const char *id = 0;
382  struct gfs_server *gfs;
383 
384  for ( ; attr; attr = attr->next)
385  if (!xmlStrcmp(attr->name, BAD_CAST "listenref")
386  && attr->children && attr->children->type == XML_TEXT_NODE)
387  listenref = nmem_dup_xml_content(gfs_nmem, attr->children);
388  else if (!xmlStrcmp(attr->name, BAD_CAST "id")
389  && attr->children
390  && attr->children->type == XML_TEXT_NODE)
391  id = nmem_dup_xml_content(gfs_nmem, attr->children);
392  else
393  yaz_log(YLOG_WARN, "Unknown attribute '%s' for server",
394  attr->name);
395  gfs = *gfsp = gfs_server_new(id);
396  gfs->server_node_ptr = ptr_server;
397  if (listenref)
398  {
399  char **refs;
400  int num, i;
401  nmem_strsplit(gfs_nmem, ",", listenref, &refs, &num);
402  gfs->listen_ref = (int*) nmem_malloc(gfs_nmem,
403  sizeof(int) * (num + 1));
404  for (i = 0; i < num; i++)
405  {
406  int id_no;
407  struct gfs_listen *gl = gfs_listen_list;
408  gfs->listen_ref[i] = 0;
409  for (id_no = 1; gl; gl = gl->next, id_no++)
410  if (gl->id && !strcmp(gl->id, refs[i]))
411  {
412  gfs->listen_ref[i] = id_no;
413  break;
414  }
415  if (!gl)
416  yaz_log(YLOG_WARN, "Non-existent listenref '%s' "
417  "in server config element", refs[i]);
418  }
419  gfs->listen_ref[i] = -1;
420  }
421  for (ptr = ptr_server->children; ptr; ptr = ptr->next)
422  {
423  if (ptr->type != XML_ELEMENT_NODE)
424  continue;
425  if (!strcmp((const char *) ptr->name, "host"))
426  {
427  gfs->host = nmem_dup_xml_content(gfs_nmem,
428  ptr->children);
429  }
430  else if (!strcmp((const char *) ptr->name, "config"))
431  {
432  char fpath[1024];
433  strcpy(gfs->cb.configname,
434  nmem_dup_xml_content(gfs_nmem, ptr->children));
435 
437  base_path, 0, fpath))
438  strcpy(gfs->cb.configname, fpath);
439  }
440  else if (!strcmp((const char *) ptr->name, "cql2rpn"))
441  {
442  char fpath[1024];
443  char *fname = nmem_dup_xml_content(gfs_nmem, ptr->children);
444  if (yaz_filepath_resolve(fname, base_path, 0, fpath))
445  fname = fpath;
446 
448  if (!gfs->cql_transform)
449  {
451  "open CQL transform file '%s'", fname);
452  exit(1);
453  }
454  }
455  else if (!strcmp((const char *) ptr->name, "ccl2rpn"))
456  {
457  char *fname, fpath[1024];
458  FILE *f;
459 
460  fname = nmem_dup_xml_content(gfs_nmem, ptr->children);
461  if (yaz_filepath_resolve(fname, base_path, 0, fpath))
462  fname = fpath;
463 
464  if ((f = fopen(fname, "r")) == 0) {
465  yaz_log(YLOG_FATAL, "can't open CCL file '%s'", fname);
466  exit(1);
467  }
468  gfs->ccl_transform = ccl_qual_mk();
469  ccl_qual_file (gfs->ccl_transform, f);
470  fclose(f);
471  }
472  else if (!strcmp((const char *) ptr->name, "directory"))
473  {
474  gfs->directory =
475  nmem_dup_xml_content(gfs_nmem, ptr->children);
476  }
477  else if (!strcmp((const char *) ptr->name, "docpath"))
478  {
479  gfs->docpath =
480  nmem_dup_xml_content(gfs_nmem, ptr->children);
481  }
482  else if (!strcmp((const char *) ptr->name, "maximumrecordsize"))
483  {
484  gfs->cb.maxrecordsize = atoi(
485  nmem_dup_xml_content(gfs_nmem, ptr->children));
486  }
487  else if (!strcmp((const char *) ptr->name, "stylesheet"))
488  {
489  char *s = nmem_dup_xml_content(gfs_nmem, ptr->children);
490  gfs->stylesheet = (char *)
491  nmem_malloc(gfs_nmem, strlen(s) + 2);
492  sprintf(gfs->stylesheet, "/%s", s);
493  }
494  else if (!strcmp((const char *) ptr->name,
495  "client_query_charset"))
496  {
497  gfs->client_query_charset =
498  nmem_dup_xml_content(gfs_nmem, ptr->children);
499  }
500  else if (!strcmp((const char *) ptr->name, "explain"))
501  {
502  ; /* being processed separately */
503  }
504  else if (!strcmp((const char *) ptr->name, "retrievalinfo"))
505  {
506  if (base_path)
507  yaz_retrieval_set_path(gfs->retrieval, base_path);
508  if (yaz_retrieval_configure(gfs->retrieval, ptr))
509  {
510  yaz_log(YLOG_FATAL, "%s in config %s",
512  control_block.xml_config);
513  exit(1);
514  }
515  }
516  else
517  {
518  yaz_log(YLOG_FATAL, "Unknown element '%s' in config %s",
519  ptr->name, control_block.xml_config);
520  exit(1);
521  }
522  }
523  gfsp = &(*gfsp)->next;
524  }
525  }
526  *gfsp = 0;
527 }
528 #endif
529 
530 static int xml_config_open(void)
531 {
532  const char *last_p;
533  const char *fname = control_block.xml_config;
534  if (!getcwd(gfs_root_dir, FILENAME_MAX))
535  {
536  yaz_log(YLOG_WARN|YLOG_ERRNO, "getcwd failed");
537  gfs_root_dir[0] = '\0';
538  }
539 #ifdef WIN32
540  init_control_tls = 1;
541  current_control_tls = TlsAlloc();
542 #elif YAZ_POSIX_THREADS
543  init_control_tls = 1;
544  pthread_key_create(&current_control_tls, 0);
545 #endif
546 
547  gfs_nmem = nmem_create();
548 #if YAZ_HAVE_XML2
549  if (fname[0] == '\0')
550  return 0;
551 
552  if (!xml_config_doc)
553  {
554  xml_config_doc = xmlParseFile(fname);
555  if (!xml_config_doc)
556  {
557  yaz_log(YLOG_FATAL, "Could not parse %s", fname);
558  return -1;
559  }
560  else
561  {
562  int noSubstitutions = xmlXIncludeProcess(xml_config_doc);
563  if (noSubstitutions == -1)
564  {
565  yaz_log(YLOG_WARN, "XInclude processing failed for config %s",
566  fname);
567  return -1;
568  }
569  }
570  }
571  last_p = strrchr(fname,
572 #ifdef WIN32
573  '\\'
574 #else
575  '/'
576 #endif
577  );
578  if (last_p)
579  {
580  WRBUF base_path = wrbuf_alloc();
581  wrbuf_write(base_path, fname, last_p - fname);
582  xml_config_read(wrbuf_cstr(base_path));
583  wrbuf_destroy(base_path);
584  }
585  else
586  xml_config_read(0);
587 #endif
588  return 0;
589 }
590 
591 static void xml_config_close(void)
592 {
593 #if YAZ_HAVE_XML2
594  if (xml_config_doc)
595  {
596  xmlFreeDoc(xml_config_doc);
597  xml_config_doc = 0;
598  }
599 #endif
600  gfs_server_list = 0;
601  nmem_destroy(gfs_nmem);
602 #ifdef WIN32
603  if (init_control_tls)
604  TlsFree(current_control_tls);
605 #elif YAZ_POSIX_THREADS
606  if (init_control_tls)
607  pthread_key_delete(current_control_tls);
608 #endif
609 }
610 
611 static int xml_config_add_listeners(void)
612 {
613  struct gfs_listen *gfs = gfs_listen_list;
614  int id_no;
615  int ret = 0;
616 
617  for (id_no = 1; gfs; gfs = gfs->next, id_no++)
618  {
619  if (!ret && gfs->address)
620  ret = add_listener(gfs->address, id_no);
621  }
622  return ret;
623 }
624 
625 static void xml_config_bend_start(void)
626 {
627  if (control_block.xml_config[0])
628  {
629  struct gfs_server *gfs = gfs_server_list;
630  for (; gfs; gfs = gfs->next)
631  {
632  yaz_log(YLOG_DEBUG, "xml_config_bend_start config=%s",
633  gfs->cb.configname);
634  statserv_setcontrol(&gfs->cb);
635  if (control_block.bend_start)
636  {
637  gfs_server_chdir(gfs);
638  (control_block.bend_start)(&gfs->cb);
639  }
640  }
641  }
642  else
643  {
644  yaz_log(YLOG_DEBUG, "xml_config_bend_start default config");
645  statserv_setcontrol(&control_block);
646  if (control_block.bend_start)
647  (*control_block.bend_start)(&control_block);
648  }
649 }
650 
651 static void xml_config_bend_stop(void)
652 {
653  if (control_block.xml_config[0])
654  {
655  struct gfs_server *gfs = gfs_server_list;
656  for (; gfs; gfs = gfs->next)
657  {
658  yaz_log(YLOG_DEBUG, "xml_config_bend_stop config=%s",
659  gfs->cb.configname);
660  statserv_setcontrol(&gfs->cb);
661  if (control_block.bend_stop)
662  (control_block.bend_stop)(&gfs->cb);
663  }
664  }
665  else
666  {
667  yaz_log(YLOG_DEBUG, "xml_config_bend_stop default config");
668  statserv_setcontrol(&control_block);
669  if (control_block.bend_stop)
670  (*control_block.bend_stop)(&control_block);
671  }
672 }
673 
674 static void remove_listeners(void);
675 
676 /*
677  * handle incoming connect requests.
678  * The dynamic mode is a bit tricky mostly because we want to avoid
679  * doing all of the listening and accepting in the parent - it's
680  * safer that way.
681  */
682 #ifdef WIN32
683 
684 typedef struct _ThreadList ThreadList;
685 
686 struct _ThreadList
687 {
688  HANDLE hThread;
689  IOCHAN pIOChannel;
690  ThreadList *pNext;
691 };
692 
693 static ThreadList *pFirstThread;
694 static CRITICAL_SECTION Thread_CritSect;
695 static BOOL bInitialized = FALSE;
696 
697 static void ThreadList_Initialize()
698 {
699  /* Initialize the critical Sections */
700  InitializeCriticalSection(&Thread_CritSect);
701 
702  /* Set the first thraed */
703  pFirstThread = NULL;
704 
705  /* we have been initialized */
706  bInitialized = TRUE;
707 }
708 
709 static void statserv_add(HANDLE hThread, IOCHAN pIOChannel)
710 {
711  /* Only one thread can go through this section at a time */
712  EnterCriticalSection(&Thread_CritSect);
713 
714  {
715  /* Lets create our new object */
716  ThreadList *pNewThread = (ThreadList *)malloc(sizeof(ThreadList));
717  pNewThread->hThread = hThread;
718  pNewThread->pIOChannel = pIOChannel;
719  pNewThread->pNext = pFirstThread;
720  pFirstThread = pNewThread;
721 
722  /* Lets let somebody else create a new object now */
723  LeaveCriticalSection(&Thread_CritSect);
724  }
725 }
726 
727 void statserv_remove(IOCHAN pIOChannel)
728 {
729  /* Only one thread can go through this section at a time */
730  EnterCriticalSection(&Thread_CritSect);
731 
732  {
733  ThreadList *pCurrentThread = pFirstThread;
734  ThreadList *pNextThread;
735  ThreadList *pPrevThread =NULL;
736 
737  /* Step through all the threads */
738  for (; pCurrentThread != NULL; pCurrentThread = pNextThread)
739  {
740  /* We only need to compare on the IO Channel */
741  if (pCurrentThread->pIOChannel == pIOChannel)
742  {
743  /* We have found the thread we want to delete */
744  /* First of all reset the next pointers */
745  if (pPrevThread == NULL)
746  pFirstThread = pCurrentThread->pNext;
747  else
748  pPrevThread->pNext = pCurrentThread->pNext;
749 
750  /* All we need todo now is delete the memory */
751  free(pCurrentThread);
752 
753  /* No need to look at any more threads */
754  pNextThread = NULL;
755  }
756  else
757  {
758  /* We need to look at another thread */
759  pNextThread = pCurrentThread->pNext;
760  pPrevThread = pCurrentThread;
761  }
762  }
763 
764  /* Lets let somebody else remove an object now */
765  LeaveCriticalSection(&Thread_CritSect);
766  }
767 }
768 
769 /* WIN32 statserv_closedown */
770 static void statserv_closedown()
771 {
772  /* Shouldn't do anything if we are not initialized */
773  if (bInitialized)
774  {
775  int iHandles = 0;
776  HANDLE *pThreadHandles = NULL;
777 
778  /* We need to stop threads adding and removing while we */
779  /* start the closedown process */
780  EnterCriticalSection(&Thread_CritSect);
781 
782  {
783  /* We have exclusive access to the thread stuff now */
784  /* Y didn't i use a semaphore - Oh well never mind */
785  ThreadList *pCurrentThread = pFirstThread;
786 
787  /* Before we do anything else, we need to shutdown the listener */
788  if (pListener != NULL)
789  iochan_destroy(pListener);
790 
791  for (; pCurrentThread != NULL; pCurrentThread = pCurrentThread->pNext)
792  {
793  /* Just destroy the IOCHAN, that should do the trick */
794  iochan_destroy(pCurrentThread->pIOChannel);
795  closesocket(pCurrentThread->pIOChannel->fd);
796 
797  /* Keep a running count of our handles */
798  iHandles++;
799  }
800 
801  if (iHandles > 0)
802  {
803  HANDLE *pCurrentHandle ;
804 
805  /* Allocate the thread handle array */
806  pThreadHandles = (HANDLE *)malloc(sizeof(HANDLE) * iHandles);
807  pCurrentHandle = pThreadHandles;
808 
809  for (pCurrentThread = pFirstThread;
810  pCurrentThread != NULL;
811  pCurrentThread = pCurrentThread->pNext, pCurrentHandle++)
812  {
813  /* Just the handle */
814  *pCurrentHandle = pCurrentThread->hThread;
815  }
816  }
817 
818  /* We can now leave the critical section */
819  LeaveCriticalSection(&Thread_CritSect);
820  }
821 
822  /* Now we can really do something */
823  if (iHandles > 0)
824  {
825  yaz_log(log_server, "waiting for %d to die", iHandles);
826  /* This will now wait, until all the threads close */
827  WaitForMultipleObjects(iHandles, pThreadHandles, TRUE, INFINITE);
828 
829  /* Free the memory we allocated for the handle array */
830  free(pThreadHandles);
831  }
832 
834  /* No longer require the critical section, since all threads are dead */
835  DeleteCriticalSection(&Thread_CritSect);
836  }
838 }
839 
840 void __cdecl event_loop_thread(IOCHAN iochan)
841 {
842  iochan_event_loop(&iochan, 0);
843 }
844 
845 /* WIN32 listener */
846 static void listener(IOCHAN h, int event)
847 {
848  COMSTACK line = (COMSTACK) iochan_getdata(h);
849  IOCHAN parent_chan = line->user;
850  association *newas;
851  int res;
852  HANDLE newHandle;
853 
854  if (event == EVENT_INPUT)
855  {
856  COMSTACK new_line;
857  IOCHAN new_chan;
858 
859  if ((res = cs_listen(line, 0, 0)) < 0)
860  {
861  yaz_log(YLOG_FATAL|YLOG_ERRNO, "cs_listen failed");
862  return;
863  }
864  else if (res == 1)
865  return; /* incomplete */
866  yaz_log(YLOG_DEBUG, "listen ok");
867  new_line = cs_accept(line);
868  if (!new_line)
869  {
870  yaz_log(YLOG_FATAL, "Accept failed.");
871  return;
872  }
873  yaz_log(YLOG_DEBUG, "Accept ok");
874 
875  if (!(new_chan = iochan_create(cs_fileno(new_line), ir_session,
876  EVENT_INPUT, parent_chan->chan_id)))
877  {
878  yaz_log(YLOG_FATAL, "Failed to create iochan");
879  iochan_destroy(h);
880  return;
881  }
882 
883  yaz_log(YLOG_DEBUG, "Creating association");
884  if (!(newas = create_association(new_chan, new_line,
885  control_block.apdufile)))
886  {
887  yaz_log(YLOG_FATAL, "Failed to create new assoc.");
888  iochan_destroy(h);
889  return;
890  }
891  newas->cs_get_mask = EVENT_INPUT;
892  newas->cs_put_mask = 0;
893  newas->cs_accept_mask = 0;
894 
895  yaz_log(YLOG_DEBUG, "Setting timeout %d", control_block.idle_timeout);
896  iochan_setdata(new_chan, newas);
897  iochan_settimeout(new_chan, 60);
898 
899  /* Now what we need todo is create a new thread with this iochan as
900  the parameter */
901  newHandle = (HANDLE) _beginthread(event_loop_thread, 0, new_chan);
902  if (newHandle == (HANDLE) -1)
903  {
904 
905  yaz_log(YLOG_FATAL|YLOG_ERRNO, "Failed to create new thread.");
906  iochan_destroy(h);
907  return;
908  }
909  /* We successfully created the thread, so add it to the list */
910  statserv_add(newHandle, new_chan);
911 
912  yaz_log(YLOG_DEBUG, "Created new thread, id = %ld iochan %p",(long) newHandle, new_chan);
913  iochan_setflags(h, EVENT_INPUT | EVENT_EXCEPT); /* reset listener */
914  }
915  else
916  {
917  yaz_log(YLOG_FATAL, "Bad event on listener.");
918  iochan_destroy(h);
919  return;
920  }
921 }
922 
923 #else /* ! WIN32 */
924 
925 /* To save having an #ifdef in event_loop we need to
926  define this empty function
927 */
928 void statserv_remove(IOCHAN pIOChannel)
929 {
930 }
931 
932 static void statserv_closedown(void)
933 {
934  IOCHAN p;
935 
937  for (p = pListener; p; p = p->next)
938  {
939  iochan_destroy(p);
940  }
942 }
943 
944 static void *new_session(void *vp);
945 static int no_sessions = 0;
946 
947 /* UNIX listener */
948 static void listener(IOCHAN h, int event)
949 {
950  COMSTACK line = (COMSTACK) iochan_getdata(h);
951  int res;
952 
953  if (event == EVENT_INPUT)
954  {
955  COMSTACK new_line;
956  if ((res = cs_listen_check(line, 0, 0, control_block.check_ip,
957  control_block.daemon_name)) < 0)
958  {
959  yaz_log(YLOG_WARN|YLOG_ERRNO, "cs_listen failed");
960  return;
961  }
962  else if (res == 1)
963  {
964  yaz_log(YLOG_WARN, "cs_listen incomplete");
965  return;
966  }
967  new_line = cs_accept(line);
968  if (!new_line)
969  {
970  yaz_log(YLOG_FATAL, "Accept failed.");
971  iochan_setflags(h, EVENT_INPUT | EVENT_EXCEPT); /* reset listener */
972  return;
973  }
974 
975  if (control_block.one_shot)
977 
978  yaz_log(log_sessiondetail, "Connect from %s", cs_addrstr(new_line));
979 
980  no_sessions++;
981  if (control_block.dynamic)
982  {
983  if ((res = fork()) < 0)
984  {
985  yaz_log(YLOG_FATAL|YLOG_ERRNO, "fork");
986  iochan_destroy(h);
987  return;
988  }
989  else if (res == 0) /* child */
990  {
991  char nbuf[100];
992  IOCHAN pp;
993 
994  for (pp = pListener; pp; pp = iochan_getnext(pp))
995  {
997  cs_close(l);
998  iochan_destroy(pp);
999  }
1000  sprintf(nbuf, "%s(%d)", me, no_sessions);
1001  yaz_log_init_prefix(nbuf);
1002  /* ensure that bend_stop is not called when each child exits -
1003  only for the main process .. */
1004  control_block.bend_stop = 0;
1005  }
1006  else /* parent */
1007  {
1008  cs_close(new_line);
1009  return;
1010  }
1011  }
1012 
1013  if (control_block.threads)
1014  {
1015 #if YAZ_POSIX_THREADS
1016  pthread_t child_thread;
1017  pthread_create(&child_thread, 0, new_session, new_line);
1018  pthread_detach(child_thread);
1019 #else
1020  new_session(new_line);
1021 #endif
1022  }
1023  else
1024  new_session(new_line);
1025  }
1026  else if (event == EVENT_TIMEOUT)
1027  {
1028  yaz_log(log_server, "Shutting down listener.");
1029  iochan_destroy(h);
1030  }
1031  else
1032  {
1033  yaz_log(YLOG_FATAL, "Bad event on listener.");
1034  iochan_destroy(h);
1035  }
1036 }
1037 
1038 static void *new_session(void *vp)
1039 {
1040  const char *a;
1041  association *newas;
1042  IOCHAN new_chan;
1043  COMSTACK new_line = (COMSTACK) vp;
1044  IOCHAN parent_chan = (IOCHAN) new_line->user;
1045 
1046  unsigned cs_get_mask, cs_accept_mask, mask =
1047  ((new_line->io_pending & CS_WANT_WRITE) ? EVENT_OUTPUT : 0) |
1048  ((new_line->io_pending & CS_WANT_READ) ? EVENT_INPUT : 0);
1049 
1050  if (mask)
1051  {
1052  cs_accept_mask = mask; /* accept didn't complete */
1053  cs_get_mask = 0;
1054  }
1055  else
1056  {
1057  cs_accept_mask = 0; /* accept completed. */
1058  cs_get_mask = mask = EVENT_INPUT;
1059  }
1060 
1061  if (!(new_chan = iochan_create(cs_fileno(new_line), ir_session, mask,
1062  parent_chan->chan_id)))
1063  {
1064  yaz_log(YLOG_FATAL, "Failed to create iochan");
1065  return 0;
1066  }
1067  if (!(newas = create_association(new_chan, new_line,
1068  control_block.apdufile)))
1069  {
1070  yaz_log(YLOG_FATAL, "Failed to create new assoc.");
1071  return 0;
1072  }
1073  newas->cs_accept_mask = cs_accept_mask;
1074  newas->cs_get_mask = cs_get_mask;
1075 
1076  iochan_setdata(new_chan, newas);
1077  iochan_settimeout(new_chan, 60);
1078 #if 1
1079  a = cs_addrstr(new_line);
1080 #else
1081  a = 0;
1082 #endif
1084  yaz_log(log_session, "Session - OK %d %s PID=%ld",
1085  no_sessions, a ? a : "[Unknown]", (long) getpid());
1087  control_block.one_shot = 1;
1088  if (control_block.threads)
1089  {
1090  iochan_event_loop(&new_chan, 0);
1091  }
1092  else
1093  {
1094  new_chan->next = pListener;
1095  pListener = new_chan;
1096  }
1097  return 0;
1098 }
1099 
1100 /* UNIX */
1101 #endif
1102 
1103 static void inetd_connection(int what)
1104 {
1105  COMSTACK line;
1106  IOCHAN chan;
1107  association *assoc;
1108  const char *addr;
1109 
1110  if ((line = cs_createbysocket(0, tcpip_type, 0, what)))
1111  {
1112  if ((chan = iochan_create(cs_fileno(line), ir_session, EVENT_INPUT,
1113  0)))
1114  {
1115  if ((assoc = create_association(chan, line,
1116  control_block.apdufile)))
1117  {
1118  iochan_setdata(chan, assoc);
1119  iochan_settimeout(chan, 60);
1120  addr = cs_addrstr(line);
1121  yaz_log(log_sessiondetail, "Inetd association from %s",
1122  addr ? addr : "[UNKNOWN]");
1123  assoc->cs_get_mask = EVENT_INPUT;
1124  }
1125  else
1126  {
1127  yaz_log(YLOG_FATAL, "Failed to create association structure");
1128  }
1129  chan->next = pListener;
1130  pListener = chan;
1131  }
1132  else
1133  {
1134  yaz_log(YLOG_FATAL, "Failed to create iochan");
1135  }
1136  }
1137  else
1138  {
1139  yaz_log(YLOG_ERRNO|YLOG_FATAL, "Failed to create comstack on socket 0");
1140  }
1141 }
1142 
1143 /*
1144  * Set up a listening endpoint, and give it to the event-handler.
1145  */
1146 static int add_listener(char *where, int listen_id)
1147 {
1148  COMSTACK l;
1149  void *ap;
1150  IOCHAN lst = NULL;
1151  const char *mode;
1152 
1153  if (control_block.dynamic)
1154  mode = "dynamic";
1155  else if (control_block.threads)
1156  mode = "threaded";
1157  else
1158  mode = "static";
1159 
1160  yaz_log(log_server, "Adding %s listener on %s id=%d PID=%ld", mode, where,
1161  listen_id, (long) getpid());
1162 
1163  l = cs_create_host(where, 2, &ap);
1164  if (!l)
1165  {
1166  yaz_log(YLOG_FATAL, "Failed to listen on %s", where);
1167  return -1;
1168  }
1169  if (*control_block.cert_fname)
1170  cs_set_ssl_certificate_file(l, control_block.cert_fname);
1171 
1172  if (cs_bind(l, ap, CS_SERVER) < 0)
1173  {
1174  if (cs_errno(l) == CSYSERR)
1175  yaz_log(YLOG_FATAL|YLOG_ERRNO, "Failed to bind to %s", where);
1176  else
1177  yaz_log(YLOG_FATAL, "Failed to bind to %s: %s", where,
1178  cs_strerror(l));
1179  cs_close(l);
1180  return -1;
1181  }
1182  if (!(lst = iochan_create(cs_fileno(l), listener, EVENT_INPUT |
1183  EVENT_EXCEPT, listen_id)))
1184  {
1185  yaz_log(YLOG_FATAL|YLOG_ERRNO, "Failed to create IOCHAN-type");
1186  cs_close(l);
1187  return -1;
1188  }
1189  iochan_setdata(lst, l); /* user-defined data for listener is COMSTACK */
1190  l->user = lst; /* user-defined data for COMSTACK is listener chan */
1191 
1192  /* Add listener to chain */
1193  lst->next = pListener;
1194  pListener = lst;
1195  return 0; /* OK */
1196 }
1197 
1198 static void remove_listeners(void)
1199 {
1200  IOCHAN l = pListener;
1201  for (; l; l = l->next)
1202  iochan_destroy(l);
1203 }
1204 
1205 #ifndef WIN32
1206 /* UNIX only (for windows we don't need to catch the signals) */
1207 static void catchchld(int num)
1208 {
1209  while (waitpid(-1, 0, WNOHANG) > 0)
1210  ;
1211  signal(SIGCHLD, catchchld);
1212 }
1213 #endif
1214 
1216 {
1217 #ifdef WIN32
1218  if (init_control_tls)
1219  return (statserv_options_block *) TlsGetValue(current_control_tls);
1220  else
1221  return &control_block;
1222 #elif YAZ_POSIX_THREADS
1223  if (init_control_tls)
1224  return (statserv_options_block *)
1225  pthread_getspecific(current_control_tls);
1226  else
1227  return &control_block;
1228 #else
1229  if (current_control_block)
1230  return current_control_block;
1231  return &control_block;
1232 #endif
1233 }
1234 
1236 {
1237  if (gfs_root_dir[0])
1238  {
1239  if (chdir(gfs_root_dir))
1240  yaz_log(YLOG_WARN|YLOG_ERRNO, "chdir %s", gfs_root_dir);
1241  }
1242 #ifdef WIN32
1243  if (init_control_tls)
1244  TlsSetValue(current_control_tls, block);
1245 #elif YAZ_POSIX_THREADS
1246  if (init_control_tls)
1247  pthread_setspecific(current_control_tls, block);
1248 #else
1249  current_control_block = block;
1250 #endif
1251 }
1252 
1253 static void statserv_reset(void)
1254 {
1255 }
1256 
1257 static int sig_received = 0;
1258 
1259 #ifndef WIN32
1260 static void normal_stop_handler(int num)
1261 {
1262  sig_received = num;
1263 }
1264 #endif
1265 
1266 static void daemon_handler(void *data)
1267 {
1268  IOCHAN *pListener = data;
1269  iochan_event_loop(pListener, &sig_received);
1270 }
1271 
1272 static void show_version(void)
1273 {
1274  char vstr[20], sha1_str[41];
1275 
1276  yaz_version(vstr, sha1_str);
1277  printf("YAZ version: %s %s\n", YAZ_VERSION, YAZ_VERSION_SHA1);
1278  if (strcmp(sha1_str, YAZ_VERSION_SHA1))
1279  printf("YAZ DLL/SO: %s %s\n", vstr, sha1_str);
1280  exit(0);
1281 }
1282 
1283 static int statserv_sc_main(yaz_sc_t s, int argc, char **argv)
1284 {
1285  char sep;
1286 #ifdef WIN32
1287  /* We need to initialize the thread list */
1288  ThreadList_Initialize();
1289 /* WIN32 */
1290 #endif
1291 
1292 
1293 #ifdef WIN32
1294  sep = '\\';
1295 #else
1296  sep = '/';
1297 #endif
1298  if ((me = strrchr(argv[0], sep)))
1299  me++; /* get the basename */
1300  else
1301  me = argv[0];
1302  programname = argv[0];
1303 
1304  if (control_block.options_func(argc, argv))
1305  return 1;
1306 
1307  if (xml_config_open())
1308  return 1;
1309 
1311 
1312  if (control_block.inetd)
1313  {
1314 #ifdef WIN32
1315  ; /* no inetd on Windows */
1316 #else
1317  inetd_connection(control_block.default_proto);
1318 #endif
1319  }
1320  else
1321  {
1323  return 1;
1324 
1325  if (!pListener)
1326  add_listener("tcp:@:9999", 0);
1327 
1328 #ifndef WIN32
1329  if (control_block.dynamic)
1330  signal(SIGCHLD, catchchld);
1331 #endif
1332  }
1333  if (pListener == NULL)
1334  return 1;
1335  if (s)
1336  yaz_sc_running(s);
1337 
1338 #ifndef WIN32
1339  signal(SIGTERM, normal_stop_handler);
1340 #endif
1342  (control_block.background ? YAZ_DAEMON_FORK : 0),
1343  daemon_handler, &pListener,
1344  *control_block.pid_fname ? control_block.pid_fname : 0,
1345  *control_block.setuid ? control_block.setuid : 0);
1346 #ifndef WIN32
1347  if (sig_received)
1348  yaz_log(YLOG_LOG, "Received SIGTERM PID=%ld", (long) getpid());
1349 #endif
1350  return 0;
1351 }
1352 
1353 static void option_copy(char *dst, const char *src)
1354 {
1355  strncpy(dst, src ? src : "", BEND_NAME_MAX-1);
1356  dst[BEND_NAME_MAX-1] = '\0';
1357 }
1358 
1359 int check_options(int argc, char **argv)
1360 {
1361  int ret = 0, r;
1362  char *arg;
1363 
1365 
1366  get_logbits(1);
1367 
1368  while ((ret = options("1a:iszSTl:v:u:c:w:t:k:Kd:A:p:DC:f:m:r:V",
1369  argv, argc, &arg)) != -2)
1370  {
1371  switch (ret)
1372  {
1373  case 0:
1374  if (add_listener(arg, 0))
1375  return 1; /* failed to create listener */
1376  break;
1377  case '1':
1378  control_block.one_shot = 1;
1379  control_block.dynamic = 0;
1380  break;
1381  case 'z':
1382  control_block.default_proto = PROTO_Z3950;
1383  break;
1384  case 's':
1385  fprintf(stderr, "%s: SR protocol no longer supported\n", me);
1386  exit(1);
1387  break;
1388  case 'S':
1389  control_block.dynamic = 0;
1390  break;
1391  case 'T':
1392 #if YAZ_POSIX_THREADS
1393  control_block.dynamic = 0;
1394  control_block.threads = 1;
1395 #else
1396  fprintf(stderr, "%s: Threaded mode not available.\n", me);
1397  return 1;
1398 #endif
1399  break;
1400  case 'l':
1401  option_copy(control_block.logfile, arg);
1402  yaz_log_init_file(control_block.logfile);
1403  break;
1404  case 'm':
1405  if (!arg) {
1406  fprintf(stderr, "%s: Specify time format for log file.\n", me);
1407  return(1);
1408  }
1409  yaz_log_time_format(arg);
1410  break;
1411  case 'v':
1413  get_logbits(1);
1414  break;
1415  case 'a':
1416  option_copy(control_block.apdufile, arg);
1417  break;
1418  case 'u':
1419  option_copy(control_block.setuid, arg);
1420  break;
1421  case 'c':
1422  option_copy(control_block.configname, arg);
1423  break;
1424  case 'C':
1425  option_copy(control_block.cert_fname, arg);
1426  break;
1427  case 'd':
1428  option_copy(control_block.daemon_name, arg);
1429  break;
1430  case 't':
1431  if (!arg || !(r = atoi(arg)))
1432  {
1433  fprintf(stderr, "%s: Specify positive timeout for -t.\n", me);
1434  return(1);
1435  }
1436  control_block.idle_timeout = strchr(arg, 's') ? r : 60 * r;
1437  break;
1438  case 'k':
1439  if (!arg || !(r = atoi(arg)))
1440  {
1441  fprintf(stderr, "%s: Specify positive size for -k.\n", me);
1442  return(1);
1443  }
1444  control_block.maxrecordsize = r * 1024;
1445  break;
1446  case 'K':
1447  control_block.keepalive = 0;
1448  break;
1449  case 'i':
1450  control_block.inetd = 1;
1451  break;
1452  case 'w':
1453  if (chdir(arg))
1454  {
1455  perror(arg);
1456  return 1;
1457  }
1458  break;
1459  case 'A':
1460  max_sessions = atoi(arg);
1461  break;
1462  case 'p':
1463  option_copy(control_block.pid_fname, arg);
1464  break;
1465  case 'f':
1466 #if YAZ_HAVE_XML2
1467  option_copy(control_block.xml_config, arg);
1468 #else
1469  fprintf(stderr, "%s: Option -f unsupported since YAZ is compiled without Libxml2 support\n", me);
1470  exit(1);
1471 #endif
1472  break;
1473  case 'D':
1474  control_block.background = 1;
1475  break;
1476  case 'r':
1477  if (!arg || !(r = atoi(arg)))
1478  {
1479  fprintf(stderr, "%s: Specify positive size for -r.\n", me);
1480  return(1);
1481  }
1482  yaz_log_init_max_size(r * 1024);
1483  break;
1484  case 'V':
1485  show_version();
1486  break;
1487  default:
1488  fprintf(stderr, "Usage: %s [ -a <pdufile> -v <loglevel>"
1489  " -l <logfile> -u <user> -c <config> -t <minutes>"
1490  " -k <kilobytes> -d <daemon> -p <pidfile> -C certfile"
1491  " -zKiDSTV1 -m <time-format> -w <directory> <listener-addr>... ]\n", me);
1492  return 1;
1493  }
1494  }
1495  return 0;
1496 }
1497 
1499 {
1501  statserv_reset();
1502 }
1503 
1504 int statserv_main(int argc, char **argv,
1506  void (*bend_close)(void *handle))
1507 {
1508  int ret;
1509  struct statserv_options_block *cb = &control_block;
1510 
1511  /* control block does not have service_name member on Unix */
1512  yaz_sc_t s = yaz_sc_create(
1513 #ifdef WIN32
1514  cb->service_name, cb->service_display_name
1515 #else
1516  0, 0
1517 #endif
1518  );
1519 
1520  cb->bend_init = bend_init;
1521  cb->bend_close = bend_close;
1522 
1523  ret = yaz_sc_program(s, argc, argv, statserv_sc_main, statserv_sc_stop);
1524  yaz_sc_destroy(&s);
1525  return ret;
1526 }
1527 
1528 /*
1529  * Local variables:
1530  * c-basic-offset: 4
1531  * c-file-style: "Stroustrup"
1532  * indent-tabs-mode: nil
1533  * End:
1534  * vim: shiftwidth=4 tabstop=8 expandtab
1535  */
1536 
#define EVENT_INPUT
Definition: eventl.h:49
void(* bend_start)(struct statserv_options_block *p)
Definition: backend.h:342
void nmem_strsplit(NMEM nmem, const char *delim, const char *dstr, char ***darray, int *num)
allocates sub strings out of string using certain delimitors
Definition: nmemsdup.c:61
static void normal_stop_handler(int num)
Definition: statserv.c:1260
IOCHAN client_chan
Definition: session.h:108
struct comstack * COMSTACK
Definition: comstack.h:43
static int log_server
Definition: statserv.c:132
#define iochan_setflags(i, d)
Definition: eventl.h:70
#define CS_SERVER
Definition: comstack.h:78
Header for TCP/IP + SSL COMSTACK.
int chan_id
Definition: eventl.h:61
static void daemon_handler(void *data)
Definition: statserv.c:1266
int(* check_ip)(void *cd, const char *addr, int len, int type)
Definition: backend.h:345
yaz_retrieval_t yaz_retrieval_create()
Definition: retrieval.c:73
#define YLOG_DEBUG
log level: debugging
Definition: log.h:44
static int logbits_set
Definition: statserv.c:129
void * malloc(YYSIZE_T)
yaz_retrieval_t retrieval
Definition: session.h:55
Unix daemon management.
char logfile[BEND_NAME_MAX]
Definition: backend.h:336
#define cs_accept(handle)
Definition: comstack.h:98
void statserv_remove(IOCHAN pIOChannel)
Definition: statserv.c:928
int cs_get_mask
Definition: session.h:129
#define EVENT_OUTPUT
Definition: eventl.h:50
static void statserv_reset(void)
Definition: statserv.c:1253
struct iochan * IOCHAN
Header for errno utilities.
static void remove_listeners(void)
Definition: statserv.c:1198
yaz_sc_t yaz_sc_create(const char *service_name, const char *display_name)
creates service handle
Definition: sc.c:44
char cert_fname[BEND_NAME_MAX]
Definition: backend.h:362
const char * yaz_retrieval_get_error(yaz_retrieval_t p)
Definition: retrieval.c:379
#define cs_close(handle)
Definition: comstack.h:99
#define YAZ_VERSION_SHA1
SHA1 ID for YAZ (Git)
Definition: yaz-version.h:45
int iochan_event_loop(IOCHAN *iochans, int *watch_sig)
Definition: eventl.c:81
const char * cs_strerror(COMSTACK h)
Definition: comstack.c:43
static void xml_config_read(const char *base_path)
Definition: statserv.c:343
const char * wrbuf_cstr(WRBUF b)
returns WRBUF content as C-string
Definition: wrbuf.c:281
void nmem_destroy(NMEM n)
destroys NMEM handle and memory associated with it
Definition: nmem.c:204
static char * me
Definition: statserv.c:76
Internal header for GFS.
static struct gfs_server * gfs_server_new(const char *id)
Definition: statserv.c:215
char pid_fname[BEND_NAME_MAX]
Definition: backend.h:360
void statserv_setcontrol(statserv_options_block *block)
Definition: statserv.c:1235
void yaz_log_time_format(const char *fmt)
sets time format for log mesages
Definition: log.c:527
static IOCHAN pListener
Definition: statserv.c:69
struct gfs_server * next
Definition: session.h:56
static char * programname
Definition: statserv.c:77
static void xml_config_close(void)
Definition: statserv.c:591
void * backend
Definition: session.h:119
static void xml_config_bend_stop(void)
Definition: statserv.c:651
void yaz_log_init_max_size(int mx)
sets limit in bytes for size for log file
Definition: log.c:288
result for init handler (must be filled by handler)
Definition: backend.h:320
#define cs_listen(handle, ap, al)
Definition: comstack.h:96
#define CS_WANT_WRITE
Definition: comstack.h:115
void * nmem_malloc(NMEM n, size_t size)
allocates memory block on NMEM handle
Definition: nmem.c:145
static int no_sessions
Definition: statserv.c:945
void wrbuf_write(WRBUF b, const char *buf, size_t size)
append constant size buffer to WRBUF
Definition: wrbuf.c:68
#define YLOG_LOG
log level: log (regular)
Definition: log.h:48
string buffer
Definition: wrbuf.h:42
void free(void *)
#define CSYSERR
Definition: comstack.h:156
#define STAT_DEFAULT_LOG_LEVEL
Definition: statserv.c:91
Information for the Init handler.
Definition: backend.h:251
CCL_bibset ccl_qual_mk(void)
creates Bibset
Definition: cclqual.c:210
int yaz_sc_program(yaz_sc_t s, int argc, char **argv, int(*sc_main)(yaz_sc_t s, int argc, char **argv), void(*sc_stop)(yaz_sc_t s))
registers service controlled program
Definition: sc.c:233
Header for Windows Service Control utility.
int check_options(int argc, char **argv)
Definition: statserv.c:1359
void(* bend_stop)(struct statserv_options_block *p)
Definition: backend.h:343
void * server_node_ptr
Definition: session.h:50
char daemon_name[BEND_NAME_MAX]
Definition: backend.h:346
#define cs_listen_check(handle, ap, al, cf, cd)
Definition: comstack.h:97
#define YLOG_FATAL
log level: fatal
Definition: log.h:42
void yaz_log_init_file(const char *fname)
sets log file
Definition: log.c:166
char configname[BEND_NAME_MAX]
Definition: backend.h:340
void ccl_qual_file(CCL_bibset bibset, FILE *inf)
Definition: cclqfile.c:316
static int xml_config_add_listeners(void)
Definition: statserv.c:611
#define EVENT_EXCEPT
Definition: eventl.h:51
static int add_listener(char *where, int listen_id)
Definition: statserv.c:1146
cql_transform_t cql_transform_open_fname(const char *fname)
creates a CQL transform handle from a file
Definition: cqltransform.c:276
void bend_close(void *handle)
Definition: ztest.c:1153
static int log_sessiondetail
Definition: statserv.c:131
struct bend_initrequest * init
Definition: session.h:133
Definitions for event loop handling for GFS.
char * host
Definition: session.h:45
#define yaz_isspace(x)
Definition: yaz-iconv.h:87
static char * nmem_dup_xml_content(NMEM n, xmlNodePtr ptr)
Definition: statserv.c:173
struct gfs_server * server
Definition: session.h:136
static char gfs_root_dir[FILENAME_MAX+1]
Definition: statserv.c:71
association * create_association(IOCHAN channel, COMSTACK link, const char *apdufile)
Definition: seshigh.c:148
char * address
Definition: session.h:61
#define YAZ_VERSION
YAZ version as string.
Definition: yaz-version.h:36
static int max_sessions
Definition: statserv.c:127
bend_initresult * bend_init(bend_initrequest *q)
Definition: ztest.c:1116
static void xml_config_bend_start(void)
Definition: statserv.c:625
#define xfree(x)
utility macro which calls xfree_f
Definition: xmalloc.h:53
File Path utilities.
IOCHAN iochan_create(int fd, IOC_CALLBACK cb, int flags, int chan_id)
Definition: eventl.c:42
int * listen_ref
Definition: session.h:47
char * nmem_strdup_null(NMEM mem, const char *src)
allocates string on NMEM handle - allows NULL ptr buffer
Definition: nmemsdup.c:25
#define iochan_setdata(i, d)
Definition: eventl.h:68
Header for GFS (Obsolete. Use yaz/backend.h)
int maximumRecordSize
Definition: session.h:126
#define YLOG_ERRNO
log level: append system error message
Definition: log.h:50
int options(const char *desc, char **argv, int argc, char **arg)
command-line options parsing for main
Definition: options.c:21
void wrbuf_destroy(WRBUF b)
destroy WRBUF and its buffer
Definition: wrbuf.c:38
static xmlDocPtr xml_config_doc
Definition: statserv.c:150
statserv_options_block cb
Definition: session.h:44
char * id
Definition: session.h:46
Header for COMSTACK.
static void get_logbits(int force)
Definition: statserv.c:135
#define cs_addrstr(handle)
Definition: comstack.h:108
int yaz_log_mask_str(const char *str)
converts log level string to log level (integer)
Definition: log.c:604
char * nmem_strdup(NMEM mem, const char *src)
allocates string on NMEM handle (similar strdup)
Definition: nmemsdup.c:18
unsigned long yaz_version(char *version_str, char *sha1_str)
returns YAZ version
Definition: version.c:18
#define iochan_destroy(i)
Definition: eventl.h:64
statserv_options_block * statserv_getcontrol(void)
Definition: statserv.c:1215
char apdufile[BEND_NAME_MAX]
Definition: backend.h:335
int preferredMessageSize
Definition: session.h:125
struct iochan * next
Definition: eventl.h:60
#define cs_createbysocket(sock, type, blocking, proto)
Definition: comstack.h:101
COMSTACK tcpip_type(int s, int flags, int protocol, void *vp)
Definition: tcpip.c:205
unsigned io_pending
Definition: comstack.h:63
void ir_session(IOCHAN h, int event)
Definition: seshigh.c:365
#define cs_errno(handle)
Definition: comstack.h:106
enum oid_proto default_proto
Definition: backend.h:337
#define cs_bind(handle, ad, mo)
Definition: comstack.h:95
static int statserv_sc_main(yaz_sc_t s, int argc, char **argv)
Definition: statserv.c:1283
char * directory
Definition: session.h:51
static statserv_options_block * current_control_block
Definition: statserv.c:85
static struct gfs_server * gfs_server_list
Definition: statserv.c:72
#define cs_fileno(handle)
Definition: comstack.h:104
int cs_set_ssl_certificate_file(COMSTACK cs, const char *fname)
Definition: tcpip.c:1641
static void inetd_connection(int what)
Definition: statserv.c:1103
void yaz_sc_destroy(yaz_sc_t *s)
frees service control handle
Definition: sc.c:371
void yaz_log_init_prefix(const char *prefix)
sets log message prefix
Definition: log.c:261
#define CS_WANT_READ
Definition: comstack.h:114
static void gfs_server_chdir(struct gfs_server *gfs)
Definition: statserv.c:252
static void option_copy(char *dst, const char *src)
Definition: statserv.c:1353
#define iochan_getnext(i)
Definition: eventl.h:77
void yaz_log_xml_errors(const char *prefix, int log_level)
Makes Libxml2 and Libxslt log errors through yaz_log.
Definition: xmlerror.c:47
#define EVENT_TIMEOUT
Definition: eventl.h:52
int yaz_retrieval_configure(yaz_retrieval_t p, const xmlNode *ptr)
Definition: retrieval.c:285
char setuid[BEND_NAME_MAX]
Definition: backend.h:341
static int log_session
Definition: statserv.c:130
control block for server
Definition: backend.h:330
void yaz_log_init_level(int level)
sets log level
Definition: log.c:226
static xmlNodePtr xml_config_get_root(void)
Definition: statserv.c:154
Definition: sc.c:26
char * stylesheet
Definition: session.h:53
int(* options_func)(int argc, char **argv)
Definition: backend.h:344
#define iochan_getdata(i)
Definition: eventl.h:67
static void show_version(void)
Definition: statserv.c:1272
static void catchchld(int num)
Definition: statserv.c:1207
int cs_put_mask
Definition: session.h:130
Header for YAZ iconv interface.
char * id
Definition: session.h:60
NMEM nmem_create(void)
returns new NMEM handle
Definition: nmem.c:181
#define YLOG_WARN
log level: warning
Definition: log.h:46
#define BEND_NAME_MAX
Definition: backend.h:327
void yaz_sc_running(yaz_sc_t s)
signals that sc_main applicatio starts running
Definition: sc.c:226
Definition: eventl.h:45
void yaz_log(int level, const char *fmt,...)
Writes log message.
Definition: log.c:485
char * docpath
Definition: session.h:52
cql_transform_t cql_transform
Definition: session.h:48
static struct gfs_listen * gfs_listen_list
Definition: statserv.c:73
static void statserv_closedown(void)
Definition: statserv.c:932
bend_initresult *(* bend_init)(bend_initrequest *r)
Definition: backend.h:350
void(* bend_close)(void *handle)
Definition: backend.h:351
int yaz_log_module_level(const char *name)
returns level for module
Definition: log.c:584
CCL_bibset ccl_transform
Definition: session.h:49
struct gfs_listen * next
Definition: session.h:62
char xml_config[BEND_NAME_MAX]
Definition: backend.h:363
void * user
Definition: comstack.h:87
Header for command line options parsing utilities.
static void * new_session(void *vp)
Definition: statserv.c:1038
static struct gfs_listen * gfs_listen_new(const char *id, const char *address)
Definition: statserv.c:237
statserv_options_block control_block
Definition: statserv.c:94
char * yaz_filepath_resolve(const char *fname, const char *path, const char *base, char *fullpath)
resolve file on path
Definition: tpath.c:74
int cs_accept_mask
Definition: session.h:131
COMSTACK client_link
Definition: session.h:109
static void listener(IOCHAN h, int event)
Definition: statserv.c:948
#define iochan_settimeout(i, t)
Definition: eventl.h:78
COMSTACK cs_create_host(const char *vhost, int blocking, void **vp)
Definition: comstack.c:167
Logging utility.
static int sig_received
Definition: statserv.c:1257
void statserv_sc_stop(yaz_sc_t s)
Definition: statserv.c:1498
void cs_set_max_recv_bytes(COMSTACK cs, int max_recv_bytes)
Definition: comstack.c:476
static int xml_config_open(void)
Definition: statserv.c:530
char * client_query_charset
Definition: session.h:54
void yaz_retrieval_set_path(yaz_retrieval_t p, const char *path)
Definition: retrieval.c:384
#define YAZ_DAEMON_FORK
Definition: daemon.h:41
static NMEM gfs_nmem
Definition: statserv.c:74
WRBUF wrbuf_alloc(void)
construct WRBUF
Definition: wrbuf.c:25
int yaz_daemon(const char *progname, unsigned int flags, void(*work)(void *data), void *data, const char *pidfile, const char *uid)
daemon utility.
Definition: daemon.c:227
statserv_options_block * last_control
Definition: session.h:134
int statserv_main(int argc, char **argv, bend_initresult *(*bend_init)(bend_initrequest *r), void(*bend_close)(void *handle))
Definition: statserv.c:1504
int mask
Definition: log.c:81
int check_ip_tcpd(void *cd, const char *addr, int len, int type)
Definition: tcpdchk.c:56
int control_association(association *assoc, const char *host, int force_open)
Definition: statserv.c:267