1 /* This file is part of the YAZ toolkit.
2 * Copyright (C) Index Data
3 * See the file LICENSE for details.
8 * \brief Implements GFS logic
26 #include <yaz/tpath.h>
29 #include <sys/types.h>
42 #include <libxml/parser.h>
43 #include <libxml/tree.h>
44 #include <libxml/xinclude.h>
55 #include <yaz/comstack.h>
56 #include <yaz/tcpip.h>
57 #include <yaz/options.h>
58 #include <yaz/errno.h>
60 #include <yaz/xmosi.h>
65 #include <yaz/statserv.h>
66 #include <yaz/daemon.h>
67 #include <yaz/yaz-iconv.h>
69 static IOCHAN pListener = NULL;
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;
76 static char *me = "statserver"; /* log prefix */
77 static char *programname="statserver"; /* full program name */
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;
85 static statserv_options_block *current_control_block = 0;
91 #define STAT_DEFAULT_LOG_LEVEL "server,session,request"
93 int check_options(int argc, char **argv);
94 statserv_options_block control_block = {
96 0, /* threaded mode */
97 0, /* one shot (single session) */
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 */
110 0, /* default value for inet deamon */
111 0, /* handle (for service, etc) */
112 0, /* bend_init handle */
113 0, /* bend_close handle */
115 "Z39.50 Server", /* NT Service Name */
116 "Server", /* NT application Name */
117 "", /* NT Service Dependencies */
118 "Z39.50 Server", /* NT Service Display Name */
121 0, /* background daemon */
122 "", /* SSL certificate filename */
123 "", /* XML config filename */
127 static int max_sessions = 0;
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;
134 /** get_logbits sets global loglevel bits */
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)
140 log_session = yaz_log_module_level("session");
141 log_sessiondetail = yaz_log_module_level("sessiondetail");
142 log_server = yaz_log_module_level("server");
147 static int add_listener(char *where, int listen_id);
150 static xmlDocPtr xml_config_doc = 0;
154 static xmlNodePtr xml_config_get_root(void)
159 ptr = xmlDocGetRootElement(xml_config_doc);
160 if (!ptr || ptr->type != XML_ELEMENT_NODE ||
161 strcmp((const char *) ptr->name, "yazgfs"))
163 yaz_log(YLOG_WARN, "Bad/missing root element for config %s",
164 control_block.xml_config);
173 static char *nmem_dup_xml_content(NMEM n, xmlNodePtr ptr)
177 int len = 1; /* start with 1, because of trailing 0 */
179 int first = 1; /* whitespace lead flag .. */
180 /* determine length */
181 for (p = ptr; p; p = p->next)
183 if (p->type == XML_TEXT_NODE)
184 len += xmlStrlen(p->content);
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)
191 if (p->type == XML_TEXT_NODE)
196 while(*cp && yaz_isspace(*cp))
199 first = 0; /* reset if we got non-whitespace out */
201 strcat((char *)str, (const char *)cp); /* append */
204 /* remove trailing whitespace */
205 cp = strlen((const char *)str) + str;
206 while (cp != str && yaz_isspace(cp[-1]))
209 /* return resulting string */
215 static struct gfs_server * gfs_server_new(const char *id)
217 struct gfs_server *n = (struct gfs_server *)
218 nmem_malloc(gfs_nmem, sizeof(*n));
219 memcpy(&n->cb, &control_block, sizeof(control_block));
223 n->cql_transform = 0;
224 n->ccl_transform = 0;
225 n->server_node_ptr = 0;
229 n->client_query_charset = 0;
230 n->id = nmem_strdup_null(gfs_nmem, id);
231 n->retrieval = yaz_retrieval_create();
237 static struct gfs_listen * gfs_listen_new(const char *id,
240 struct gfs_listen *n = (struct gfs_listen *)
241 nmem_malloc(gfs_nmem, sizeof(*n));
243 n->id = nmem_strdup(gfs_nmem, id);
247 n->address = nmem_strdup(gfs_nmem, address);
252 static void gfs_server_chdir(struct gfs_server *gfs)
256 if (chdir(gfs_root_dir))
257 yaz_log(YLOG_WARN|YLOG_ERRNO, "chdir %s", gfs_root_dir);
261 if (chdir(gfs->directory))
262 yaz_log(YLOG_WARN|YLOG_ERRNO, "chdir %s",
267 int control_association(association *assoc, const char *host, int force_open)
269 char vhost[128], *cp;
272 strncpy(vhost, host, 127);
274 cp = strchr(vhost, ':');
280 if (control_block.xml_config[0])
282 struct gfs_server *gfs;
283 for (gfs = gfs_server_list; gfs; gfs = gfs->next)
285 int listen_match = 0;
287 if ( !gfs->host || (host && gfs->host && !strcmp(host, gfs->host)))
289 if (!gfs->listen_ref)
294 for (i = 0; gfs->listen_ref[i] != -1; i++)
295 if (gfs->listen_ref[i] == assoc->client_chan->chan_id)
298 if (listen_match && host_match)
301 (assoc->last_control != &gfs->cb && assoc->backend))
303 statserv_setcontrol(assoc->last_control);
304 if (assoc->backend && assoc->init)
306 gfs_server_chdir(gfs);
307 (assoc->last_control->bend_close)(assoc->backend);
314 assoc->last_control = &gfs->cb;
315 statserv_setcontrol(&gfs->cb);
317 gfs_server_chdir(gfs);
323 statserv_setcontrol(0);
324 assoc->last_control = 0;
330 statserv_setcontrol(&control_block);
331 assoc->last_control = &control_block;
333 yaz_log(YLOG_DEBUG, "server select: config=%s",
334 assoc->last_control->configname);
336 assoc->maximumRecordSize = assoc->last_control->maxrecordsize;
337 assoc->preferredMessageSize = assoc->last_control->maxrecordsize;
338 cs_set_max_recv_bytes(assoc->client_link, assoc->maximumRecordSize);
343 static void xml_config_read(const char *base_path)
345 struct gfs_server **gfsp = &gfs_server_list;
346 struct gfs_listen **gfslp = &gfs_listen_list;
347 xmlNodePtr ptr = xml_config_get_root();
351 for (ptr = ptr->children; ptr; ptr = ptr->next)
353 struct _xmlAttr *attr;
354 if (ptr->type != XML_ELEMENT_NODE)
356 attr = ptr->properties;
357 if (!strcmp((const char *) ptr->name, "listen"))
360 <listen id="listenerid">tcp:@:9999</listen>
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);
371 *gfslp = gfs_listen_new(id, address);
372 gfslp = &(*gfslp)->next;
373 *gfslp = 0; /* make listener list consistent for search */
376 else if (!strcmp((const char *) ptr->name, "server"))
378 xmlNodePtr ptr_server = ptr;
380 const char *listenref = 0;
382 struct gfs_server *gfs;
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")
390 && attr->children->type == XML_TEXT_NODE)
391 id = nmem_dup_xml_content(gfs_nmem, attr->children);
393 yaz_log(YLOG_WARN, "Unknown attribute '%s' for server",
395 gfs = *gfsp = gfs_server_new(id);
396 gfs->server_node_ptr = ptr_server;
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++)
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]))
412 gfs->listen_ref[i] = id_no;
416 yaz_log(YLOG_WARN, "Non-existent listenref '%s' "
417 "in server config element", refs[i]);
419 gfs->listen_ref[i] = -1;
421 for (ptr = ptr_server->children; ptr; ptr = ptr->next)
423 if (ptr->type != XML_ELEMENT_NODE)
425 if (!strcmp((const char *) ptr->name, "host"))
427 gfs->host = nmem_dup_xml_content(gfs_nmem,
430 else if (!strcmp((const char *) ptr->name, "config"))
433 strcpy(gfs->cb.configname,
434 nmem_dup_xml_content(gfs_nmem, ptr->children));
436 if (yaz_filepath_resolve(gfs->cb.configname,
437 base_path, 0, fpath))
438 strcpy(gfs->cb.configname, fpath);
440 else if (!strcmp((const char *) ptr->name, "cql2rpn"))
443 char *fname = nmem_dup_xml_content(gfs_nmem, ptr->children);
444 if (yaz_filepath_resolve(fname, base_path, 0, fpath))
447 gfs->cql_transform = cql_transform_open_fname(fname);
448 if (!gfs->cql_transform)
450 yaz_log(YLOG_FATAL|YLOG_ERRNO,
451 "open CQL transform file '%s'", fname);
455 else if (!strcmp((const char *) ptr->name, "ccl2rpn"))
457 char *fname, fpath[1024];
460 fname = nmem_dup_xml_content(gfs_nmem, ptr->children);
461 if (yaz_filepath_resolve(fname, base_path, 0, fpath))
464 if ((f = fopen(fname, "r")) == 0) {
465 yaz_log(YLOG_FATAL, "can't open CCL file '%s'", fname);
468 gfs->ccl_transform = ccl_qual_mk();
469 ccl_qual_file (gfs->ccl_transform, f);
472 else if (!strcmp((const char *) ptr->name, "directory"))
475 nmem_dup_xml_content(gfs_nmem, ptr->children);
477 else if (!strcmp((const char *) ptr->name, "docpath"))
480 nmem_dup_xml_content(gfs_nmem, ptr->children);
482 else if (!strcmp((const char *) ptr->name, "maximumrecordsize"))
484 gfs->cb.maxrecordsize = atoi(
485 nmem_dup_xml_content(gfs_nmem, ptr->children));
487 else if (!strcmp((const char *) ptr->name, "stylesheet"))
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);
494 else if (!strcmp((const char *) ptr->name,
495 "client_query_charset"))
497 gfs->client_query_charset =
498 nmem_dup_xml_content(gfs_nmem, ptr->children);
500 else if (!strcmp((const char *) ptr->name, "explain"))
502 ; /* being processed separately */
504 else if (!strcmp((const char *) ptr->name, "retrievalinfo"))
507 yaz_retrieval_set_path(gfs->retrieval, base_path);
508 if (yaz_retrieval_configure(gfs->retrieval, ptr))
510 yaz_log(YLOG_FATAL, "%s in config %s",
511 yaz_retrieval_get_error(gfs->retrieval),
512 control_block.xml_config);
518 yaz_log(YLOG_FATAL, "Unknown element '%s' in config %s",
519 ptr->name, control_block.xml_config);
523 gfsp = &(*gfsp)->next;
530 static int xml_config_open(void)
533 const char *fname = control_block.xml_config;
534 if (!getcwd(gfs_root_dir, FILENAME_MAX))
536 yaz_log(YLOG_WARN|YLOG_ERRNO, "getcwd failed");
537 gfs_root_dir[0] = '\0';
540 init_control_tls = 1;
541 current_control_tls = TlsAlloc();
542 #elif YAZ_POSIX_THREADS
543 init_control_tls = 1;
544 pthread_key_create(¤t_control_tls, 0);
547 gfs_nmem = nmem_create();
549 if (fname[0] == '\0')
554 xml_config_doc = xmlParseFile(fname);
557 yaz_log(YLOG_FATAL, "Could not parse %s", fname);
562 int noSubstitutions = xmlXIncludeProcess(xml_config_doc);
563 if (noSubstitutions == -1)
565 yaz_log(YLOG_WARN, "XInclude processing failed for config %s",
571 last_p = strrchr(fname,
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);
591 static void xml_config_close(void)
596 xmlFreeDoc(xml_config_doc);
601 nmem_destroy(gfs_nmem);
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);
611 static int xml_config_add_listeners(void)
613 struct gfs_listen *gfs = gfs_listen_list;
617 for (id_no = 1; gfs; gfs = gfs->next, id_no++)
619 if (!ret && gfs->address)
620 ret = add_listener(gfs->address, id_no);
625 static void xml_config_bend_start(void)
627 if (control_block.xml_config[0])
629 struct gfs_server *gfs = gfs_server_list;
630 for (; gfs; gfs = gfs->next)
632 yaz_log(YLOG_DEBUG, "xml_config_bend_start config=%s",
634 statserv_setcontrol(&gfs->cb);
635 if (control_block.bend_start)
637 gfs_server_chdir(gfs);
638 (control_block.bend_start)(&gfs->cb);
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);
651 static void xml_config_bend_stop(void)
653 if (control_block.xml_config[0])
655 struct gfs_server *gfs = gfs_server_list;
656 for (; gfs; gfs = gfs->next)
658 yaz_log(YLOG_DEBUG, "xml_config_bend_stop config=%s",
660 statserv_setcontrol(&gfs->cb);
661 if (control_block.bend_stop)
662 (control_block.bend_stop)(&gfs->cb);
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);
674 static void remove_listeners(void);
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
684 typedef struct _ThreadList ThreadList;
693 static ThreadList *pFirstThread;
694 static CRITICAL_SECTION Thread_CritSect;
695 static BOOL bInitialized = FALSE;
697 static void ThreadList_Initialize()
699 /* Initialize the critical Sections */
700 InitializeCriticalSection(&Thread_CritSect);
702 /* Set the first thraed */
705 /* we have been initialized */
709 static void statserv_add(HANDLE hThread, IOCHAN pIOChannel)
711 /* Only one thread can go through this section at a time */
712 EnterCriticalSection(&Thread_CritSect);
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;
722 /* Lets let somebody else create a new object now */
723 LeaveCriticalSection(&Thread_CritSect);
727 void statserv_remove(IOCHAN pIOChannel)
729 /* Only one thread can go through this section at a time */
730 EnterCriticalSection(&Thread_CritSect);
733 ThreadList *pCurrentThread = pFirstThread;
734 ThreadList *pNextThread;
735 ThreadList *pPrevThread =NULL;
737 /* Step through all the threads */
738 for (; pCurrentThread != NULL; pCurrentThread = pNextThread)
740 /* We only need to compare on the IO Channel */
741 if (pCurrentThread->pIOChannel == pIOChannel)
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;
748 pPrevThread->pNext = pCurrentThread->pNext;
750 /* All we need todo now is delete the memory */
751 free(pCurrentThread);
753 /* No need to look at any more threads */
758 /* We need to look at another thread */
759 pNextThread = pCurrentThread->pNext;
760 pPrevThread = pCurrentThread;
764 /* Lets let somebody else remove an object now */
765 LeaveCriticalSection(&Thread_CritSect);
769 /* WIN32 statserv_closedown */
770 static void statserv_closedown()
772 /* Shouldn't do anything if we are not initialized */
776 HANDLE *pThreadHandles = NULL;
778 /* We need to stop threads adding and removing while we */
779 /* start the closedown process */
780 EnterCriticalSection(&Thread_CritSect);
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;
787 /* Before we do anything else, we need to shutdown the listener */
788 if (pListener != NULL)
789 iochan_destroy(pListener);
791 for (; pCurrentThread != NULL; pCurrentThread = pCurrentThread->pNext)
793 /* Just destroy the IOCHAN, that should do the trick */
794 iochan_destroy(pCurrentThread->pIOChannel);
795 closesocket(pCurrentThread->pIOChannel->fd);
797 /* Keep a running count of our handles */
803 HANDLE *pCurrentHandle ;
805 /* Allocate the thread handle array */
806 pThreadHandles = (HANDLE *)malloc(sizeof(HANDLE) * iHandles);
807 pCurrentHandle = pThreadHandles;
809 for (pCurrentThread = pFirstThread;
810 pCurrentThread != NULL;
811 pCurrentThread = pCurrentThread->pNext, pCurrentHandle++)
813 /* Just the handle */
814 *pCurrentHandle = pCurrentThread->hThread;
818 /* We can now leave the critical section */
819 LeaveCriticalSection(&Thread_CritSect);
822 /* Now we can really do something */
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);
829 /* Free the memory we allocated for the handle array */
830 free(pThreadHandles);
833 xml_config_bend_stop();
834 /* No longer require the critical section, since all threads are dead */
835 DeleteCriticalSection(&Thread_CritSect);
840 void __cdecl event_loop_thread(IOCHAN iochan)
842 iochan_event_loop(&iochan, 0);
846 static void listener(IOCHAN h, int event)
848 COMSTACK line = (COMSTACK) iochan_getdata(h);
849 IOCHAN parent_chan = line->user;
854 if (event == EVENT_INPUT)
859 if ((res = cs_listen(line, 0, 0)) < 0)
861 yaz_log(YLOG_FATAL|YLOG_ERRNO, "cs_listen failed");
865 return; /* incomplete */
866 yaz_log(YLOG_DEBUG, "listen ok");
867 new_line = cs_accept(line);
870 yaz_log(YLOG_FATAL, "Accept failed.");
873 yaz_log(YLOG_DEBUG, "Accept ok");
875 if (!(new_chan = iochan_create(cs_fileno(new_line), ir_session,
876 EVENT_INPUT, parent_chan->chan_id)))
878 yaz_log(YLOG_FATAL, "Failed to create iochan");
883 yaz_log(YLOG_DEBUG, "Creating association");
884 if (!(newas = create_association(new_chan, new_line,
885 control_block.apdufile)))
887 yaz_log(YLOG_FATAL, "Failed to create new assoc.");
891 newas->cs_get_mask = EVENT_INPUT;
892 newas->cs_put_mask = 0;
893 newas->cs_accept_mask = 0;
895 yaz_log(YLOG_DEBUG, "Setting timeout %d", control_block.idle_timeout);
896 iochan_setdata(new_chan, newas);
897 iochan_settimeout(new_chan, 60);
899 /* Now what we need todo is create a new thread with this iochan as
901 newHandle = (HANDLE) _beginthread(event_loop_thread, 0, new_chan);
902 if (newHandle == (HANDLE) -1)
905 yaz_log(YLOG_FATAL|YLOG_ERRNO, "Failed to create new thread.");
909 /* We successfully created the thread, so add it to the list */
910 statserv_add(newHandle, new_chan);
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 */
917 yaz_log(YLOG_FATAL, "Bad event on listener.");
925 /* To save having an #ifdef in event_loop we need to
926 define this empty function
928 void statserv_remove(IOCHAN pIOChannel)
932 static void statserv_closedown(void)
936 xml_config_bend_stop();
937 for (p = pListener; p; p = p->next)
944 static void *new_session(void *vp);
945 static int no_sessions = 0;
948 static void listener(IOCHAN h, int event)
950 COMSTACK line = (COMSTACK) iochan_getdata(h);
953 if (event == EVENT_INPUT)
956 if ((res = cs_listen_check(line, 0, 0, control_block.check_ip,
957 control_block.daemon_name)) < 0)
959 yaz_log(YLOG_WARN|YLOG_ERRNO, "cs_listen failed");
964 yaz_log(YLOG_WARN, "cs_listen incomplete");
967 new_line = cs_accept(line);
970 yaz_log(YLOG_FATAL, "Accept failed.");
971 iochan_setflags(h, EVENT_INPUT | EVENT_EXCEPT); /* reset listener */
975 if (control_block.one_shot)
978 yaz_log(log_sessiondetail, "Connect from %s", cs_addrstr(new_line));
981 if (control_block.dynamic)
983 if ((res = fork()) < 0)
985 yaz_log(YLOG_FATAL|YLOG_ERRNO, "fork");
989 else if (res == 0) /* child */
994 for (pp = pListener; pp; pp = iochan_getnext(pp))
996 COMSTACK l = (COMSTACK)iochan_getdata(pp);
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;
1013 if (control_block.threads)
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);
1020 new_session(new_line);
1024 new_session(new_line);
1026 else if (event == EVENT_TIMEOUT)
1028 yaz_log(log_server, "Shutting down listener.");
1033 yaz_log(YLOG_FATAL, "Bad event on listener.");
1038 static void *new_session(void *vp)
1043 COMSTACK new_line = (COMSTACK) vp;
1044 IOCHAN parent_chan = (IOCHAN) new_line->user;
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);
1052 cs_accept_mask = mask; /* accept didn't complete */
1057 cs_accept_mask = 0; /* accept completed. */
1058 cs_get_mask = mask = EVENT_INPUT;
1061 if (!(new_chan = iochan_create(cs_fileno(new_line), ir_session, mask,
1062 parent_chan->chan_id)))
1064 yaz_log(YLOG_FATAL, "Failed to create iochan");
1067 if (!(newas = create_association(new_chan, new_line,
1068 control_block.apdufile)))
1070 yaz_log(YLOG_FATAL, "Failed to create new assoc.");
1073 newas->cs_accept_mask = cs_accept_mask;
1074 newas->cs_get_mask = cs_get_mask;
1076 iochan_setdata(new_chan, newas);
1077 iochan_settimeout(new_chan, 60);
1079 a = cs_addrstr(new_line);
1083 yaz_log_xml_errors(0, YLOG_WARN);
1084 yaz_log(log_session, "Session - OK %d %s PID=%ld",
1085 no_sessions, a ? a : "[Unknown]", (long) getpid());
1086 if (max_sessions && no_sessions >= max_sessions)
1087 control_block.one_shot = 1;
1088 if (control_block.threads)
1090 iochan_event_loop(&new_chan, 0);
1094 new_chan->next = pListener;
1095 pListener = new_chan;
1103 static void inetd_connection(int what)
1110 if ((line = cs_createbysocket(0, tcpip_type, 0, what)))
1112 if ((chan = iochan_create(cs_fileno(line), ir_session, EVENT_INPUT,
1115 if ((assoc = create_association(chan, line,
1116 control_block.apdufile)))
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;
1127 yaz_log(YLOG_FATAL, "Failed to create association structure");
1129 chan->next = pListener;
1134 yaz_log(YLOG_FATAL, "Failed to create iochan");
1139 yaz_log(YLOG_ERRNO|YLOG_FATAL, "Failed to create comstack on socket 0");
1144 * Set up a listening endpoint, and give it to the event-handler.
1146 static int add_listener(char *where, int listen_id)
1153 if (control_block.dynamic)
1155 else if (control_block.threads)
1160 yaz_log(log_server, "Adding %s listener on %s id=%d PID=%ld", mode, where,
1161 listen_id, (long) getpid());
1163 l = cs_create_host(where, 2, &ap);
1166 yaz_log(YLOG_FATAL, "Failed to listen on %s", where);
1169 if (*control_block.cert_fname)
1170 cs_set_ssl_certificate_file(l, control_block.cert_fname);
1172 if (cs_bind(l, ap, CS_SERVER) < 0)
1174 if (cs_errno(l) == CSYSERR)
1175 yaz_log(YLOG_FATAL|YLOG_ERRNO, "Failed to bind to %s", where);
1177 yaz_log(YLOG_FATAL, "Failed to bind to %s: %s", where,
1182 if (!(lst = iochan_create(cs_fileno(l), listener, EVENT_INPUT |
1183 EVENT_EXCEPT, listen_id)))
1185 yaz_log(YLOG_FATAL|YLOG_ERRNO, "Failed to create IOCHAN-type");
1189 iochan_setdata(lst, l); /* user-defined data for listener is COMSTACK */
1190 l->user = lst; /* user-defined data for COMSTACK is listener chan */
1192 /* Add listener to chain */
1193 lst->next = pListener;
1198 static void remove_listeners(void)
1200 IOCHAN l = pListener;
1201 for (; l; l = l->next)
1206 /* UNIX only (for windows we don't need to catch the signals) */
1207 static void catchchld(int num)
1209 while (waitpid(-1, 0, WNOHANG) > 0)
1211 signal(SIGCHLD, catchchld);
1215 statserv_options_block *statserv_getcontrol(void)
1218 if (init_control_tls)
1219 return (statserv_options_block *) TlsGetValue(current_control_tls);
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);
1227 return &control_block;
1229 if (current_control_block)
1230 return current_control_block;
1231 return &control_block;
1235 void statserv_setcontrol(statserv_options_block *block)
1237 if (gfs_root_dir[0])
1239 if (chdir(gfs_root_dir))
1240 yaz_log(YLOG_WARN|YLOG_ERRNO, "chdir %s", gfs_root_dir);
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);
1249 current_control_block = block;
1253 static void statserv_reset(void)
1257 static int sig_received = 0;
1260 static void normal_stop_handler(int num)
1266 static void daemon_handler(void *data)
1268 IOCHAN *pListener = data;
1269 iochan_event_loop(pListener, &sig_received);
1272 static void show_version(void)
1274 char vstr[20], sha1_str[41];
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);
1283 static int statserv_sc_main(yaz_sc_t s, int argc, char **argv)
1287 /* We need to initialize the thread list */
1288 ThreadList_Initialize();
1298 if ((me = strrchr(argv[0], sep)))
1299 me++; /* get the basename */
1302 programname = argv[0];
1304 if (control_block.options_func(argc, argv))
1307 if (xml_config_open())
1310 xml_config_bend_start();
1312 if (control_block.inetd)
1315 ; /* no inetd on Windows */
1317 inetd_connection(control_block.default_proto);
1322 if (xml_config_add_listeners())
1326 add_listener("tcp:@:9999", 0);
1329 if (control_block.dynamic)
1330 signal(SIGCHLD, catchchld);
1333 if (pListener == NULL)
1339 signal(SIGTERM, normal_stop_handler);
1341 yaz_daemon(programname,
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);
1348 yaz_log(YLOG_LOG, "Received SIGTERM PID=%ld", (long) getpid());
1353 static void option_copy(char *dst, const char *src)
1355 strncpy(dst, src ? src : "", BEND_NAME_MAX-1);
1356 dst[BEND_NAME_MAX-1] = '\0';
1359 int check_options(int argc, char **argv)
1364 yaz_log_init_level(yaz_log_mask_str(STAT_DEFAULT_LOG_LEVEL));
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)
1374 if (add_listener(arg, 0))
1375 return 1; /* failed to create listener */
1378 control_block.one_shot = 1;
1379 control_block.dynamic = 0;
1382 control_block.default_proto = PROTO_Z3950;
1385 fprintf(stderr, "%s: SR protocol no longer supported\n", me);
1389 control_block.dynamic = 0;
1392 #if YAZ_POSIX_THREADS
1393 control_block.dynamic = 0;
1394 control_block.threads = 1;
1396 fprintf(stderr, "%s: Threaded mode not available.\n", me);
1401 option_copy(control_block.logfile, arg);
1402 yaz_log_init_file(control_block.logfile);
1406 fprintf(stderr, "%s: Specify time format for log file.\n", me);
1409 yaz_log_time_format(arg);
1412 yaz_log_init_level(yaz_log_mask_str(arg));
1416 option_copy(control_block.apdufile, arg);
1419 option_copy(control_block.setuid, arg);
1422 option_copy(control_block.configname, arg);
1425 option_copy(control_block.cert_fname, arg);
1428 option_copy(control_block.daemon_name, arg);
1431 if (!arg || !(r = atoi(arg)))
1433 fprintf(stderr, "%s: Specify positive timeout for -t.\n", me);
1436 control_block.idle_timeout = strchr(arg, 's') ? r : 60 * r;
1439 if (!arg || !(r = atoi(arg)))
1441 fprintf(stderr, "%s: Specify positive size for -k.\n", me);
1444 control_block.maxrecordsize = r * 1024;
1447 control_block.keepalive = 0;
1450 control_block.inetd = 1;
1460 max_sessions = atoi(arg);
1463 option_copy(control_block.pid_fname, arg);
1467 option_copy(control_block.xml_config, arg);
1469 fprintf(stderr, "%s: Option -f unsupported since YAZ is compiled without Libxml2 support\n", me);
1474 control_block.background = 1;
1477 if (!arg || !(r = atoi(arg)))
1479 fprintf(stderr, "%s: Specify positive size for -r.\n", me);
1482 yaz_log_init_max_size(r * 1024);
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);
1498 void statserv_sc_stop(yaz_sc_t s)
1500 statserv_closedown();
1504 int statserv_main(int argc, char **argv,
1505 bend_initresult *(*bend_init)(bend_initrequest *r),
1506 void (*bend_close)(void *handle))
1509 struct statserv_options_block *cb = &control_block;
1511 /* control block does not have service_name member on Unix */
1512 yaz_sc_t s = yaz_sc_create(
1514 cb->service_name, cb->service_display_name
1520 cb->bend_init = bend_init;
1521 cb->bend_close = bend_close;
1523 ret = yaz_sc_program(s, argc, argv, statserv_sc_main, statserv_sc_stop);
1531 * c-file-style: "Stroustrup"
1532 * indent-tabs-mode: nil
1534 * vim: shiftwidth=4 tabstop=8 expandtab