/*
- * Copyright (c) 1995-2003, Index Data
+ * Copyright (C) 1995-2005, Index Data ApS
* See the file LICENSE for details.
- * Sebastian Hammer, Adam Dickmeiss
*
* NT threaded server code by
* Chas Woodfield, Fretwell Downing Informatics.
*
- * $Id: statserv.c,v 1.3 2004-01-15 10:05:56 adam Exp $
+ * $Id: statserv.c,v 1.19 2005-01-16 21:51:50 adam Exp $
+ */
+
+/**
+ * \file statserv.c
+ * \brief Implements GFS logic
*/
#include <stdio.h>
#include <winsock.h>
#include <direct.h>
#include "service.h"
-#else
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#if HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+#if HAVE_UNISTD_H
#include <unistd.h>
+#endif
+#if HAVE_PWD_H
#include <pwd.h>
#endif
static IOCHAN pListener = NULL;
-static char *me = "statserver";
+static char *me = "statserver"; /* log prefix */
+static char *programname="statserver"; /* full program name */
/*
* default behavior.
*/
+#define STAT_DEFAULT_LOG_LEVEL "none,fatal,warn,log,server,session,request"
+/* the 'none' clears yaz' own default settings, including [log] */
+
int check_options(int argc, char **argv);
statserv_options_block control_block = {
1, /* dynamic mode */
0, /* threaded mode */
0, /* one shot (single session) */
- LOG_DEFAULT_LEVEL, /* log level */
+ YLOG_DEFAULT_LEVEL, /* log level */
"", /* no PDUs */
"", /* diagnostic output to stderr */
"tcp:@:9999", /* default listener port */
"", /* NT Service Dependencies */
"Z39.50 Server", /* NT Service Display Name */
#endif /* WIN32 */
- 0 /* SOAP handlers */
+ 0, /* SOAP handlers */
+ "", /* PID fname */
+ 0, /* background daemon */
+ "" /* SSL certificate filename */
};
static int max_sessions = 0;
+static int logbits_set = 0;
+static int log_session = 0;
+static int log_server = 0;
+
+/** get_logbits sets global loglevel bits */
+static void get_logbits(int force)
+{ /* needs to be called after parsing cmd-line args that can set loglevels!*/
+ if (force || !logbits_set)
+ {
+ logbits_set = 1;
+ log_session = yaz_log_module_level("session");
+ log_server = yaz_log_module_level("server");
+ }
+}
+
+
/*
* handle incoming connect requests.
* The dynamic mode is a bit tricky mostly because we want to avoid
/* Now we can really do something */
if (iHandles > 0)
{
- logf (LOG_LOG, "waiting for %d to die", iHandles);
+ logf (log_server, "waiting for %d to die", iHandles);
/* This will now wait, until all the threads close */
WaitForMultipleObjects(iHandles, pThreadHandles, TRUE, INFINITE);
{
if ((res = cs_listen(line, 0, 0)) < 0)
{
- yaz_log(LOG_FATAL, "cs_listen failed");
+ yaz_log(YLOG_FATAL, "cs_listen failed");
return;
}
else if (res == 1)
return;
- yaz_log(LOG_DEBUG, "listen ok");
+ yaz_log(YLOG_DEBUG, "listen ok");
iochan_setevent(h, EVENT_OUTPUT);
iochan_setflags(h, EVENT_OUTPUT | EVENT_EXCEPT); /* set up for acpt */
}
if (!new_line)
{
- yaz_log(LOG_FATAL, "Accept failed.");
+ yaz_log(YLOG_FATAL, "Accept failed.");
iochan_setflags(h, EVENT_INPUT | EVENT_EXCEPT);
return;
}
- yaz_log(LOG_DEBUG, "Accept ok");
+ yaz_log(YLOG_DEBUG, "Accept ok");
if (!(new_chan = iochan_create(cs_fileno(new_line), ir_session,
EVENT_INPUT)))
{
- yaz_log(LOG_FATAL, "Failed to create iochan");
+ yaz_log(YLOG_FATAL, "Failed to create iochan");
iochan_destroy(h);
return;
}
- yaz_log(LOG_DEBUG, "Creating association");
+ yaz_log(YLOG_DEBUG, "Creating association");
if (!(newas = create_association(new_chan, new_line)))
{
- yaz_log(LOG_FATAL, "Failed to create new assoc.");
+ yaz_log(YLOG_FATAL, "Failed to create new assoc.");
iochan_destroy(h);
return;
}
newas->cs_put_mask = 0;
newas->cs_accept_mask = 0;
- yaz_log(LOG_DEBUG, "Setting timeout %d", control_block.idle_timeout);
+ yaz_log(YLOG_DEBUG, "Setting timeout %d", control_block.idle_timeout);
iochan_setdata(new_chan, newas);
iochan_settimeout(new_chan, 60);
if (newHandle == (HANDLE) -1)
{
- yaz_log(LOG_FATAL|LOG_ERRNO, "Failed to create new thread.");
+ yaz_log(YLOG_FATAL|YLOG_ERRNO, "Failed to create new thread.");
iochan_destroy(h);
return;
}
/* We successfully created the thread, so add it to the list */
statserv_add(newHandle, new_chan);
- yaz_log(LOG_DEBUG, "Created new thread, id = %ld iochan %p",(long) newHandle, new_chan);
+ yaz_log(YLOG_DEBUG, "Created new thread, id = %ld iochan %p",(long) newHandle, new_chan);
iochan_setflags(h, EVENT_INPUT | EVENT_EXCEPT); /* reset listener */
}
else
{
- yaz_log(LOG_FATAL, "Bad event on listener.");
+ yaz_log(YLOG_FATAL, "Bad event on listener.");
iochan_destroy(h);
return;
}
static void listener(IOCHAN h, int event)
{
COMSTACK line = (COMSTACK) iochan_getdata(h);
- static int hand[2];
- static int child = 0;
int res;
if (event == EVENT_INPUT)
{
- if (control_block.dynamic && !child)
+ COMSTACK new_line;
+ if ((res = cs_listen_check(line, 0, 0, control_block.check_ip,
+ control_block.daemon_name)) < 0)
+ {
+ yaz_log(YLOG_WARN|YLOG_ERRNO, "cs_listen failed");
+ return;
+ }
+ else if (res == 1)
+ {
+ yaz_log(YLOG_WARN, "cs_listen incomplete");
+ return;
+ }
+ new_line = cs_accept(line);
+ if (!new_line)
+ {
+ yaz_log(YLOG_FATAL, "Accept failed.");
+ iochan_setflags(h, EVENT_INPUT | EVENT_EXCEPT); /* reset listener */
+ return;
+ }
+ no_sessions++;
+ if (control_block.dynamic)
{
- int res;
-
- ++no_sessions;
- if (pipe(hand) < 0)
- {
- yaz_log(LOG_FATAL|LOG_ERRNO, "pipe");
- iochan_destroy(h);
- return;
- }
if ((res = fork()) < 0)
{
- yaz_log(LOG_FATAL|LOG_ERRNO, "fork");
+ yaz_log(YLOG_FATAL|YLOG_ERRNO, "fork");
iochan_destroy(h);
return;
}
char nbuf[100];
IOCHAN pp;
- close(hand[0]);
- child = 1;
for (pp = pListener; pp; pp = iochan_getnext(pp))
{
- if (pp != h)
- {
- COMSTACK l = (COMSTACK)iochan_getdata(pp);
- cs_close(l);
- iochan_destroy(pp);
- }
+ COMSTACK l = (COMSTACK)iochan_getdata(pp);
+ cs_close(l);
+ iochan_destroy(pp);
}
- sprintf(nbuf, "%s(%d)", me, getpid());
+ sprintf(nbuf, "%s(%d)", me, no_sessions);
yaz_log_init(control_block.loglevel, nbuf, 0);
/* ensure that bend_stop is not called when each child exits -
- only for the main process ..
- */
+ only for the main process .. */
control_block.bend_stop = 0;
}
else /* parent */
{
- close(hand[1]);
- /* wait for child to take the call */
- for (;;)
- {
- char dummy[1];
- int res;
-
- if ((res = read(hand[0], dummy, 1)) < 0 &&
- yaz_errno() != EINTR)
- {
- yaz_log(LOG_FATAL|LOG_ERRNO, "handshake read");
- return;
- }
- else if (res >= 0)
- break;
- }
- yaz_log(LOG_DEBUG, "P: Child has taken the call");
- close(hand[0]);
+ cs_close(new_line);
return;
}
}
- if ((res = cs_listen_check(line, 0, 0, control_block.check_ip,
- control_block.daemon_name)) < 0)
- {
- yaz_log(LOG_WARN|LOG_ERRNO, "cs_listen failed");
- return;
- }
- else if (res == 1)
- return;
- yaz_log(LOG_DEBUG, "listen ok");
- iochan_setevent(h, EVENT_OUTPUT);
- iochan_setflags(h, EVENT_OUTPUT | EVENT_EXCEPT); /* set up for acpt */
- }
- /* in dynamic mode, only the child ever comes down here */
- else if (event == EVENT_OUTPUT)
- {
- COMSTACK new_line = cs_accept(line);
-
- if (!new_line)
- {
- yaz_log(LOG_FATAL, "Accept failed.");
- iochan_setflags(h, EVENT_INPUT | EVENT_EXCEPT); /* reset listener */
- return;
- }
- yaz_log(LOG_DEBUG, "accept ok");
- if (control_block.dynamic)
- {
- IOCHAN pp;
- /* close our half of the listener socket */
- for (pp = pListener; pp; pp = iochan_getnext(pp))
- {
- COMSTACK l = (COMSTACK)iochan_getdata(pp);
- cs_close(l);
- iochan_destroy(pp);
- }
- /* release dad */
- yaz_log(LOG_DEBUG, "Releasing parent");
- close(hand[1]);
- }
- else
- {
- iochan_setflags(h, EVENT_INPUT | EVENT_EXCEPT); /* reset listener */
- ++no_sessions;
- }
-#if YAZ_POSIX_THREADS
if (control_block.threads)
{
+#if YAZ_POSIX_THREADS
pthread_t child_thread;
pthread_create (&child_thread, 0, new_session, new_line);
pthread_detach (child_thread);
- }
- else
- new_session(new_line);
#elif YAZ_GNU_THREADS
- if (control_block.threads)
- {
pth_attr_t attr;
pth_t child_thread;
pth_attr_set (attr, PTH_ATTR_JOINABLE, FALSE);
pth_attr_set (attr, PTH_ATTR_STACK_SIZE, 32*1024);
pth_attr_set (attr, PTH_ATTR_NAME, "session");
- yaz_log (LOG_LOG, "pth_spawn begin");
+ yaz_log (YLOG_DEBUG, "pth_spawn begin");
child_thread = pth_spawn (attr, new_session, new_line);
- yaz_log (LOG_LOG, "pth_spawn finish");
+ yaz_log (YLOG_DEBUG, "pth_spawn finish");
pth_attr_destroy (attr);
+#else
+ new_session(new_line);
+#endif
}
else
new_session(new_line);
-#else
- new_session(new_line);
-#endif
}
else if (event == EVENT_TIMEOUT)
{
- yaz_log(LOG_LOG, "Shutting down listener.");
+ yaz_log(log_server, "Shutting down listener.");
iochan_destroy(h);
}
else
{
- yaz_log(LOG_FATAL, "Bad event on listener.");
+ yaz_log(YLOG_FATAL, "Bad event on listener.");
iochan_destroy(h);
}
}
if (!(new_chan = iochan_create(cs_fileno(new_line), ir_session, mask)))
{
- yaz_log(LOG_FATAL, "Failed to create iochan");
+ yaz_log(YLOG_FATAL, "Failed to create iochan");
return 0;
}
if (!(newas = create_association(new_chan, new_line)))
{
- yaz_log(LOG_FATAL, "Failed to create new assoc.");
+ yaz_log(YLOG_FATAL, "Failed to create new assoc.");
return 0;
}
newas->cs_accept_mask = cs_accept_mask;
#else
a = 0;
#endif
- yaz_log(LOG_LOG, "Starting session %d from %s",
- no_sessions, a ? a : "[Unknown]");
- if (max_sessions && no_sessions == max_sessions)
+ yaz_log(log_session, "Starting session %d from %s (pid=%ld)",
+ no_sessions, a ? a : "[Unknown]", (long) getpid());
+ if (max_sessions && no_sessions >= max_sessions)
control_block.one_shot = 1;
if (control_block.threads)
{
iochan_setdata(chan, assoc);
iochan_settimeout(chan, 60);
addr = cs_addrstr(line);
- yaz_log(LOG_LOG, "Inetd association from %s",
+ yaz_log(log_session, "Inetd association from %s",
addr ? addr : "[UNKNOWN]");
assoc->cs_get_mask = EVENT_INPUT;
}
else
{
- yaz_log(LOG_FATAL, "Failed to create association structure");
+ yaz_log(YLOG_FATAL, "Failed to create association structure");
}
chan->next = pListener;
pListener = chan;
}
else
{
- yaz_log(LOG_FATAL, "Failed to create iochan");
+ yaz_log(YLOG_FATAL, "Failed to create iochan");
}
}
else
{
- yaz_log(LOG_ERRNO|LOG_FATAL, "Failed to create comstack on socket 0");
+ yaz_log(YLOG_ERRNO|YLOG_FATAL, "Failed to create comstack on socket 0");
}
}
else
mode = "static";
- yaz_log(LOG_LOG, "Adding %s %s listener on %s", mode,
+ yaz_log(log_server, "Adding %s %s listener on %s", mode,
what == PROTO_SR ? "SR" : "Z3950", where);
l = cs_create_host(where, 2, &ap);
if (!l)
{
- yaz_log(LOG_FATAL|LOG_ERRNO, "Failed to listen on %s", where);
+ yaz_log(YLOG_FATAL, "Failed to listen on %s", where);
return -1;
}
+ if (*control_block.cert_fname)
+ cs_set_ssl_certificate_file(l, control_block.cert_fname);
+
if (cs_bind(l, ap, CS_SERVER) < 0)
{
- yaz_log(LOG_FATAL|LOG_ERRNO, "Failed to bind to %s", where);
+ yaz_log(YLOG_FATAL|YLOG_ERRNO, "Failed to bind to %s", where);
cs_close (l);
return -1;
}
if (!(lst = iochan_create(cs_fileno(l), listener, EVENT_INPUT |
EVENT_EXCEPT)))
{
- yaz_log(LOG_FATAL|LOG_ERRNO, "Failed to create IOCHAN-type");
+ yaz_log(YLOG_FATAL|YLOG_ERRNO, "Failed to create IOCHAN-type");
cs_close (l);
return -1;
}
int statserv_start(int argc, char **argv)
{
- int ret;
-
+ int ret = 0;
+ char sep;
#ifdef WIN32
/* We need to initialize the thread list */
ThreadList_Initialize();
#endif
#ifdef WIN32
- if ((me = strrchr (argv[0], '\\')))
- me++;
- else
- me = argv[0];
+ sep = '\\';
#else
- me = argv[0];
+ sep = '/';
#endif
+ if ((me = strrchr (argv[0], sep)))
+ me++; /* get the basename */
+ else
+ me = argv[0];
+ programname = argv[0];
+
if (control_block.options_func(argc, argv))
return(1);
if (control_block.bend_start)
(*control_block.bend_start)(&control_block);
#ifdef WIN32
- yaz_log (LOG_LOG, "Starting server %s", me);
+ yaz_log (log_server, "Starting server %s", me);
+ if (!pListener && *control_block.default_listen)
+ add_listener(control_block.default_listen,
+ control_block.default_proto);
+
+ if (!pListener)
+ return 1;
#else
/* UNIX */
if (control_block.inetd)
inetd_connection(control_block.default_proto);
else
{
- yaz_log (LOG_LOG, "Starting server %s pid=%d", me, getpid());
+ static int hand[2];
+ if (control_block.background)
+ {
+ /* create pipe so that parent waits until child has created
+ PID (or failed) */
+ if (pipe(hand) < 0)
+ {
+ yaz_log(YLOG_FATAL|YLOG_ERRNO, "pipe");
+ return 1;
+ }
+ switch (fork())
+ {
+ case 0:
+ break;
+ case -1:
+ return 1;
+ default:
+ close(hand[1]);
+ while(1)
+ {
+ char dummy[1];
+ int res = read(hand[0], dummy, 1);
+ if (res < 0 && yaz_errno() != EINTR)
+ {
+ yaz_log(YLOG_FATAL|YLOG_ERRNO, "read fork handshake");
+ break;
+ }
+ else if (res >= 0)
+ break;
+ }
+ close(hand[0]);
+ _exit(0);
+ }
+ /* child */
+ close(hand[0]);
+ if (setsid() < 0)
+ return 1;
+
+ close(0);
+ close(1);
+ close(2);
+ open("/dev/null", O_RDWR);
+ dup(0); dup(0);
+ }
+ if (!pListener && *control_block.default_listen)
+ add_listener(control_block.default_listen,
+ control_block.default_proto);
+
+ if (!pListener)
+ return 1;
+
+ if (*control_block.pid_fname)
+ {
+ FILE *f = fopen(control_block.pid_fname, "w");
+ if (!f)
+ {
+ yaz_log(YLOG_FATAL|YLOG_ERRNO, "Couldn't create %s",
+ control_block.pid_fname);
+ exit(0);
+ }
+ fprintf(f, "%ld", (long) getpid());
+ fclose(f);
+ }
+
+ if (control_block.background)
+ close(hand[1]);
+
+ yaz_log (log_server, "Starting server %s pid=%ld", programname,
+ (long) getpid());
+
#if 0
sigset_t sigs_to_block;
if (!(pw = getpwnam(control_block.setuid)))
{
- yaz_log(LOG_FATAL, "%s: Unknown user", control_block.setuid);
+ yaz_log(YLOG_FATAL, "%s: Unknown user", control_block.setuid);
return(1);
}
if (setuid(pw->pw_uid) < 0)
{
- yaz_log(LOG_FATAL|LOG_ERRNO, "setuid");
+ yaz_log(YLOG_FATAL|YLOG_ERRNO, "setuid");
exit(1);
}
}
/* UNIX */
#endif
-
-
if ((pListener == NULL) && *control_block.default_listen)
add_listener(control_block.default_listen,
control_block.default_proto);
ret = 1;
else
{
- yaz_log(LOG_LOG, "Entering event loop.");
+ yaz_log(YLOG_DEBUG, "Entering event loop.");
ret = event_loop(&pListener);
}
return ret;
int ret = 0, r;
char *arg;
- while ((ret = options("1a:iszSTl:v:u:c:w:t:k:d:D:", argv, argc, &arg)) != -2)
+ /* set default log level */
+ control_block.loglevel = yaz_log_mask_str(STAT_DEFAULT_LOG_LEVEL);
+ yaz_log_init_level(control_block.loglevel);
+
+ while ((ret = options("1a:iszSTl:v:u:c:w:t:k:d:A:p:DC:",
+ argv, argc, &arg)) != -2)
{
switch (ret)
{
yaz_log_init(control_block.loglevel, me, control_block.logfile);
break;
case 'v':
- control_block.loglevel = yaz_log_mask_str(arg);
+ control_block.loglevel = yaz_log_mask_str_x(arg,control_block.loglevel);
yaz_log_init(control_block.loglevel, me, control_block.logfile);
break;
case 'a':
case 'c':
strcpy(control_block.configname, arg ? arg : "");
break;
+ case 'C':
+ strcpy(control_block.cert_fname, arg ? arg : "");
+ break;
case 'd':
strcpy(control_block.daemon_name, arg ? arg : "");
break;
return 1;
}
break;
- case 'D':
+ case 'A':
max_sessions = atoi(arg);
break;
+ case 'p':
+ if (strlen(arg) >= sizeof(control_block.pid_fname))
+ {
+ yaz_log(YLOG_FATAL, "pid fname too long");
+ exit(1);
+ }
+ strcpy(control_block.pid_fname, arg);
+ break;
+ case 'D':
+ control_block.background = 1;
+ break;
default:
fprintf(stderr, "Usage: %s [ -a <pdufile> -v <loglevel>"
" -l <logfile> -u <user> -c <config> -t <minutes>"
- " -k <kilobytes> -d <daemon>"
- " -ziST1 -w <directory> <listener-addr>... ]\n", me);
+ " -k <kilobytes> -d <daemon> -p <pidfile> -C certfile"
+ " -ziDST1 -w <directory> <listener-addr>... ]\n", me);
return 1;
}
}
+ get_logbits(1);
return 0;
}