Skip to content
Snippets Groups Projects
Forked from lyskom-server / lyskom-server
3022 commits behind the upstream repository.
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;
}