00001
00002
00003
00004
00005
00011 #include <stdio.h>
00012 #include <stdlib.h>
00013 #include <string.h>
00014 #include <ctype.h>
00015
00016 #ifdef WIN32
00017 #include <process.h>
00018 #include <winsock.h>
00019 #include <direct.h>
00020 #endif
00021
00022 #include <yaz/sc.h>
00023
00024 #if HAVE_SYS_TYPES_H
00025 #include <sys/types.h>
00026 #endif
00027 #if HAVE_SYS_WAIT_H
00028 #include <sys/wait.h>
00029 #endif
00030 #if HAVE_UNISTD_H
00031 #include <unistd.h>
00032 #endif
00033 #if HAVE_PWD_H
00034 #include <pwd.h>
00035 #endif
00036
00037 #if YAZ_HAVE_XML2
00038 #include <libxml/parser.h>
00039 #include <libxml/tree.h>
00040 #include <libxml/xinclude.h>
00041 #endif
00042
00043 #if YAZ_POSIX_THREADS
00044 #include <pthread.h>
00045 #elif YAZ_GNU_THREADS
00046 #include <pth.h>
00047 #endif
00048
00049 #include <fcntl.h>
00050 #include <signal.h>
00051 #include <errno.h>
00052
00053 #include <yaz/comstack.h>
00054 #include <yaz/tcpip.h>
00055 #include <yaz/options.h>
00056 #ifdef USE_XTIMOSI
00057 #include <yaz/xmosi.h>
00058 #endif
00059 #include <yaz/log.h>
00060 #include "eventl.h"
00061 #include "session.h"
00062 #include <yaz/statserv.h>
00063
00064 static IOCHAN pListener = NULL;
00065
00066 static char gfs_root_dir[FILENAME_MAX+1];
00067 static struct gfs_server *gfs_server_list = 0;
00068 static struct gfs_listen *gfs_listen_list = 0;
00069 static NMEM gfs_nmem = 0;
00070
00071 static char *me = "statserver";
00072 static char *programname="statserver";
00073 #ifdef WIN32
00074 DWORD current_control_tls;
00075 static int init_control_tls = 0;
00076 #elif YAZ_POSIX_THREADS
00077 static pthread_key_t current_control_tls;
00078 static int init_control_tls = 0;
00079 #else
00080 static statserv_options_block *current_control_block = 0;
00081 #endif
00082
00083
00084
00085
00086 #define STAT_DEFAULT_LOG_LEVEL "server,session,request"
00087
00088 int check_options(int argc, char **argv);
00089 statserv_options_block control_block = {
00090 1,
00091 0,
00092 0,
00093 "",
00094 "",
00095 "tcp:@:9999",
00096 PROTO_Z3950,
00097 15,
00098 1024*1024,
00099 "default-config",
00100 "",
00101 0,
00102 0,
00103 check_options,
00104 check_ip_tcpd,
00105 "",
00106 0,
00107 0,
00108 0,
00109 0,
00110 #ifdef WIN32
00111 "Z39.50 Server",
00112 "Server",
00113 "",
00114 "Z39.50 Server",
00115 #endif
00116 0,
00117 "",
00118 0,
00119 "",
00120 ""
00121 };
00122
00123 static int max_sessions = 0;
00124
00125 static int logbits_set = 0;
00126 static int log_session = 0;
00127 static int log_sessiondetail = 0;
00128 static int log_server = 0;
00129
00131 static void get_logbits(int force)
00132 {
00133 if (force || !logbits_set)
00134 {
00135 logbits_set = 1;
00136 log_session = yaz_log_module_level("session");
00137 log_sessiondetail = yaz_log_module_level("sessiondetail");
00138 log_server = yaz_log_module_level("server");
00139 }
00140 }
00141
00142
00143 static int add_listener(char *where, int listen_id);
00144
00145 #if YAZ_HAVE_XML2
00146 static xmlDocPtr xml_config_doc = 0;
00147 #endif
00148
00149 #if YAZ_HAVE_XML2
00150 static xmlNodePtr xml_config_get_root(void)
00151 {
00152 xmlNodePtr ptr = 0;
00153 if (xml_config_doc)
00154 {
00155 ptr = xmlDocGetRootElement(xml_config_doc);
00156 if (!ptr || ptr->type != XML_ELEMENT_NODE ||
00157 strcmp((const char *) ptr->name, "yazgfs"))
00158 {
00159 yaz_log(YLOG_WARN, "Bad/missing root element for config %s",
00160 control_block.xml_config);
00161 return 0;
00162
00163 }
00164 }
00165 return ptr;
00166 }
00167 #endif
00168
00169 #if YAZ_HAVE_XML2
00170 static char *nmem_dup_xml_content(NMEM n, xmlNodePtr ptr)
00171 {
00172 unsigned char *cp;
00173 xmlNodePtr p;
00174 int len = 1;
00175 unsigned char *str;
00176 int first = 1;
00177
00178 for (p = ptr; p; p = p->next)
00179 {
00180 if (p->type == XML_TEXT_NODE)
00181 len += xmlStrlen(p->content);
00182 }
00183
00184 str = (unsigned char *) nmem_malloc(n, len);
00185 *str = '\0';
00186 for (p = ptr; p; p = p->next)
00187 {
00188 if (p->type == XML_TEXT_NODE)
00189 {
00190 cp = p->content;
00191 if (first)
00192 {
00193 while(*cp && isspace(*cp))
00194 cp++;
00195 if (*cp)
00196 first = 0;
00197 }
00198 strcat((char *)str, (const char *)cp);
00199 }
00200 }
00201
00202 cp = strlen((const char *)str) + str;
00203 while (cp != str && isspace(cp[-1]))
00204 cp--;
00205 *cp = '\0';
00206
00207 return (char *) str;
00208 }
00209 #endif
00210
00211 #if YAZ_HAVE_XML2
00212 static struct gfs_server * gfs_server_new(void)
00213 {
00214 struct gfs_server *n = (struct gfs_server *)
00215 nmem_malloc(gfs_nmem, sizeof(*n));
00216 memcpy(&n->cb, &control_block, sizeof(control_block));
00217 n->next = 0;
00218 n->host = 0;
00219 n->listen_ref = 0;
00220 n->cql_transform = 0;
00221 n->ccl_transform = 0;
00222 n->server_node_ptr = 0;
00223 n->directory = 0;
00224 n->docpath = 0;
00225 n->stylesheet = 0;
00226 n->retrieval = yaz_retrieval_create();
00227 return n;
00228 }
00229 #endif
00230
00231 #if YAZ_HAVE_XML2
00232 static struct gfs_listen * gfs_listen_new(const char *id,
00233 const char *address)
00234 {
00235 struct gfs_listen *n = (struct gfs_listen *)
00236 nmem_malloc(gfs_nmem, sizeof(*n));
00237 if (id)
00238 n->id = nmem_strdup(gfs_nmem, id);
00239 else
00240 n->id = 0;
00241 n->next = 0;
00242 n->address = nmem_strdup(gfs_nmem, address);
00243 return n;
00244 }
00245 #endif
00246
00247 static void gfs_server_chdir(struct gfs_server *gfs)
00248 {
00249 if (gfs_root_dir[0])
00250 {
00251 if (chdir(gfs_root_dir))
00252 yaz_log(YLOG_WARN|YLOG_ERRNO, "chdir %s", gfs_root_dir);
00253 }
00254 if (gfs->directory)
00255 {
00256 if (chdir(gfs->directory))
00257 yaz_log(YLOG_WARN|YLOG_ERRNO, "chdir %s",
00258 gfs->directory);
00259 }
00260 }
00261
00262 int control_association(association *assoc, const char *host, int force_open)
00263 {
00264 char vhost[128], *cp;
00265 if (host)
00266 {
00267 strncpy(vhost, host, 127);
00268 vhost[127] = '\0';
00269 cp = strchr(vhost, ':');
00270 if (cp)
00271 *cp = '\0';
00272 host = vhost;
00273 }
00274 assoc->server = 0;
00275 if (control_block.xml_config[0])
00276 {
00277 struct gfs_server *gfs;
00278 for (gfs = gfs_server_list; gfs; gfs = gfs->next)
00279 {
00280 int listen_match = 0;
00281 int host_match = 0;
00282 if ( !gfs->host || (host && gfs->host && !strcmp(host, gfs->host)))
00283 host_match = 1;
00284 if (!gfs->listen_ref ||
00285 gfs->listen_ref == assoc->client_chan->chan_id)
00286 listen_match = 1;
00287 if (listen_match && host_match)
00288 {
00289 if (force_open ||
00290 (assoc->last_control != &gfs->cb && assoc->backend))
00291 {
00292 statserv_setcontrol(assoc->last_control);
00293 if (assoc->backend && assoc->init)
00294 {
00295 gfs_server_chdir(gfs);
00296 (assoc->last_control->bend_close)(assoc->backend);
00297 }
00298 assoc->backend = 0;
00299 xfree(assoc->init);
00300 assoc->init = 0;
00301 }
00302 assoc->server = gfs;
00303 assoc->last_control = &gfs->cb;
00304 statserv_setcontrol(&gfs->cb);
00305
00306 gfs_server_chdir(gfs);
00307 break;
00308 }
00309 }
00310 if (!gfs)
00311 {
00312 statserv_setcontrol(0);
00313 assoc->last_control = 0;
00314 return 0;
00315 }
00316 }
00317 else
00318 {
00319 statserv_setcontrol(&control_block);
00320 assoc->last_control = &control_block;
00321 }
00322 yaz_log(YLOG_DEBUG, "server select: config=%s",
00323 assoc->last_control->configname);
00324
00325 assoc->maximumRecordSize = assoc->last_control->maxrecordsize;
00326 assoc->preferredMessageSize = assoc->last_control->maxrecordsize;
00327 cs_set_max_recv_bytes(assoc->client_link, assoc->maximumRecordSize);
00328 return 1;
00329 }
00330
00331 #if YAZ_HAVE_XML2
00332 static void xml_config_read(void)
00333 {
00334 struct gfs_server **gfsp = &gfs_server_list;
00335 struct gfs_listen **gfslp = &gfs_listen_list;
00336 xmlNodePtr ptr = xml_config_get_root();
00337
00338 if (!ptr)
00339 return;
00340 for (ptr = ptr->children; ptr; ptr = ptr->next)
00341 {
00342 struct _xmlAttr *attr;
00343 if (ptr->type != XML_ELEMENT_NODE)
00344 continue;
00345 attr = ptr->properties;
00346 if (!strcmp((const char *) ptr->name, "listen"))
00347 {
00348
00349
00350
00351 const char *id = 0;
00352 const char *address =
00353 nmem_dup_xml_content(gfs_nmem, ptr->children);
00354 for ( ; attr; attr = attr->next)
00355 if (!xmlStrcmp(attr->name, BAD_CAST "id")
00356 && attr->children && attr->children->type == XML_TEXT_NODE)
00357 id = nmem_dup_xml_content(gfs_nmem, attr->children);
00358 if (address)
00359 {
00360 *gfslp = gfs_listen_new(id, address);
00361 gfslp = &(*gfslp)->next;
00362 *gfslp = 0;
00363 }
00364 }
00365 else if (!strcmp((const char *) ptr->name, "server"))
00366 {
00367 xmlNodePtr ptr_server = ptr;
00368 xmlNodePtr ptr;
00369 const char *listenref = 0;
00370 const char *id = 0;
00371 struct gfs_server *gfs;
00372
00373 for ( ; attr; attr = attr->next)
00374 if (!xmlStrcmp(attr->name, BAD_CAST "listenref")
00375 && attr->children && attr->children->type == XML_TEXT_NODE)
00376 listenref = nmem_dup_xml_content(gfs_nmem, attr->children);
00377 else if (!xmlStrcmp(attr->name, BAD_CAST "id")
00378 && attr->children
00379 && attr->children->type == XML_TEXT_NODE)
00380 id = nmem_dup_xml_content(gfs_nmem, attr->children);
00381 else
00382 yaz_log(YLOG_WARN, "Unknown attribute '%s' for server",
00383 attr->name);
00384 gfs = *gfsp = gfs_server_new();
00385 gfs->server_node_ptr = ptr_server;
00386 if (listenref)
00387 {
00388 int id_no;
00389 struct gfs_listen *gl = gfs_listen_list;
00390 for (id_no = 1; gl; gl = gl->next, id_no++)
00391 if (gl->id && !strcmp(gl->id, listenref))
00392 {
00393 gfs->listen_ref = id_no;
00394 break;
00395 }
00396 if (!gl)
00397 yaz_log(YLOG_WARN, "Non-existent listenref '%s' in server "
00398 "config element", listenref);
00399 }
00400 for (ptr = ptr_server->children; ptr; ptr = ptr->next)
00401 {
00402 if (ptr->type != XML_ELEMENT_NODE)
00403 continue;
00404 if (!strcmp((const char *) ptr->name, "host"))
00405 {
00406 gfs->host = nmem_dup_xml_content(gfs_nmem,
00407 ptr->children);
00408 }
00409 else if (!strcmp((const char *) ptr->name, "config"))
00410 {
00411 strcpy(gfs->cb.configname,
00412 nmem_dup_xml_content(gfs_nmem, ptr->children));
00413 }
00414 else if (!strcmp((const char *) ptr->name, "cql2rpn"))
00415 {
00416 gfs->cql_transform = cql_transform_open_fname(
00417 nmem_dup_xml_content(gfs_nmem, ptr->children)
00418 );
00419 }
00420 else if (!strcmp((const char *) ptr->name, "ccl2rpn"))
00421 {
00422 char *name;
00423 FILE *f;
00424
00425 name = nmem_dup_xml_content(gfs_nmem, ptr->children);
00426 if ((f = fopen(name, "r")) == 0) {
00427 yaz_log(YLOG_FATAL, "can't open CCL file '%s'", name);
00428 exit(1);
00429 }
00430 gfs->ccl_transform = ccl_qual_mk();
00431 ccl_qual_file (gfs->ccl_transform, f);
00432 fclose(f);
00433 }
00434 else if (!strcmp((const char *) ptr->name, "directory"))
00435 {
00436 gfs->directory =
00437 nmem_dup_xml_content(gfs_nmem, ptr->children);
00438 }
00439 else if (!strcmp((const char *) ptr->name, "docpath"))
00440 {
00441 gfs->docpath =
00442 nmem_dup_xml_content(gfs_nmem, ptr->children);
00443 }
00444 else if (!strcmp((const char *) ptr->name, "maximumrecordsize"))
00445 {
00446 gfs->cb.maxrecordsize = atoi(
00447 nmem_dup_xml_content(gfs_nmem, ptr->children));
00448 }
00449 else if (!strcmp((const char *) ptr->name, "stylesheet"))
00450 {
00451 char *s = nmem_dup_xml_content(gfs_nmem, ptr->children);
00452 gfs->stylesheet = (char *)
00453 nmem_malloc(gfs_nmem, strlen(s) + 2);
00454 sprintf(gfs->stylesheet, "/%s", s);
00455 }
00456 else if (!strcmp((const char *) ptr->name, "explain"))
00457 {
00458 ;
00459 }
00460 else if (!strcmp((const char *) ptr->name, "retrievalinfo"))
00461 {
00462 if (yaz_retrieval_configure(gfs->retrieval, ptr))
00463 {
00464 yaz_log(YLOG_FATAL, "%s in config %s",
00465 yaz_retrieval_get_error(gfs->retrieval),
00466 control_block.xml_config);
00467 exit(1);
00468 }
00469 }
00470 else
00471 {
00472 yaz_log(YLOG_FATAL, "Unknown element '%s' in config %s",
00473 ptr->name, control_block.xml_config);
00474 exit(1);
00475 }
00476 }
00477 gfsp = &(*gfsp)->next;
00478 }
00479 }
00480 *gfsp = 0;
00481 }
00482 #endif
00483
00484 static void xml_config_open(void)
00485 {
00486 if (!getcwd(gfs_root_dir, FILENAME_MAX))
00487 {
00488 yaz_log(YLOG_WARN|YLOG_ERRNO, "getcwd failed");
00489 gfs_root_dir[0] = '\0';
00490 }
00491 #ifdef WIN32
00492 init_control_tls = 1;
00493 current_control_tls = TlsAlloc();
00494 #elif YAZ_POSIX_THREADS
00495 init_control_tls = 1;
00496 pthread_key_create(¤t_control_tls, 0);
00497 #endif
00498
00499 gfs_nmem = nmem_create();
00500 #if YAZ_HAVE_XML2
00501 if (control_block.xml_config[0] == '\0')
00502 return;
00503
00504 if (!xml_config_doc)
00505 {
00506 xml_config_doc = xmlParseFile(control_block.xml_config);
00507 if (!xml_config_doc)
00508 {
00509 yaz_log(YLOG_FATAL, "Could not parse %s", control_block.xml_config);
00510 exit(1);
00511 }
00512 else
00513 {
00514 int noSubstitutions = xmlXIncludeProcess(xml_config_doc);
00515 if (noSubstitutions == -1)
00516 {
00517 yaz_log(YLOG_WARN, "XInclude processing failed for config %s",
00518 control_block.xml_config);
00519 exit(1);
00520 }
00521 }
00522 }
00523 xml_config_read();
00524 #endif
00525 }
00526
00527 static void xml_config_close(void)
00528 {
00529 #if YAZ_HAVE_XML2
00530 if (xml_config_doc)
00531 {
00532 xmlFreeDoc(xml_config_doc);
00533 xml_config_doc = 0;
00534 }
00535 #endif
00536 gfs_server_list = 0;
00537 nmem_destroy(gfs_nmem);
00538 #ifdef WIN32
00539 if (init_control_tls)
00540 TlsFree(current_control_tls);
00541 #elif YAZ_POSIX_THREADS
00542 if (init_control_tls)
00543 pthread_key_delete(current_control_tls);
00544 #endif
00545 }
00546
00547 static void xml_config_add_listeners(void)
00548 {
00549 struct gfs_listen *gfs = gfs_listen_list;
00550 int id_no;
00551
00552 for (id_no = 1; gfs; gfs = gfs->next, id_no++)
00553 {
00554 if (gfs->address)
00555 add_listener(gfs->address, id_no);
00556 }
00557 }
00558
00559 static void xml_config_bend_start(void)
00560 {
00561 if (control_block.xml_config[0])
00562 {
00563 struct gfs_server *gfs = gfs_server_list;
00564 for (; gfs; gfs = gfs->next)
00565 {
00566 yaz_log(YLOG_DEBUG, "xml_config_bend_start config=%s",
00567 gfs->cb.configname);
00568 statserv_setcontrol(&gfs->cb);
00569 if (control_block.bend_start)
00570 {
00571 gfs_server_chdir(gfs);
00572 (control_block.bend_start)(&gfs->cb);
00573 }
00574 }
00575 }
00576 else
00577 {
00578 yaz_log(YLOG_DEBUG, "xml_config_bend_start default config");
00579 statserv_setcontrol(&control_block);
00580 if (control_block.bend_start)
00581 (*control_block.bend_start)(&control_block);
00582 }
00583 }
00584
00585 static void xml_config_bend_stop(void)
00586 {
00587 if (control_block.xml_config[0])
00588 {
00589 struct gfs_server *gfs = gfs_server_list;
00590 for (; gfs; gfs = gfs->next)
00591 {
00592 yaz_log(YLOG_DEBUG, "xml_config_bend_stop config=%s",
00593 gfs->cb.configname);
00594 statserv_setcontrol(&gfs->cb);
00595 if (control_block.bend_stop)
00596 (control_block.bend_stop)(&gfs->cb);
00597 }
00598 }
00599 else
00600 {
00601 yaz_log(YLOG_DEBUG, "xml_config_bend_stop default config");
00602 statserv_setcontrol(&control_block);
00603 if (control_block.bend_stop)
00604 (*control_block.bend_stop)(&control_block);
00605 }
00606 }
00607
00608
00609
00610
00611
00612
00613
00614 #ifdef WIN32
00615
00616 typedef struct _ThreadList ThreadList;
00617
00618 struct _ThreadList
00619 {
00620 HANDLE hThread;
00621 IOCHAN pIOChannel;
00622 ThreadList *pNext;
00623 };
00624
00625 static ThreadList *pFirstThread;
00626 static CRITICAL_SECTION Thread_CritSect;
00627 static BOOL bInitialized = FALSE;
00628
00629 static void ThreadList_Initialize()
00630 {
00631
00632 InitializeCriticalSection(&Thread_CritSect);
00633
00634
00635 pFirstThread = NULL;
00636
00637
00638 bInitialized = TRUE;
00639 }
00640
00641 static void statserv_add(HANDLE hThread, IOCHAN pIOChannel)
00642 {
00643
00644 EnterCriticalSection(&Thread_CritSect);
00645
00646 {
00647
00648 ThreadList *pNewThread = (ThreadList *)malloc(sizeof(ThreadList));
00649 pNewThread->hThread = hThread;
00650 pNewThread->pIOChannel = pIOChannel;
00651 pNewThread->pNext = pFirstThread;
00652 pFirstThread = pNewThread;
00653
00654
00655 LeaveCriticalSection(&Thread_CritSect);
00656 }
00657 }
00658
00659 void statserv_remove(IOCHAN pIOChannel)
00660 {
00661
00662 EnterCriticalSection(&Thread_CritSect);
00663
00664 {
00665 ThreadList *pCurrentThread = pFirstThread;
00666 ThreadList *pNextThread;
00667 ThreadList *pPrevThread =NULL;
00668
00669
00670 for (; pCurrentThread != NULL; pCurrentThread = pNextThread)
00671 {
00672
00673 if (pCurrentThread->pIOChannel == pIOChannel)
00674 {
00675
00676
00677 if (pPrevThread == NULL)
00678 pFirstThread = pCurrentThread->pNext;
00679 else
00680 pPrevThread->pNext = pCurrentThread->pNext;
00681
00682
00683 free(pCurrentThread);
00684
00685
00686 pNextThread = NULL;
00687 }
00688 else
00689 {
00690
00691 pNextThread = pCurrentThread->pNext;
00692 pPrevThread = pCurrentThread;
00693 }
00694 }
00695
00696
00697 LeaveCriticalSection(&Thread_CritSect);
00698 }
00699 }
00700
00701
00702 static void statserv_closedown()
00703 {
00704
00705 if (bInitialized)
00706 {
00707 int iHandles = 0;
00708 HANDLE *pThreadHandles = NULL;
00709
00710
00711
00712 EnterCriticalSection(&Thread_CritSect);
00713
00714 {
00715
00716
00717 ThreadList *pCurrentThread = pFirstThread;
00718
00719
00720 if (pListener != NULL)
00721 iochan_destroy(pListener);
00722
00723 for (; pCurrentThread != NULL; pCurrentThread = pCurrentThread->pNext)
00724 {
00725
00726 iochan_destroy(pCurrentThread->pIOChannel);
00727 closesocket(pCurrentThread->pIOChannel->fd);
00728
00729
00730 iHandles++;
00731 }
00732
00733 if (iHandles > 0)
00734 {
00735 HANDLE *pCurrentHandle ;
00736
00737
00738 pThreadHandles = (HANDLE *)malloc(sizeof(HANDLE) * iHandles);
00739 pCurrentHandle = pThreadHandles;
00740
00741 for (pCurrentThread = pFirstThread;
00742 pCurrentThread != NULL;
00743 pCurrentThread = pCurrentThread->pNext, pCurrentHandle++)
00744 {
00745
00746 *pCurrentHandle = pCurrentThread->hThread;
00747 }
00748 }
00749
00750
00751 LeaveCriticalSection(&Thread_CritSect);
00752 }
00753
00754
00755 if (iHandles > 0)
00756 {
00757 yaz_log(log_server, "waiting for %d to die", iHandles);
00758
00759 WaitForMultipleObjects(iHandles, pThreadHandles, TRUE, INFINITE);
00760
00761
00762 free(pThreadHandles);
00763 }
00764
00765 xml_config_bend_stop();
00766
00767 DeleteCriticalSection(&Thread_CritSect);
00768 }
00769 xml_config_close();
00770 }
00771
00772 void __cdecl event_loop_thread(IOCHAN iochan)
00773 {
00774 iochan_event_loop(&iochan);
00775 }
00776
00777
00778 static void listener(IOCHAN h, int event)
00779 {
00780 COMSTACK line = (COMSTACK) iochan_getdata(h);
00781 IOCHAN parent_chan = line->user;
00782 association *newas;
00783 int res;
00784 HANDLE newHandle;
00785
00786 if (event == EVENT_INPUT)
00787 {
00788 COMSTACK new_line;
00789 IOCHAN new_chan;
00790
00791 if ((res = cs_listen(line, 0, 0)) < 0)
00792 {
00793 yaz_log(YLOG_FATAL|YLOG_ERRNO, "cs_listen failed");
00794 return;
00795 }
00796 else if (res == 1)
00797 return;
00798 yaz_log(YLOG_DEBUG, "listen ok");
00799 new_line = cs_accept(line);
00800 if (!new_line)
00801 {
00802 yaz_log(YLOG_FATAL, "Accept failed.");
00803 return;
00804 }
00805 yaz_log(YLOG_DEBUG, "Accept ok");
00806
00807 if (!(new_chan = iochan_create(cs_fileno(new_line), ir_session,
00808 EVENT_INPUT, parent_chan->chan_id)))
00809 {
00810 yaz_log(YLOG_FATAL, "Failed to create iochan");
00811 iochan_destroy(h);
00812 return;
00813 }
00814
00815 yaz_log(YLOG_DEBUG, "Creating association");
00816 if (!(newas = create_association(new_chan, new_line,
00817 control_block.apdufile)))
00818 {
00819 yaz_log(YLOG_FATAL, "Failed to create new assoc.");
00820 iochan_destroy(h);
00821 return;
00822 }
00823 newas->cs_get_mask = EVENT_INPUT;
00824 newas->cs_put_mask = 0;
00825 newas->cs_accept_mask = 0;
00826
00827 yaz_log(YLOG_DEBUG, "Setting timeout %d", control_block.idle_timeout);
00828 iochan_setdata(new_chan, newas);
00829 iochan_settimeout(new_chan, 60);
00830
00831
00832
00833 newHandle = (HANDLE) _beginthread(event_loop_thread, 0, new_chan);
00834 if (newHandle == (HANDLE) -1)
00835 {
00836
00837 yaz_log(YLOG_FATAL|YLOG_ERRNO, "Failed to create new thread.");
00838 iochan_destroy(h);
00839 return;
00840 }
00841
00842 statserv_add(newHandle, new_chan);
00843
00844 yaz_log(YLOG_DEBUG, "Created new thread, id = %ld iochan %p",(long) newHandle, new_chan);
00845 iochan_setflags(h, EVENT_INPUT | EVENT_EXCEPT);
00846 }
00847 else
00848 {
00849 yaz_log(YLOG_FATAL, "Bad event on listener.");
00850 iochan_destroy(h);
00851 return;
00852 }
00853 }
00854
00855 int statserv_must_terminate(void)
00856 {
00857 return 0;
00858 }
00859
00860 #else
00861
00862 static int term_flag = 0;
00863
00864
00865
00866 int statserv_must_terminate(void)
00867 {
00868 return term_flag;
00869 }
00870
00871 void statserv_remove(IOCHAN pIOChannel)
00872 {
00873 }
00874
00875 static void statserv_closedown(void)
00876 {
00877 IOCHAN p;
00878
00879 xml_config_bend_stop();
00880 for (p = pListener; p; p = p->next)
00881 {
00882 iochan_destroy(p);
00883 }
00884 xml_config_close();
00885 }
00886
00887 void sigterm(int sig)
00888 {
00889 term_flag = 1;
00890 }
00891
00892 static void *new_session(void *vp);
00893 static int no_sessions = 0;
00894
00895
00896 static void listener(IOCHAN h, int event)
00897 {
00898 COMSTACK line = (COMSTACK) iochan_getdata(h);
00899 int res;
00900
00901 if (event == EVENT_INPUT)
00902 {
00903 COMSTACK new_line;
00904 if ((res = cs_listen_check(line, 0, 0, control_block.check_ip,
00905 control_block.daemon_name)) < 0)
00906 {
00907 yaz_log(YLOG_WARN|YLOG_ERRNO, "cs_listen failed");
00908 return;
00909 }
00910 else if (res == 1)
00911 {
00912 yaz_log(YLOG_WARN, "cs_listen incomplete");
00913 return;
00914 }
00915 new_line = cs_accept(line);
00916 if (!new_line)
00917 {
00918 yaz_log(YLOG_FATAL, "Accept failed.");
00919 iochan_setflags(h, EVENT_INPUT | EVENT_EXCEPT);
00920 return;
00921 }
00922
00923 yaz_log(log_sessiondetail, "Connect from %s", cs_addrstr(new_line));
00924
00925 no_sessions++;
00926 if (control_block.dynamic)
00927 {
00928 if ((res = fork()) < 0)
00929 {
00930 yaz_log(YLOG_FATAL|YLOG_ERRNO, "fork");
00931 iochan_destroy(h);
00932 return;
00933 }
00934 else if (res == 0)
00935 {
00936 char nbuf[100];
00937 IOCHAN pp;
00938
00939 for (pp = pListener; pp; pp = iochan_getnext(pp))
00940 {
00941 COMSTACK l = (COMSTACK)iochan_getdata(pp);
00942 cs_close(l);
00943 iochan_destroy(pp);
00944 }
00945 sprintf(nbuf, "%s(%d)", me, no_sessions);
00946 yaz_log_init_prefix(nbuf);
00947
00948
00949 control_block.bend_stop = 0;
00950 }
00951 else
00952 {
00953 cs_close(new_line);
00954 return;
00955 }
00956 }
00957
00958 if (control_block.threads)
00959 {
00960 #if YAZ_POSIX_THREADS
00961 pthread_t child_thread;
00962 pthread_create(&child_thread, 0, new_session, new_line);
00963 pthread_detach(child_thread);
00964 #elif YAZ_GNU_THREADS
00965 pth_attr_t attr;
00966 pth_t child_thread;
00967
00968 attr = pth_attr_new();
00969 pth_attr_set(attr, PTH_ATTR_JOINABLE, FALSE);
00970 pth_attr_set(attr, PTH_ATTR_STACK_SIZE, 32*1024);
00971 pth_attr_set(attr, PTH_ATTR_NAME, "session");
00972 yaz_log(YLOG_DEBUG, "pth_spawn begin");
00973 child_thread = pth_spawn(attr, new_session, new_line);
00974 yaz_log(YLOG_DEBUG, "pth_spawn finish");
00975 pth_attr_destroy(attr);
00976 #else
00977 new_session(new_line);
00978 #endif
00979 }
00980 else
00981 new_session(new_line);
00982 }
00983 else if (event == EVENT_TIMEOUT)
00984 {
00985 yaz_log(log_server, "Shutting down listener.");
00986 iochan_destroy(h);
00987 }
00988 else
00989 {
00990 yaz_log(YLOG_FATAL, "Bad event on listener.");
00991 iochan_destroy(h);
00992 }
00993 }
00994
00995 static void *new_session(void *vp)
00996 {
00997 char *a;
00998 association *newas;
00999 IOCHAN new_chan;
01000 COMSTACK new_line = (COMSTACK) vp;
01001 IOCHAN parent_chan = (IOCHAN) new_line->user;
01002
01003 unsigned cs_get_mask, cs_accept_mask, mask =
01004 ((new_line->io_pending & CS_WANT_WRITE) ? EVENT_OUTPUT : 0) |
01005 ((new_line->io_pending & CS_WANT_READ) ? EVENT_INPUT : 0);
01006
01007 if (mask)
01008 {
01009 cs_accept_mask = mask;
01010 cs_get_mask = 0;
01011 }
01012 else
01013 {
01014 cs_accept_mask = 0;
01015 cs_get_mask = mask = EVENT_INPUT;
01016 }
01017
01018 if (!(new_chan = iochan_create(cs_fileno(new_line), ir_session, mask,
01019 parent_chan->chan_id)))
01020 {
01021 yaz_log(YLOG_FATAL, "Failed to create iochan");
01022 return 0;
01023 }
01024 if (!(newas = create_association(new_chan, new_line,
01025 control_block.apdufile)))
01026 {
01027 yaz_log(YLOG_FATAL, "Failed to create new assoc.");
01028 return 0;
01029 }
01030 newas->cs_accept_mask = cs_accept_mask;
01031 newas->cs_get_mask = cs_get_mask;
01032
01033 iochan_setdata(new_chan, newas);
01034 iochan_settimeout(new_chan, 60);
01035 #if 1
01036 a = cs_addrstr(new_line);
01037 #else
01038 a = 0;
01039 #endif
01040 yaz_log_xml_errors(0, YLOG_WARN);
01041 yaz_log(log_session, "Session - OK %d %s %ld",
01042 no_sessions, a ? a : "[Unknown]", (long) getpid());
01043 if (max_sessions && no_sessions >= max_sessions)
01044 control_block.one_shot = 1;
01045 if (control_block.threads)
01046 {
01047 iochan_event_loop(&new_chan);
01048 }
01049 else
01050 {
01051 new_chan->next = pListener;
01052 pListener = new_chan;
01053 }
01054 return 0;
01055 }
01056
01057
01058 #endif
01059
01060 static void inetd_connection(int what)
01061 {
01062 COMSTACK line;
01063 IOCHAN chan;
01064 association *assoc;
01065 char *addr;
01066
01067 if ((line = cs_createbysocket(0, tcpip_type, 0, what)))
01068 {
01069 if ((chan = iochan_create(cs_fileno(line), ir_session, EVENT_INPUT,
01070 0)))
01071 {
01072 if ((assoc = create_association(chan, line,
01073 control_block.apdufile)))
01074 {
01075 iochan_setdata(chan, assoc);
01076 iochan_settimeout(chan, 60);
01077 addr = cs_addrstr(line);
01078 yaz_log(log_sessiondetail, "Inetd association from %s",
01079 addr ? addr : "[UNKNOWN]");
01080 assoc->cs_get_mask = EVENT_INPUT;
01081 }
01082 else
01083 {
01084 yaz_log(YLOG_FATAL, "Failed to create association structure");
01085 }
01086 chan->next = pListener;
01087 pListener = chan;
01088 }
01089 else
01090 {
01091 yaz_log(YLOG_FATAL, "Failed to create iochan");
01092 }
01093 }
01094 else
01095 {
01096 yaz_log(YLOG_ERRNO|YLOG_FATAL, "Failed to create comstack on socket 0");
01097 }
01098 }
01099
01100
01101
01102
01103 static int add_listener(char *where, int listen_id)
01104 {
01105 COMSTACK l;
01106 void *ap;
01107 IOCHAN lst = NULL;
01108 const char *mode;
01109
01110 if (control_block.dynamic)
01111 mode = "dynamic";
01112 else if (control_block.threads)
01113 mode = "threaded";
01114 else
01115 mode = "static";
01116
01117 yaz_log(log_server, "Adding %s listener on %s id=%d", mode, where,
01118 listen_id);
01119
01120 l = cs_create_host(where, 2, &ap);
01121 if (!l)
01122 {
01123 yaz_log(YLOG_FATAL, "Failed to listen on %s", where);
01124 return -1;
01125 }
01126 if (*control_block.cert_fname)
01127 cs_set_ssl_certificate_file(l, control_block.cert_fname);
01128
01129 if (cs_bind(l, ap, CS_SERVER) < 0)
01130 {
01131 if (cs_errno(l) == CSYSERR)
01132 yaz_log(YLOG_FATAL|YLOG_ERRNO, "Failed to bind to %s", where);
01133 else
01134 yaz_log(YLOG_FATAL, "Failed to bind to %s: %s", where,
01135 cs_strerror(l));
01136 cs_close(l);
01137 return -1;
01138 }
01139 if (!(lst = iochan_create(cs_fileno(l), listener, EVENT_INPUT |
01140 EVENT_EXCEPT, listen_id)))
01141 {
01142 yaz_log(YLOG_FATAL|YLOG_ERRNO, "Failed to create IOCHAN-type");
01143 cs_close(l);
01144 return -1;
01145 }
01146 iochan_setdata(lst, l);
01147 l->user = lst;
01148
01149
01150 lst->next = pListener;
01151 pListener = lst;
01152 return 0;
01153 }
01154
01155 #ifndef WIN32
01156
01157 static void catchchld(int num)
01158 {
01159 while (waitpid(-1, 0, WNOHANG) > 0)
01160 ;
01161 signal(SIGCHLD, catchchld);
01162 }
01163 #endif
01164
01165 statserv_options_block *statserv_getcontrol(void)
01166 {
01167 #ifdef WIN32
01168 if (init_control_tls)
01169 return (statserv_options_block *) TlsGetValue(current_control_tls);
01170 else
01171 return &control_block;
01172 #elif YAZ_POSIX_THREADS
01173 if (init_control_tls)
01174 return (statserv_options_block *)
01175 pthread_getspecific(current_control_tls);
01176 else
01177 return &control_block;
01178 #else
01179 if (current_control_block)
01180 return current_control_block;
01181 return &control_block;
01182 #endif
01183 }
01184
01185 void statserv_setcontrol(statserv_options_block *block)
01186 {
01187 if (gfs_root_dir[0])
01188 {
01189 if (chdir(gfs_root_dir))
01190 yaz_log(YLOG_WARN|YLOG_ERRNO, "chdir %s", gfs_root_dir);
01191 }
01192 #ifdef WIN32
01193 if (init_control_tls)
01194 TlsSetValue(current_control_tls, block);
01195 #elif YAZ_POSIX_THREADS
01196 if (init_control_tls)
01197 pthread_setspecific(current_control_tls, block);
01198 #else
01199 current_control_block = block;
01200 #endif
01201 }
01202
01203 static void statserv_reset(void)
01204 {
01205 }
01206
01207 static int statserv_sc_main(yaz_sc_t s, int argc, char **argv)
01208 {
01209 char sep;
01210 #ifdef WIN32
01211
01212 ThreadList_Initialize();
01213
01214 #endif
01215
01216
01217 #ifdef WIN32
01218 sep = '\\';
01219 #else
01220 sep = '/';
01221 #endif
01222 if ((me = strrchr(argv[0], sep)))
01223 me++;
01224 else
01225 me = argv[0];
01226 programname = argv[0];
01227
01228 if (control_block.options_func(argc, argv))
01229 return 1;
01230
01231 xml_config_open();
01232
01233 xml_config_bend_start();
01234
01235 #ifdef WIN32
01236 xml_config_add_listeners();
01237
01238 yaz_log(log_server, "Starting server %s", me);
01239 if (!pListener && *control_block.default_listen)
01240 add_listener(control_block.default_listen, 0);
01241 #else
01242
01243 if (control_block.inetd)
01244 inetd_connection(control_block.default_proto);
01245 else
01246 {
01247 static int hand[2];
01248 if (control_block.background)
01249 {
01250
01251
01252 if (pipe(hand) < 0)
01253 {
01254 yaz_log(YLOG_FATAL|YLOG_ERRNO, "pipe");
01255 return 1;
01256 }
01257 switch (fork())
01258 {
01259 case 0:
01260 break;
01261 case -1:
01262 return 1;
01263 default:
01264 close(hand[1]);
01265 while(1)
01266 {
01267 char dummy[1];
01268 int res = read(hand[0], dummy, 1);
01269 if (res < 0 && yaz_errno() != EINTR)
01270 {
01271 yaz_log(YLOG_FATAL|YLOG_ERRNO, "read fork handshake");
01272 break;
01273 }
01274 else if (res >= 0)
01275 break;
01276 }
01277 close(hand[0]);
01278 _exit(0);
01279 }
01280
01281 close(hand[0]);
01282 if (setsid() < 0)
01283 return 1;
01284
01285 close(0);
01286 close(1);
01287 close(2);
01288 open("/dev/null", O_RDWR);
01289 if (dup(0) == -1)
01290 return 1;
01291 if (dup(0) == -1)
01292 return 1;
01293 }
01294 xml_config_add_listeners();
01295
01296 if (!pListener && *control_block.default_listen)
01297 add_listener(control_block.default_listen, 0);
01298
01299 if (!pListener)
01300 return 1;
01301
01302 if (*control_block.pid_fname)
01303 {
01304 FILE *f = fopen(control_block.pid_fname, "w");
01305 if (!f)
01306 {
01307 yaz_log(YLOG_FATAL|YLOG_ERRNO, "Couldn't create %s",
01308 control_block.pid_fname);
01309 exit(0);
01310 }
01311 fprintf(f, "%ld", (long) getpid());
01312 fclose(f);
01313 }
01314
01315 if (control_block.background)
01316 close(hand[1]);
01317
01318
01319 yaz_log(log_server, "Starting server %s pid=%ld", programname,
01320 (long) getpid());
01321 #if 0
01322 sigset_t sigs_to_block;
01323
01324 sigemptyset(&sigs_to_block);
01325 sigaddset(&sigs_to_block, SIGTERM);
01326 pthread_sigmask(SIG_BLOCK, &sigs_to_block, 0);
01327
01328 #endif
01329 if (control_block.dynamic)
01330 signal(SIGCHLD, catchchld);
01331 }
01332 signal(SIGPIPE, SIG_IGN);
01333 signal(SIGTERM, sigterm);
01334 if (*control_block.setuid)
01335 {
01336 struct passwd *pw;
01337
01338 if (!(pw = getpwnam(control_block.setuid)))
01339 {
01340 yaz_log(YLOG_FATAL, "%s: Unknown user", control_block.setuid);
01341 return(1);
01342 }
01343 if (setuid(pw->pw_uid) < 0)
01344 {
01345 yaz_log(YLOG_FATAL|YLOG_ERRNO, "setuid");
01346 exit(1);
01347 }
01348 }
01349
01350 #endif
01351 if (pListener == NULL)
01352 return 1;
01353 if (s)
01354 yaz_sc_running(s);
01355 yaz_log(YLOG_DEBUG, "Entering event loop.");
01356 return iochan_event_loop(&pListener);
01357 }
01358
01359 static void option_copy(char *dst, const char *src)
01360 {
01361 strncpy(dst, src ? src : "", 127);
01362 dst[127] = '\0';
01363 }
01364
01365 int check_options(int argc, char **argv)
01366 {
01367 int ret = 0, r;
01368 char *arg;
01369
01370 yaz_log_init_level(yaz_log_mask_str(STAT_DEFAULT_LOG_LEVEL));
01371
01372 get_logbits(1);
01373
01374 while ((ret = options("1a:iszSTl:v:u:c:w:t:k:d:A:p:DC:f:m:r:",
01375 argv, argc, &arg)) != -2)
01376 {
01377 switch (ret)
01378 {
01379 case 0:
01380 if (add_listener(arg, 0))
01381 return 1;
01382 break;
01383 case '1':
01384 control_block.one_shot = 1;
01385 control_block.dynamic = 0;
01386 break;
01387 case 'z':
01388 control_block.default_proto = PROTO_Z3950;
01389 break;
01390 case 's':
01391 fprintf(stderr, "%s: SR protocol no longer supported\n", me);
01392 exit(1);
01393 break;
01394 case 'S':
01395 control_block.dynamic = 0;
01396 break;
01397 case 'T':
01398 #if YAZ_POSIX_THREADS
01399 control_block.dynamic = 0;
01400 control_block.threads = 1;
01401 #elif YAZ_GNU_THREADS
01402 control_block.dynamic = 0;
01403 control_block.threads = 1;
01404 #else
01405 fprintf(stderr, "%s: Threaded mode not available.\n", me);
01406 return 1;
01407 #endif
01408 break;
01409 case 'l':
01410 option_copy(control_block.logfile, arg);
01411 yaz_log_init_file(control_block.logfile);
01412 break;
01413 case 'm':
01414 if (!arg) {
01415 fprintf(stderr, "%s: Specify time format for log file.\n", me);
01416 return(1);
01417 }
01418 yaz_log_time_format(arg);
01419 break;
01420 case 'v':
01421 yaz_log_init_level(yaz_log_mask_str(arg));
01422 get_logbits(1);
01423 break;
01424 case 'a':
01425 option_copy(control_block.apdufile, arg);
01426 break;
01427 case 'u':
01428 option_copy(control_block.setuid, arg);
01429 break;
01430 case 'c':
01431 option_copy(control_block.configname, arg);
01432 break;
01433 case 'C':
01434 option_copy(control_block.cert_fname, arg);
01435 break;
01436 case 'd':
01437 option_copy(control_block.daemon_name, arg);
01438 break;
01439 case 't':
01440 if (!arg || !(r = atoi(arg)))
01441 {
01442 fprintf(stderr, "%s: Specify positive timeout for -t.\n", me);
01443 return(1);
01444 }
01445 control_block.idle_timeout = r;
01446 break;
01447 case 'k':
01448 if (!arg || !(r = atoi(arg)))
01449 {
01450 fprintf(stderr, "%s: Specify positive size for -k.\n", me);
01451 return(1);
01452 }
01453 control_block.maxrecordsize = r * 1024;
01454 break;
01455 case 'i':
01456 control_block.inetd = 1;
01457 break;
01458 case 'w':
01459 if (chdir(arg))
01460 {
01461 perror(arg);
01462 return 1;
01463 }
01464 break;
01465 case 'A':
01466 max_sessions = atoi(arg);
01467 break;
01468 case 'p':
01469 option_copy(control_block.pid_fname, arg);
01470 break;
01471 case 'f':
01472 #if YAZ_HAVE_XML2
01473 option_copy(control_block.xml_config, arg);
01474 #else
01475 fprintf(stderr, "%s: Option -f unsupported since YAZ is compiled without Libxml2 support\n", me);
01476 exit(1);
01477 #endif
01478 break;
01479 case 'D':
01480 control_block.background = 1;
01481 break;
01482 case 'r':
01483 if (!arg || !(r = atoi(arg)))
01484 {
01485 fprintf(stderr, "%s: Specify positive size for -r.\n", me);
01486 return(1);
01487 }
01488 yaz_log_init_max_size(r * 1024);
01489 break;
01490 default:
01491 fprintf(stderr, "Usage: %s [ -a <pdufile> -v <loglevel>"
01492 " -l <logfile> -u <user> -c <config> -t <minutes>"
01493 " -k <kilobytes> -d <daemon> -p <pidfile> -C certfile"
01494 " -ziDST1 -m <time-format> -w <directory> <listener-addr>... ]\n", me);
01495 return 1;
01496 }
01497 }
01498 return 0;
01499 }
01500
01501 void statserv_sc_stop(yaz_sc_t s)
01502 {
01503 statserv_closedown();
01504 statserv_reset();
01505 }
01506
01507 int statserv_main(int argc, char **argv,
01508 bend_initresult *(*bend_init)(bend_initrequest *r),
01509 void (*bend_close)(void *handle))
01510 {
01511 int ret;
01512 struct statserv_options_block *cb = &control_block;
01513
01514
01515 yaz_sc_t s = yaz_sc_create(
01516 #ifdef WIN32
01517 cb->service_name, cb->service_display_name
01518 #else
01519 0, 0
01520 #endif
01521 );
01522
01523 cb->bend_init = bend_init;
01524 cb->bend_close = bend_close;
01525
01526 ret = yaz_sc_program(s, argc, argv, statserv_sc_main, statserv_sc_stop);
01527 yaz_sc_destroy(&s);
01528 return ret;
01529 }
01530
01531
01532
01533
01534
01535
01536
01537
01538
01539