1 /* This file is part of the YAZ toolkit.
2 * Copyright (C) 1995-2013 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);
174 static char *nmem_dup_xml_content(NMEM n, xmlNodePtr ptr)
178 int len = 1; /* start with 1, because of trailing 0 */
180 int first = 1; /* whitespace lead flag .. */
181 /* determine length */
182 for (p = ptr; p; p = p->next)
184 if (p->type == XML_TEXT_NODE)
185 len += xmlStrlen(p->content);
187 /* now allocate for the string */
188 str = (unsigned char *) nmem_malloc(n, len);
189 *str = '\0'; /* so we can use strcat */
190 for (p = ptr; p; p = p->next)
192 if (p->type == XML_TEXT_NODE)
197 while(*cp && yaz_isspace(*cp))
200 first = 0; /* reset if we got non-whitespace out */
202 strcat((char *)str, (const char *)cp); /* append */
205 /* remove trailing whitespace */
206 cp = strlen((const char *)str) + str;
207 while (cp != str && yaz_isspace(cp[-1]))
210 /* return resulting string */
216 static struct gfs_server * gfs_server_new(const char *id)
218 struct gfs_server *n = (struct gfs_server *)
219 nmem_malloc(gfs_nmem, sizeof(*n));
220 memcpy(&n->cb, &control_block, sizeof(control_block));
224 n->cql_transform = 0;
225 n->ccl_transform = 0;
226 n->server_node_ptr = 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, "explain"))
496 ; /* being processed separately */
498 else if (!strcmp((const char *) ptr->name, "retrievalinfo"))
501 yaz_retrieval_set_path(gfs->retrieval, base_path);
502 if (yaz_retrieval_configure(gfs->retrieval, ptr))
504 yaz_log(YLOG_FATAL, "%s in config %s",
505 yaz_retrieval_get_error(gfs->retrieval),
506 control_block.xml_config);
512 yaz_log(YLOG_FATAL, "Unknown element '%s' in config %s",
513 ptr->name, control_block.xml_config);
517 gfsp = &(*gfsp)->next;
524 static void xml_config_open(void)
527 const char *fname = control_block.xml_config;
528 if (!getcwd(gfs_root_dir, FILENAME_MAX))
530 yaz_log(YLOG_WARN|YLOG_ERRNO, "getcwd failed");
531 gfs_root_dir[0] = '\0';
534 init_control_tls = 1;
535 current_control_tls = TlsAlloc();
536 #elif YAZ_POSIX_THREADS
537 init_control_tls = 1;
538 pthread_key_create(¤t_control_tls, 0);
541 gfs_nmem = nmem_create();
543 if (fname[0] == '\0')
548 xml_config_doc = xmlParseFile(fname);
551 yaz_log(YLOG_FATAL, "Could not parse %s", fname);
556 int noSubstitutions = xmlXIncludeProcess(xml_config_doc);
557 if (noSubstitutions == -1)
559 yaz_log(YLOG_WARN, "XInclude processing failed for config %s",
565 last_p = strrchr(fname,
574 WRBUF base_path = wrbuf_alloc();
575 wrbuf_write(base_path, fname, last_p - fname);
576 xml_config_read(wrbuf_cstr(base_path));
577 wrbuf_destroy(base_path);
584 static void xml_config_close(void)
589 xmlFreeDoc(xml_config_doc);
594 nmem_destroy(gfs_nmem);
596 if (init_control_tls)
597 TlsFree(current_control_tls);
598 #elif YAZ_POSIX_THREADS
599 if (init_control_tls)
600 pthread_key_delete(current_control_tls);
604 static void xml_config_add_listeners(void)
606 struct gfs_listen *gfs = gfs_listen_list;
609 for (id_no = 1; gfs; gfs = gfs->next, id_no++)
612 add_listener(gfs->address, id_no);
616 static void xml_config_bend_start(void)
618 if (control_block.xml_config[0])
620 struct gfs_server *gfs = gfs_server_list;
621 for (; gfs; gfs = gfs->next)
623 yaz_log(YLOG_DEBUG, "xml_config_bend_start config=%s",
625 statserv_setcontrol(&gfs->cb);
626 if (control_block.bend_start)
628 gfs_server_chdir(gfs);
629 (control_block.bend_start)(&gfs->cb);
635 yaz_log(YLOG_DEBUG, "xml_config_bend_start default config");
636 statserv_setcontrol(&control_block);
637 if (control_block.bend_start)
638 (*control_block.bend_start)(&control_block);
642 static void xml_config_bend_stop(void)
644 if (control_block.xml_config[0])
646 struct gfs_server *gfs = gfs_server_list;
647 for (; gfs; gfs = gfs->next)
649 yaz_log(YLOG_DEBUG, "xml_config_bend_stop config=%s",
651 statserv_setcontrol(&gfs->cb);
652 if (control_block.bend_stop)
653 (control_block.bend_stop)(&gfs->cb);
658 yaz_log(YLOG_DEBUG, "xml_config_bend_stop default config");
659 statserv_setcontrol(&control_block);
660 if (control_block.bend_stop)
661 (*control_block.bend_stop)(&control_block);
665 static void remove_listeners(void);
668 * handle incoming connect requests.
669 * The dynamic mode is a bit tricky mostly because we want to avoid
670 * doing all of the listening and accepting in the parent - it's
675 typedef struct _ThreadList ThreadList;
684 static ThreadList *pFirstThread;
685 static CRITICAL_SECTION Thread_CritSect;
686 static BOOL bInitialized = FALSE;
688 static void ThreadList_Initialize()
690 /* Initialize the critical Sections */
691 InitializeCriticalSection(&Thread_CritSect);
693 /* Set the first thraed */
696 /* we have been initialized */
700 static void statserv_add(HANDLE hThread, IOCHAN pIOChannel)
702 /* Only one thread can go through this section at a time */
703 EnterCriticalSection(&Thread_CritSect);
706 /* Lets create our new object */
707 ThreadList *pNewThread = (ThreadList *)malloc(sizeof(ThreadList));
708 pNewThread->hThread = hThread;
709 pNewThread->pIOChannel = pIOChannel;
710 pNewThread->pNext = pFirstThread;
711 pFirstThread = pNewThread;
713 /* Lets let somebody else create a new object now */
714 LeaveCriticalSection(&Thread_CritSect);
718 void statserv_remove(IOCHAN pIOChannel)
720 /* Only one thread can go through this section at a time */
721 EnterCriticalSection(&Thread_CritSect);
724 ThreadList *pCurrentThread = pFirstThread;
725 ThreadList *pNextThread;
726 ThreadList *pPrevThread =NULL;
728 /* Step through all the threads */
729 for (; pCurrentThread != NULL; pCurrentThread = pNextThread)
731 /* We only need to compare on the IO Channel */
732 if (pCurrentThread->pIOChannel == pIOChannel)
734 /* We have found the thread we want to delete */
735 /* First of all reset the next pointers */
736 if (pPrevThread == NULL)
737 pFirstThread = pCurrentThread->pNext;
739 pPrevThread->pNext = pCurrentThread->pNext;
741 /* All we need todo now is delete the memory */
742 free(pCurrentThread);
744 /* No need to look at any more threads */
749 /* We need to look at another thread */
750 pNextThread = pCurrentThread->pNext;
751 pPrevThread = pCurrentThread;
755 /* Lets let somebody else remove an object now */
756 LeaveCriticalSection(&Thread_CritSect);
760 /* WIN32 statserv_closedown */
761 static void statserv_closedown()
763 /* Shouldn't do anything if we are not initialized */
767 HANDLE *pThreadHandles = NULL;
769 /* We need to stop threads adding and removing while we */
770 /* start the closedown process */
771 EnterCriticalSection(&Thread_CritSect);
774 /* We have exclusive access to the thread stuff now */
775 /* Y didn't i use a semaphore - Oh well never mind */
776 ThreadList *pCurrentThread = pFirstThread;
778 /* Before we do anything else, we need to shutdown the listener */
779 if (pListener != NULL)
780 iochan_destroy(pListener);
782 for (; pCurrentThread != NULL; pCurrentThread = pCurrentThread->pNext)
784 /* Just destroy the IOCHAN, that should do the trick */
785 iochan_destroy(pCurrentThread->pIOChannel);
786 closesocket(pCurrentThread->pIOChannel->fd);
788 /* Keep a running count of our handles */
794 HANDLE *pCurrentHandle ;
796 /* Allocate the thread handle array */
797 pThreadHandles = (HANDLE *)malloc(sizeof(HANDLE) * iHandles);
798 pCurrentHandle = pThreadHandles;
800 for (pCurrentThread = pFirstThread;
801 pCurrentThread != NULL;
802 pCurrentThread = pCurrentThread->pNext, pCurrentHandle++)
804 /* Just the handle */
805 *pCurrentHandle = pCurrentThread->hThread;
809 /* We can now leave the critical section */
810 LeaveCriticalSection(&Thread_CritSect);
813 /* Now we can really do something */
816 yaz_log(log_server, "waiting for %d to die", iHandles);
817 /* This will now wait, until all the threads close */
818 WaitForMultipleObjects(iHandles, pThreadHandles, TRUE, INFINITE);
820 /* Free the memory we allocated for the handle array */
821 free(pThreadHandles);
824 xml_config_bend_stop();
825 /* No longer require the critical section, since all threads are dead */
826 DeleteCriticalSection(&Thread_CritSect);
831 void __cdecl event_loop_thread(IOCHAN iochan)
833 iochan_event_loop(&iochan);
837 static void listener(IOCHAN h, int event)
839 COMSTACK line = (COMSTACK) iochan_getdata(h);
840 IOCHAN parent_chan = line->user;
845 if (event == EVENT_INPUT)
850 if ((res = cs_listen(line, 0, 0)) < 0)
852 yaz_log(YLOG_FATAL|YLOG_ERRNO, "cs_listen failed");
856 return; /* incomplete */
857 yaz_log(YLOG_DEBUG, "listen ok");
858 new_line = cs_accept(line);
861 yaz_log(YLOG_FATAL, "Accept failed.");
864 yaz_log(YLOG_DEBUG, "Accept ok");
866 if (!(new_chan = iochan_create(cs_fileno(new_line), ir_session,
867 EVENT_INPUT, parent_chan->chan_id)))
869 yaz_log(YLOG_FATAL, "Failed to create iochan");
874 yaz_log(YLOG_DEBUG, "Creating association");
875 if (!(newas = create_association(new_chan, new_line,
876 control_block.apdufile)))
878 yaz_log(YLOG_FATAL, "Failed to create new assoc.");
882 newas->cs_get_mask = EVENT_INPUT;
883 newas->cs_put_mask = 0;
884 newas->cs_accept_mask = 0;
886 yaz_log(YLOG_DEBUG, "Setting timeout %d", control_block.idle_timeout);
887 iochan_setdata(new_chan, newas);
888 iochan_settimeout(new_chan, 60);
890 /* Now what we need todo is create a new thread with this iochan as
892 newHandle = (HANDLE) _beginthread(event_loop_thread, 0, new_chan);
893 if (newHandle == (HANDLE) -1)
896 yaz_log(YLOG_FATAL|YLOG_ERRNO, "Failed to create new thread.");
900 /* We successfully created the thread, so add it to the list */
901 statserv_add(newHandle, new_chan);
903 yaz_log(YLOG_DEBUG, "Created new thread, id = %ld iochan %p",(long) newHandle, new_chan);
904 iochan_setflags(h, EVENT_INPUT | EVENT_EXCEPT); /* reset listener */
908 yaz_log(YLOG_FATAL, "Bad event on listener.");
916 /* To save having an #ifdef in event_loop we need to
917 define this empty function
919 void statserv_remove(IOCHAN pIOChannel)
923 static void statserv_closedown(void)
927 xml_config_bend_stop();
928 for (p = pListener; p; p = p->next)
935 static void *new_session(void *vp);
936 static int no_sessions = 0;
939 static void listener(IOCHAN h, int event)
941 COMSTACK line = (COMSTACK) iochan_getdata(h);
944 if (event == EVENT_INPUT)
947 if ((res = cs_listen_check(line, 0, 0, control_block.check_ip,
948 control_block.daemon_name)) < 0)
950 yaz_log(YLOG_WARN|YLOG_ERRNO, "cs_listen failed");
955 yaz_log(YLOG_WARN, "cs_listen incomplete");
958 new_line = cs_accept(line);
961 yaz_log(YLOG_FATAL, "Accept failed.");
962 iochan_setflags(h, EVENT_INPUT | EVENT_EXCEPT); /* reset listener */
966 if (control_block.one_shot)
969 yaz_log(log_sessiondetail, "Connect from %s", cs_addrstr(new_line));
972 if (control_block.dynamic)
974 if ((res = fork()) < 0)
976 yaz_log(YLOG_FATAL|YLOG_ERRNO, "fork");
980 else if (res == 0) /* child */
985 for (pp = pListener; pp; pp = iochan_getnext(pp))
987 COMSTACK l = (COMSTACK)iochan_getdata(pp);
991 sprintf(nbuf, "%s(%d)", me, no_sessions);
992 yaz_log_init_prefix(nbuf);
993 /* ensure that bend_stop is not called when each child exits -
994 only for the main process .. */
995 control_block.bend_stop = 0;
1004 if (control_block.threads)
1006 #if YAZ_POSIX_THREADS
1007 pthread_t child_thread;
1008 pthread_create(&child_thread, 0, new_session, new_line);
1009 pthread_detach(child_thread);
1011 new_session(new_line);
1015 new_session(new_line);
1017 else if (event == EVENT_TIMEOUT)
1019 yaz_log(log_server, "Shutting down listener.");
1024 yaz_log(YLOG_FATAL, "Bad event on listener.");
1029 static void *new_session(void *vp)
1034 COMSTACK new_line = (COMSTACK) vp;
1035 IOCHAN parent_chan = (IOCHAN) new_line->user;
1037 unsigned cs_get_mask, cs_accept_mask, mask =
1038 ((new_line->io_pending & CS_WANT_WRITE) ? EVENT_OUTPUT : 0) |
1039 ((new_line->io_pending & CS_WANT_READ) ? EVENT_INPUT : 0);
1043 cs_accept_mask = mask; /* accept didn't complete */
1048 cs_accept_mask = 0; /* accept completed. */
1049 cs_get_mask = mask = EVENT_INPUT;
1052 if (!(new_chan = iochan_create(cs_fileno(new_line), ir_session, mask,
1053 parent_chan->chan_id)))
1055 yaz_log(YLOG_FATAL, "Failed to create iochan");
1058 if (!(newas = create_association(new_chan, new_line,
1059 control_block.apdufile)))
1061 yaz_log(YLOG_FATAL, "Failed to create new assoc.");
1064 newas->cs_accept_mask = cs_accept_mask;
1065 newas->cs_get_mask = cs_get_mask;
1067 iochan_setdata(new_chan, newas);
1068 iochan_settimeout(new_chan, 60);
1070 a = cs_addrstr(new_line);
1074 yaz_log_xml_errors(0, YLOG_WARN);
1075 yaz_log(log_session, "Session - OK %d %s PID=%ld",
1076 no_sessions, a ? a : "[Unknown]", (long) getpid());
1077 if (max_sessions && no_sessions >= max_sessions)
1078 control_block.one_shot = 1;
1079 if (control_block.threads)
1081 iochan_event_loop(&new_chan);
1085 new_chan->next = pListener;
1086 pListener = new_chan;
1094 static void inetd_connection(int what)
1101 if ((line = cs_createbysocket(0, tcpip_type, 0, what)))
1103 if ((chan = iochan_create(cs_fileno(line), ir_session, EVENT_INPUT,
1106 if ((assoc = create_association(chan, line,
1107 control_block.apdufile)))
1109 iochan_setdata(chan, assoc);
1110 iochan_settimeout(chan, 60);
1111 addr = cs_addrstr(line);
1112 yaz_log(log_sessiondetail, "Inetd association from %s",
1113 addr ? addr : "[UNKNOWN]");
1114 assoc->cs_get_mask = EVENT_INPUT;
1118 yaz_log(YLOG_FATAL, "Failed to create association structure");
1120 chan->next = pListener;
1125 yaz_log(YLOG_FATAL, "Failed to create iochan");
1130 yaz_log(YLOG_ERRNO|YLOG_FATAL, "Failed to create comstack on socket 0");
1135 * Set up a listening endpoint, and give it to the event-handler.
1137 static int add_listener(char *where, int listen_id)
1144 if (control_block.dynamic)
1146 else if (control_block.threads)
1151 yaz_log(log_server, "Adding %s listener on %s id=%d PID=%ld", mode, where,
1152 listen_id, (long) getpid());
1154 l = cs_create_host(where, 2, &ap);
1157 yaz_log(YLOG_FATAL, "Failed to listen on %s", where);
1160 if (*control_block.cert_fname)
1161 cs_set_ssl_certificate_file(l, control_block.cert_fname);
1163 if (cs_bind(l, ap, CS_SERVER) < 0)
1165 if (cs_errno(l) == CSYSERR)
1166 yaz_log(YLOG_FATAL|YLOG_ERRNO, "Failed to bind to %s", where);
1168 yaz_log(YLOG_FATAL, "Failed to bind to %s: %s", where,
1173 if (!(lst = iochan_create(cs_fileno(l), listener, EVENT_INPUT |
1174 EVENT_EXCEPT, listen_id)))
1176 yaz_log(YLOG_FATAL|YLOG_ERRNO, "Failed to create IOCHAN-type");
1180 iochan_setdata(lst, l); /* user-defined data for listener is COMSTACK */
1181 l->user = lst; /* user-defined data for COMSTACK is listener chan */
1183 /* Add listener to chain */
1184 lst->next = pListener;
1189 static void remove_listeners(void)
1191 IOCHAN l = pListener;
1192 for (; l; l = l->next)
1197 /* UNIX only (for windows we don't need to catch the signals) */
1198 static void catchchld(int num)
1200 while (waitpid(-1, 0, WNOHANG) > 0)
1202 signal(SIGCHLD, catchchld);
1206 statserv_options_block *statserv_getcontrol(void)
1209 if (init_control_tls)
1210 return (statserv_options_block *) TlsGetValue(current_control_tls);
1212 return &control_block;
1213 #elif YAZ_POSIX_THREADS
1214 if (init_control_tls)
1215 return (statserv_options_block *)
1216 pthread_getspecific(current_control_tls);
1218 return &control_block;
1220 if (current_control_block)
1221 return current_control_block;
1222 return &control_block;
1226 void statserv_setcontrol(statserv_options_block *block)
1228 if (gfs_root_dir[0])
1230 if (chdir(gfs_root_dir))
1231 yaz_log(YLOG_WARN|YLOG_ERRNO, "chdir %s", gfs_root_dir);
1234 if (init_control_tls)
1235 TlsSetValue(current_control_tls, block);
1236 #elif YAZ_POSIX_THREADS
1237 if (init_control_tls)
1238 pthread_setspecific(current_control_tls, block);
1240 current_control_block = block;
1244 static void statserv_reset(void)
1248 static void daemon_handler(void *data)
1250 IOCHAN *pListener = data;
1251 iochan_event_loop(pListener);
1255 static void normal_stop_handler(int num)
1257 yaz_log(log_server, "Received SIGTERM. PID=%ld", (long) getpid());
1262 static void show_version(void)
1264 char vstr[20], sha1_str[41];
1266 yaz_version(vstr, sha1_str);
1267 printf("YAZ version: %s %s\n", YAZ_VERSION, YAZ_VERSION_SHA1);
1268 if (strcmp(sha1_str, YAZ_VERSION_SHA1))
1269 printf("YAZ DLL/SO: %s %s\n", vstr, sha1_str);
1273 static int statserv_sc_main(yaz_sc_t s, int argc, char **argv)
1277 /* We need to initialize the thread list */
1278 ThreadList_Initialize();
1288 if ((me = strrchr(argv[0], sep)))
1289 me++; /* get the basename */
1292 programname = argv[0];
1294 if (control_block.options_func(argc, argv))
1299 xml_config_bend_start();
1301 if (control_block.inetd)
1304 ; /* no inetd on Windows */
1306 inetd_connection(control_block.default_proto);
1311 xml_config_add_listeners();
1314 add_listener("tcp:@:9999", 0);
1317 if (control_block.dynamic)
1318 signal(SIGCHLD, catchchld);
1321 if (pListener == NULL)
1327 signal(SIGTERM, normal_stop_handler);
1329 yaz_daemon(programname,
1330 (control_block.background ? YAZ_DAEMON_FORK : 0),
1331 daemon_handler, &pListener,
1332 *control_block.pid_fname ? control_block.pid_fname : 0,
1333 *control_block.setuid ? control_block.setuid : 0);
1337 static void option_copy(char *dst, const char *src)
1339 strncpy(dst, src ? src : "", BEND_NAME_MAX-1);
1340 dst[BEND_NAME_MAX-1] = '\0';
1343 int check_options(int argc, char **argv)
1348 yaz_log_init_level(yaz_log_mask_str(STAT_DEFAULT_LOG_LEVEL));
1352 while ((ret = options("1a:iszSTl:v:u:c:w:t:k:Kd:A:p:DC:f:m:r:V",
1353 argv, argc, &arg)) != -2)
1358 if (add_listener(arg, 0))
1359 return 1; /* failed to create listener */
1362 control_block.one_shot = 1;
1363 control_block.dynamic = 0;
1366 control_block.default_proto = PROTO_Z3950;
1369 fprintf(stderr, "%s: SR protocol no longer supported\n", me);
1373 control_block.dynamic = 0;
1376 #if YAZ_POSIX_THREADS
1377 control_block.dynamic = 0;
1378 control_block.threads = 1;
1380 fprintf(stderr, "%s: Threaded mode not available.\n", me);
1385 option_copy(control_block.logfile, arg);
1386 yaz_log_init_file(control_block.logfile);
1390 fprintf(stderr, "%s: Specify time format for log file.\n", me);
1393 yaz_log_time_format(arg);
1396 yaz_log_init_level(yaz_log_mask_str(arg));
1400 option_copy(control_block.apdufile, arg);
1403 option_copy(control_block.setuid, arg);
1406 option_copy(control_block.configname, arg);
1409 option_copy(control_block.cert_fname, arg);
1412 option_copy(control_block.daemon_name, arg);
1415 if (!arg || !(r = atoi(arg)))
1417 fprintf(stderr, "%s: Specify positive timeout for -t.\n", me);
1420 control_block.idle_timeout = strchr(arg, 's') ? r : 60 * r;
1423 if (!arg || !(r = atoi(arg)))
1425 fprintf(stderr, "%s: Specify positive size for -k.\n", me);
1428 control_block.maxrecordsize = r * 1024;
1431 control_block.keepalive = 0;
1434 control_block.inetd = 1;
1444 max_sessions = atoi(arg);
1447 option_copy(control_block.pid_fname, arg);
1451 option_copy(control_block.xml_config, arg);
1453 fprintf(stderr, "%s: Option -f unsupported since YAZ is compiled without Libxml2 support\n", me);
1458 control_block.background = 1;
1461 if (!arg || !(r = atoi(arg)))
1463 fprintf(stderr, "%s: Specify positive size for -r.\n", me);
1466 yaz_log_init_max_size(r * 1024);
1472 fprintf(stderr, "Usage: %s [ -a <pdufile> -v <loglevel>"
1473 " -l <logfile> -u <user> -c <config> -t <minutes>"
1474 " -k <kilobytes> -d <daemon> -p <pidfile> -C certfile"
1475 " -zKiDSTV1 -m <time-format> -w <directory> <listener-addr>... ]\n", me);
1482 void statserv_sc_stop(yaz_sc_t s)
1484 statserv_closedown();
1488 int statserv_main(int argc, char **argv,
1489 bend_initresult *(*bend_init)(bend_initrequest *r),
1490 void (*bend_close)(void *handle))
1493 struct statserv_options_block *cb = &control_block;
1495 /* control block does not have service_name member on Unix */
1496 yaz_sc_t s = yaz_sc_create(
1498 cb->service_name, cb->service_display_name
1504 cb->bend_init = bend_init;
1505 cb->bend_close = bend_close;
1507 ret = yaz_sc_program(s, argc, argv, statserv_sc_main, statserv_sc_stop);
1515 * c-file-style: "Stroustrup"
1516 * indent-tabs-mode: nil
1518 * vim: shiftwidth=4 tabstop=8 expandtab