Forked from
lyskom-server / lyskom-server
3022 commits behind the upstream repository.
-
Per Cederqvist authoredPer Cederqvist authored
ramkomd.c 14.97 KiB
/*
* $Id: ramkomd.c,v 0.91 1999/06/24 12:59:03 ceder Exp $
* Copyright (C) 1991-1999 Lysator Academic Computer Association.
*
* This file is part of the LysKOM server.
*
* LysKOM is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 1, or (at your option)
* any later version.
*
* LysKOM is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License
* along with LysKOM; see the file COPYING. If not, write to
* Lysator, c/o ISY, Linkoping University, S-581 83 Linkoping, SWEDEN,
* or the Free Software Foundation, Inc., 675 Mass Ave, Cambridge,
* MA 02139, USA.
*
* Please mail bug reports to bug-lyskom@lysator.liu.se.
*/
/*
* The next comment block is a historic comment, written in Swedish
* using ISO 646 since nobody in Lysator knew about ISO 8859-1 by
* then. It translates, rougly, to "This is the main program of the
* server. It will hopefully be bigger than it is at the moment.
* Created by Willfr 31-mar-1990."
*/
/*
* Detta {r serverns huvudprogram. Det kommer f|rhoppningsvis bli st|rre
* {n det {r just nu...
*
* Created by Willf|r 31/3-90
*
* It has grown! /ceder
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
static const char *
rcsid = "$Id: ramkomd.c,v 0.91 1999/06/24 12:59:03 ceder Exp $";
#include "rcs.h"
USE(rcsid);
#ifdef HAVE_LOCALE_H
# include <locale.h>
#endif
#include <stdio.h>
#include <signal.h>
#ifdef HAVE_STDLIB_H
# include <stdlib.h>
#endif
#ifdef HAVE_STRING_H
# include <string.h>
#endif
#include <sys/wait.h>
#include <time.h>
#ifdef HAVE_SYS_RESOURCE_H
# include <sys/time.h>
# include <sys/resource.h>
#endif
#include <unistd.h>
#include <setjmp.h>
#if defined(HAVE_SYS_PARAM_H) && !defined(HAVE_GETCWD)
# include <sys/param.h>
#endif
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <fcntl.h>
#include "exp.h"
#include "s-string.h"
#include "misc-types.h"
#include "kom-types.h"
#include "com.h"
#include "async.h"
#include "connections.h"
#include "internal-connections.h"
#include "kom-errno.h"
#include "isc-malloc.h"
#include "isc-interface.h"
#include "kom-config.h"
#include "cache.h"
#include "string-malloc.h"
#include "lyskomd.h"
#include "log.h"
#include "internal-services.h"
#include "server/smalloc.h"
#include "kom-memory.h"
#include "conf-file.h"
#include "param.h"
#include "server-config.h"
#include "manipulate.h"
#include "version-info.h"
#include "aux-items.h"
#include "admin.h"
#include "unused.h"
#include "sigflags.h"
#include "local-to-global.h"
#include "server-time.h"
#include "lockdb.h"
#ifdef TRACED_ALLOCATIONS
# include "trace-alloc.h"
#endif
#if defined(HAVE_SETRLIMIT) && defined(RLIMIT_OFILE) && !defined(RLIMIT_NOFILE)
# define RLIMIT_NOFILE RLIMIT_OFILE
#endif
Kom_err kom_errno = KOM_NO_ERROR;
unsigned long err_stat = 0;
time_t current_time = 0;
#ifndef NDEBUG
int buglevel = 0;
#endif
static IscSession *listen_client = NULL; /* ISC listen identifier */
static void dump_exit_statistics(void);
static void free_kom_info(void);
static void
server_init(const char *host, const char * client_port)
{
IscConfig config;
IscAddress *isc_adr = NULL;
#ifdef HAVE_STRUCT_SIGACTION
struct sigaction act;
#endif
/*
** Setup some parameters here
*/
config.version = 1006;
config.master.version = 1001;
config.master.memfn.alloc = &isc_malloc_wrapper;
config.master.memfn.realloc = &isc_realloc_wrapper;
config.master.memfn.free = &isc_free_wrapper;
config.master.abortfn = NULL; /* Use default abort function. */
config.session.version = 1002;
config.session.max.msgsize = -1; /* Use default sizes. */
config.session.max.queuedsize = -1;
config.session.max.dequeuelen = -1;
config.session.max.openretries = -1;
config.session.max.backlog = -1;
config.session.fd_relocate = PROTECTED_FDS;
kom_server_mcb = isc_initialize(&config);
if ( kom_server_mcb == NULL )
restart_kom("server_init: can't isc_initialize()\n");
listen_client = isc_listentcp(kom_server_mcb, host, client_port);
if (listen_client == NULL)
restart_kom("server_init: can't isc_listentcp(CLIENT)\n");
isc_adr = isc_getladdress (listen_client);
if (isc_adr == NULL)
restart_kom("server_init(): can't isc_getladdress (listen_client)\n");
kom_log("Listening for clients on %d.\n", isc_getportnum(isc_adr));
isc_freeaddress (isc_adr);
/*
* Ignore SIGPIPE, which the server gets if it tries to write to a
* socket and the client has died. The server will anyhow handle
* this situation correct.
*/
#ifdef HAVE_STRUCT_SIGACTION
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
act.sa_handler = SIG_IGN;
sigaction(SIGPIPE, &act, NULL);
#else
signal(SIGPIPE, SIG_IGN);
#endif
}
static void
init_data_base(void)
{
kom_log("Database = %s\n", param.datafile_name);
kom_log("Backup = %s\n", param.backupfile_name);
kom_log("2nd Backup = %s\n", param.backupfile_name_2);
kom_log("Lock File = %s\n", param.lockfile_name);
if ( init_cache() == FAILURE )
restart_kom ("Cannot find database.\n");
}
static void
sighandler_hup (int UNUSED(sig))
{
#ifndef HAVE_STRUCT_SIGACTION
signal(SIGHUP, sighandler_hup);
#endif
kom_log ("Signal HUP received. Shutting down server.\n");
go_and_die = 1;
}
static void
sighandler_quit (int UNUSED(sig))
{
kom_log ("Signal QUIT received - syncing...\n");
cache_sync_all();
kom_log ("Dumping core now.\n");
abort();
}
static void
sighandler_usr1 (int UNUSED(sig))
{
#ifndef HAVE_STRUCT_SIGACTION
signal(SIGUSR1, sighandler_usr1);
#endif
do_statistics = 1;
}
static void
sighandler_usr2 (int UNUSED(sig))
{
int child;
#ifndef HAVE_STRUCT_SIGACTION
signal(SIGUSR2, sighandler_usr2);
#endif
kom_log ("Signal USR2 received - will dump core now. (Check that child dies.)\n");
if ((child = fork()) == 0)
{
abort();
kom_log ("Abort() failed!!!\n");
exit(1);
}
else if (child < 0)
{
kom_log ("Couldn't fork.\n");
}
else
{
wait (NULL);
}
}
static void
save_pid(void)
{
FILE *fp;
if ((fp = fopen(param.pid_name, "w")) == NULL)
return;
fprintf(fp, "%ld\n", (long)getpid());
fclose(fp);
}
static void
go_daemon(void)
{
pid_t child;
int fd;
#ifdef HAVE_STRUCT_SIGACTION
struct sigaction act;
#endif
if (buglevel != 0)
{
return;
}
if (getppid() != 1)
{
/* We were not invoked from /etc/inittab, so
disassociate from controlling terminal. */
#ifdef HAVE_STRUCT_SIGACTION
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
act.sa_handler = SIG_IGN;
# ifdef SIGTTOU
sigaction(SIGTTOU, &act, NULL);
# endif
# ifdef SIGTTIN
sigaction(SIGTTIN, &act, NULL);
# endif
# ifdef SIGTSTP
sigaction(SIGTSTP, &act, NULL);
# endif
#else /* !HAVE_STRUCT_SIGACTION */
# ifdef SIGTTOU
signal(SIGTTOU, SIG_IGN);
# endif
# ifdef SIGTTIN
signal(SIGTTIN, SIG_IGN);
# endif
# ifdef SIGTSTP
signal(SIGTSTP, SIG_IGN);
# endif
#endif
child = fork();
if (child < 0)
restart_kom("fork failed: %d\n", errno);
else if (child > 0)
exit (0); /* parent */
setsid();
}
/* Close all file descriptors */
for (fd = 0; fd < MAX_NO_OF_CONNECTIONS + PROTECTED_FDS; fd++)
close(fd);
if (open("/dev/null", O_RDONLY) != 0
||open("/dev/null", O_WRONLY) != 1
||open(param.logfile_name, O_WRONLY|O_CREAT|O_APPEND, 0644) != 2)
{
/* Kinda stupid to try to log an error message now, but
who knows? */
restart_kom("open of log file failed: %d\n", errno);
}
kom_log("*** Version %s (process %lu) coming up.\n",
kom_version_info.server_version, (unsigned long)getpid());
}
static void
initialize(const char *config_file)
{
#if defined(HAVE_SETRLIMIT) && defined(RLIMIT_NOFILE)
struct rlimit rlim;
#endif
read_configuration(config_file);
initialize_aux_items(param.aux_def_file);
#ifdef HAVE_LOCALE_H
if (param.use_locale != NULL)
if (setlocale(LC_CTYPE, param.use_locale) == NULL)
{
fprintf(stderr, "setlocale: ");
perror(param.use_locale);
exit(1);
}
#else
if (param.use_locale != NULL)
{
fprintf(stderr, "locale not supported in your environment.\n");
exit(1);
}
#endif
#if defined(HAVE_SETRLIMIT) && defined(RLIMIT_NOFILE)
if (getrlimit(RLIMIT_NOFILE, &rlim) < 0)
{
perror("getrlimit(RLIMIT_NOFILE) failed");
exit(1);
}
if (param.no_files != -1)
{
if (param.no_files > rlim.rlim_max)
{
fprintf(stderr, "attempt to raise open files from %ld to %ld, "
"but only %ld is allowed\n",
(long)rlim.rlim_cur, (long)param.no_files,
(long)rlim.rlim_max);
rlim.rlim_cur = rlim.rlim_max;
}
else
rlim.rlim_cur = param.no_files;
if (setrlimit(RLIMIT_NOFILE, &rlim) < 0)
{
perror("setrlimit failed");
exit(1);
}
}
#else
if (param.no_files != -1)
{
fprintf(stderr,
"can't alter number of open files in your environment.\n");
exit(1);
}
#endif
/* Find out how many connections we can handle. */
#if defined (HAVE_SYSCONF)
MAX_NO_OF_CONNECTIONS = sysconf(_SC_OPEN_MAX) - PROTECTED_FDS;
#else
# ifdef HAVE_GETDTABLESIZE
MAX_NO_OF_CONNECTIONS = getdtablesize() - PROTECTED_FDS;
# else
/* If we don't have getdtablesize or sysconf, MAX_NO_OF_CONNECTIONS is
set at compile time. */
# endif
#endif
#if defined(HAVE_SETRLIMIT) && defined(RLIMIT_NOFILE)
/* Check that getrlimit and sysconf/getdtablesize/the compiled default
makes sense. */
if (rlim.rlim_cur > MAX_NO_OF_CONNECTIONS + PROTECTED_FDS)
kom_log("WARNING: getrlimit indicates that %ld files can be open, "
"but MAX_NO_OF_CONNECTIONS is only %ld\n",
(long)rlim.rlim_cur, (long)MAX_NO_OF_CONNECTIONS);
else if (rlim.rlim_cur < MAX_NO_OF_CONNECTIONS + PROTECTED_FDS)
kom_log("warning: getrlimit indicates that only %ld files can be open, "
"but MAX_NO_OF_CONNECTIONS is %ld (this should be harmless)\n",
(long)rlim.rlim_cur, (long)MAX_NO_OF_CONNECTIONS);
#endif
go_daemon();
if (lock_db() < 0)
{
/* Don't actually die until something is entered on stdin in debug
mode. This is mainly here for the benefit of the test suite,
but is could also be useful to be able to attach a debugger and
do pre-mortem debugging of the process at this point. */
kom_log("Cannot obtain database lock. Exiting.\n");
if (buglevel > 0)
{
kom_log("Press enter to terminate lyskomd\n");
getchar();
}
exit(1);
}
server_init(param.ip_client_host, param.ip_client_port);
init_data_base();
}
/* Stop complaint from gcc 2.0 about "no previous prototype for `main'". */
int main(int argc, char **argv);
int
main (int argc,
char **argv)
{
int i;
char *default_config_file;
char *config_file;
#ifdef HAVE_STRUCT_SIGACTION
struct sigaction act;
#endif
current_time = time(NULL);
#ifdef TRACED_ALLOCATIONS
/* We must do this before we allocate any memory... */
{
char buf[1024];
char *nl;
fputs("Where does the trace want to go today? [stderr]\n", stdout);
fflush(stdout);
if (fgets(buf, sizeof(buf), stdin) != buf)
restart_kom("main(): unable to read trace location\n");
if ((nl = strchr(buf, '\n')) != NULL)
*nl = '\0';
trace_alloc_file(buf);
}
#endif
kom_log("*** Version %s (process %lu) started.\n",
kom_version_info.server_version, (unsigned long)getpid());
#ifdef DEBUG_CALLS
kom_log("WARNING: This server was compiled with --with-debug-calls.\n");
kom_log("It isn't safe to use in a production environment.\n");
#endif
#ifdef ENCRYPT_PASSWORDS
/* Seed the random number generator. */
srand(time(NULL) + getpid());
#endif
/* Initialize the string handling package. */
s_set_storage_management(string_malloc, string_realloc, string_free);
/* Parse command line arguments. */
for (i = 1; i < argc && argv[i][0] == '-'; i++)
switch (argv[i][1])
{
case 'd':
buglevel++;
break;
default:
restart_kom("usage: %s [-d ...] [config-file]\n", argv[0]);
}
/* Read in the configuration file. */
default_config_file = smalloc(strlen(DEFAULT_DBASE_DIR) +
strlen(CONFIG_FILE) + 2);
sprintf(default_config_file, "%s/%s", DEFAULT_DBASE_DIR, CONFIG_FILE);
if (i < argc)
config_file = argv[i++];
else
config_file = default_config_file;
if (i < argc)
restart_kom("usage: %s [-d ...] [config-file]\n", argv[0]);
#ifdef HAVE_STRUCT_SIGACTION
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
act.sa_handler = sighandler_hup;
sigaction(SIGHUP, &act, NULL);
act.sa_handler = sighandler_quit;
sigaction(SIGQUIT, &act, NULL);
act.sa_handler = sighandler_usr1;
sigaction(SIGUSR1, &act, NULL);
act.sa_handler = sighandler_usr2;
sigaction(SIGUSR2, &act, NULL);
#else
signal(SIGHUP, sighandler_hup);
signal(SIGQUIT, sighandler_quit);
signal(SIGUSR1, sighandler_usr1);
signal(SIGUSR2, sighandler_usr2);
#endif
initialize(config_file); /* Read config, listen, and start db */
chdir(param.core_dir);
sfree(default_config_file);
save_pid();
toploop();
logout_all_clients();
isc_shutdown(kom_server_mcb);
cache_sync_all();
unlock_db();
/* Finish */
dump_exit_statistics();
kom_log("%s terminated normally.\n", argv[0]);
/* Don't actually die until something is entered on stdin in debug
mode. This is mainly here for the benefit of the test suite,
but is could also be useful to be able to attach a debugger and
do pre-mortem debugging of the process at this point. */
if (buglevel > 0)
{
kom_log("Press enter to terminate lyskomd\n");
getchar();
}
exit(0);
}
static void
dump_exit_statistics(void)
{
FILE *stat_file;
time_t now;
time(&now);
stat_file = fopen(param.memuse_name, "a");
if ( stat_file == NULL )
restart_kom("Can't open file to save memory usage to.\n");
fprintf(stat_file, "\nLysKOM Server going down at %s\n", ctime(&now));
dump_cache_stats (stat_file);
free_all_tmp();
free_all_cache();
free_all_jubel();
free_kom_info();
free_aux_item_definitions();
free_configuration();
dump_smalloc_counts(stat_file);
dump_alloc_counts(stat_file);
dump_cache_mem_usage(stat_file);
dump_string_alloc_counts(stat_file);
dump_allocated_connections(stat_file);
dump_isc_alloc_counts(stat_file);
dump_l2g_stats(stat_file);
fclose (stat_file);
}
static void
free_kom_info(void)
{
unsigned long i;
if (kom_info.aux_item_list.items != NULL)
{
for (i = 0; i < kom_info.aux_item_list.length; i++)
{
s_clear(&kom_info.aux_item_list.items[i].data);
}
sfree(kom_info.aux_item_list.items);
}
kom_info.aux_item_list.length = 0;
kom_info.aux_item_list.items = 0;
}