X-Git-Url: https://code.citadel.org/?p=citadel.git;a=blobdiff_plain;f=citadel%2Fserver_main.c;h=68af7888e9ef221011eb88f6d523d2736d6a6ac6;hp=c46c0857a2375fa55bd52064429cfe09f8ea4573;hb=6d051cee6f669b6ecd950e933a95644f3cf097ea;hpb=dff1f2c563c6c998431eb687039096a4060dff32 diff --git a/citadel/server_main.c b/citadel/server_main.c index c46c0857a..68af7888e 100644 --- a/citadel/server_main.c +++ b/citadel/server_main.c @@ -1,78 +1,67 @@ /* * citserver's main() function lives here. + * + * Copyright (c) 1987-2020 by the citadel.org team * - * $Id$ + * This program is open source software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3. + * + * This program 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. */ - -#include "sysdep.h" #include #include #include -#include -#include -#include #include #include -#include -#include -#include - -#if TIME_WITH_SYS_TIME -# include -# include -#else -# if HAVE_SYS_TIME_H -# include -# else -# include -# endif -#endif - -#include -#include -#include -#include -#include -#include -#include -#include #include -#include -#ifdef HAVE_PTHREAD_H -#include -#endif -#ifdef HAVE_SYS_PRCTL_H -#include -#endif +#include #include -#include "citadel.h" -#include "server.h" -#include "serv_extensions.h" -#include "sysdep_decls.h" + #include "citserver.h" -#include "support.h" +#include "svn_revision.h" +#include "modules_init.h" #include "config.h" #include "control.h" -#include "database.h" +#include "serv_extensions.h" +#include "citadel_dirs.h" #include "user_ops.h" -#include "housekeeping.h" -#include "citadel_dirs.c" - -#include "modules_init.h" -#include "ecrash.h" -#ifdef HAVE_SYS_SELECT_H -#include -#endif - -#ifndef HAVE_SNPRINTF -#include "snprintf.h" -#endif +uid_t ctdluid = 0; const char *CitadelServiceUDS="citadel-UDS"; const char *CitadelServiceTCP="citadel-TCP"; +int sanity_diag_mode = 0; -void go_threading(void); +/* + * Create or remove a lock file, so we only have one Citadel Server running at a time. + */ +void ctdl_lockfile(int yo) { + static char lockfilename[PATH_MAX]; + static FILE *fp; + + if (yo) { + syslog(LOG_DEBUG, "main: creating lockfile"); + snprintf(lockfilename, sizeof lockfilename, "%s/citadel.lock", ctdl_run_dir); + fp = fopen(lockfilename, "w"); + if (!fp) { + syslog(LOG_ERR, "%s: %m", lockfilename); + exit(CTDLEXIT_DB); + } + if (flock(fileno(fp), (LOCK_EX|LOCK_NB)) != 0) { + syslog(LOG_ERR, "main: cannot lock %s , is another citserver running?", lockfilename); + exit(CTDLEXIT_DB); + } + return; + } + + syslog(LOG_DEBUG, "main: removing lockfile"); + unlink(lockfilename); + flock(fileno(fp), LOCK_UN); + fclose(fp); +} /* @@ -80,94 +69,138 @@ void go_threading(void); */ int main(int argc, char **argv) { + size_t basesize = 64; char facility[32]; int a; /* General-purpose variables */ struct passwd pw, *pwp = NULL; char pwbuf[SIZ]; int drop_root_perms = 1; - size_t size; int relh=0; int home=0; int dbg=0; + int max_log_level = LOG_INFO; char relhome[PATH_MAX]=""; char ctdldir[PATH_MAX]=CTDLDIR; + int syslog_facility = LOG_DAEMON; + uid_t u = 0; + struct passwd *p = NULL; #ifdef HAVE_RUN_DIR struct stat filestats; #endif -#ifdef HAVE_BACKTRACE - eCrashParameters params; -// eCrashSymbolTable symbol_table; -#endif - /* initialise semaphores here. Patch by Matt and davew - * its called here as they are needed by CtdlLogPrintf for thread safety - */ - CtdlInitBase64Table(); - InitialiseSemaphores(); - + /* initialize the master context */ InitializeMasterCC(); + InitializeMasterTSD(); /* parse command-line arguments */ - for (a=1; a 0) { + ctdluid = u; + } + else { + p = getpwnam(optarg); + if (p) { + u = p->pw_uid; + } + } + if (u > 0) { + ctdluid = u; + } + break; + + /* -s tells the server to behave differently during sanity checks */ + case 's': + sanity_diag_mode = atoi(optarg); + break; + + default: /* any other parameter makes it crash and burn */ - else { - CtdlLogPrintf(CTDL_EMERG, "citserver: usage: " + fprintf(stderr, "citserver: usage: " "citserver " - "[-lLogFacility] " - "[-d] [-f] [-D] " - " [-tTraceFile]" - " [-xLogLevel] [-hHomeDir]\n"); + "[-l LogFacility] " + "[-x MaxLogLevel] " + "[-d] [-D] [-r] " + "[-u user] " + "[-h HomeDir]\n" + ); exit(1); + } + + /* Last ditch effort to determine the user name ... if there's a user called "citadel" then use that */ + if (ctdluid == 0) { + p = getpwnam("citadel"); + if (!p) { + p = getpwnam("bbs"); } + if (!p) { + p = getpwnam("guest"); + } + if (p) { + u = p->pw_uid; + } + if (u > 0) { + ctdluid = u; + } + } + if ((ctdluid == 0) && (drop_root_perms == 0)) { + fprintf(stderr, "citserver: cannot determine user to run as; please specify -r or -u options\n"); + exit(CTDLEXIT_UNUSER); } + StartLibCitadel(basesize); + setlogmask(LOG_UPTO(max_log_level)); + openlog("citserver", + ( running_as_daemon ? (LOG_PID) : (LOG_PID | LOG_PERROR) ), + syslog_facility + ); + calc_dirs_n_files(relh, home, relhome, ctdldir, dbg); /* daemonize, if we were asked to */ if (running_as_daemon) { @@ -175,130 +208,116 @@ int main(int argc, char **argv) drop_root_perms = 1; } -#ifdef HAVE_BACKTRACE - bzero(¶ms, sizeof(params)); - params.filename = file_pid_paniclog; - panic_fd=open(file_pid_paniclog, O_APPEND|O_CREAT|O_DIRECT); - params.filep = fopen(file_pid_paniclog, "a+"); - params.debugLevel = ECRASH_DEBUG_VERBOSE; - params.dumpAllThreads = TRUE; - params.useBacktraceSymbols = 1; -/// BuildSymbolTable(&symbol_table); -// params.symbolTable = &symbol_table; - params.signals[0]=SIGSEGV; - params.signals[1]=SIGILL; - params.signals[2]=SIGBUS; - params.signals[3]=SIGABRT; - - eCrash_Init(¶ms); - - eCrash_RegisterThread("MasterThread", 0); - -/// signal(SIGSEGV, cit_panic_backtrace); -#endif - /* Initialize the syslogger. Yes, we are really using 0 as the - * facility, because we are going to bitwise-OR the facility to - * the severity of each message, allowing us to write to other - * facilities when we need to... - */ - if (enable_syslog) { - openlog("citadel", LOG_NDELAY, 0); - setlogmask(LOG_UPTO(verbosity)); - } - /* Tell 'em who's in da house */ - CtdlLogPrintf(CTDL_NOTICE, "\n"); - CtdlLogPrintf(CTDL_NOTICE, "\n"); - CtdlLogPrintf(CTDL_NOTICE, - "*** Citadel server engine v%d.%02d ***\n", - (REV_LEVEL/100), (REV_LEVEL%100)); - CtdlLogPrintf(CTDL_NOTICE, - "Copyright (C) 1987-2007 by the Citadel development team.\n"); - CtdlLogPrintf(CTDL_NOTICE, - "This program is distributed under the terms of the GNU " - "General Public License.\n"); - CtdlLogPrintf(CTDL_NOTICE, "\n"); - CtdlLogPrintf(CTDL_DEBUG, "Called as: %s\n", argv[0]); - CtdlLogPrintf(CTDL_INFO, "%s\n", libcitadel_version_string()); - - /* Load site-specific parameters, and set the ipgm secret */ - CtdlLogPrintf(CTDL_INFO, "Loading citadel.config\n"); - get_config(); - config.c_ipgm_secret = rand(); - put_config(); + syslog(LOG_INFO, " "); + syslog(LOG_INFO, " "); + syslog(LOG_INFO, "*** Citadel server engine ***\n"); + syslog(LOG_INFO, "Version %d (build %s) ***", REV_LEVEL, svn_revision()); + syslog(LOG_INFO, "Copyright (C) 1987-2020 by the Citadel development team."); + syslog(LOG_INFO, " "); + syslog(LOG_INFO, "This program is open source software: you can redistribute it and/or"); + syslog(LOG_INFO, "modify it under the terms of the GNU General Public License, version 3."); + syslog(LOG_INFO, " "); + syslog(LOG_INFO, "This program is distributed in the hope that it will be useful,"); + syslog(LOG_INFO, "but WITHOUT ANY WARRANTY; without even the implied warranty of"); + syslog(LOG_INFO, "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the"); + syslog(LOG_INFO, "GNU General Public License for more details."); + syslog(LOG_INFO, " "); + syslog(LOG_INFO, "%s", libcitadel_version_string()); #ifdef HAVE_RUN_DIR /* on some dists rundir gets purged on startup. so we need to recreate it. */ - if (stat(ctdl_run_dir, &filestats)==-1){ + if (stat(ctdl_run_dir, &filestats) == -1) { +#ifdef HAVE_GETPWUID_R #ifdef SOLARIS_GETPWUID - pwp = getpwuid_r(config.c_ctdluid, &pw, pwbuf, sizeof(pwbuf)); -#else - getpwuid_r(config.c_ctdluid, &pw, pwbuf, sizeof(pwbuf), &pwp); -#endif - mkdir(ctdl_run_dir, 0755); - chown(ctdl_run_dir, config.c_ctdluid, (pwp==NULL)?-1:pw.pw_gid); - } - + pwp = getpwuid_r(ctdluid, &pw, pwbuf, sizeof(pwbuf)); +#else // SOLARIS_GETPWUID + getpwuid_r(ctdluid, &pw, pwbuf, sizeof(pwbuf), &pwp); +#endif // SOLARIS_GETPWUID +#else // HAVE_GETPWUID_R + pwp = NULL; +#endif // HAVE_GETPWUID_R + + if ((mkdir(ctdl_run_dir, 0755) != 0) && (errno != EEXIST)) { + syslog(LOG_ERR, "main: unable to create run directory [%s]: %m", ctdl_run_dir); + } + if (chown(ctdl_run_dir, ctdluid, (pwp==NULL)?-1:pw.pw_gid) != 0) { + syslog(LOG_ERR, "main: unable to set the access rights for [%s]: %m", ctdl_run_dir); + } + } #endif - /* Initialize... */ - init_sysdep(); + ctdl_lockfile(1); + init_sysdep(); // Initialize... + master_startup(); // Do non system dependent startup functions + check_control(); // Check/sanitize/initialize control record, fix user indexes + syslog(LOG_INFO, "main: upgrading modules"); // Run any upgrade entry points + upgrade_modules(); +/* + * Load the user for the masterCC or create them if they don't exist + */ + if (CtdlGetUser(&masterCC.user, "SYS_Citadel")) + { + /* User doesn't exist. We can't use create user here as the user number needs to be 0 */ + strcpy (masterCC.user.fullname, "SYS_Citadel") ; + CtdlPutUser(&masterCC.user); + CtdlGetUser(&masterCC.user, "SYS_Citadel"); /* Just to be safe */ + } + /* - * Do non system dependent startup functions. + * Bind the server to a Unix-domain socket (user client access) */ - master_startup(); - - CtdlLogPrintf(CTDL_INFO, "Acquiring control record\n"); - get_control(); + CtdlRegisterServiceHook(0, + file_citadel_socket, + citproto_begin_session, + do_command_loop, + do_async_loop, + CitadelServiceUDS); /* - * Bind the server to a Unix-domain socket. + * Bind the server to a Unix-domain socket (admin client access) */ CtdlRegisterServiceHook(0, - file_citadel_socket, - citproto_begin_session, + file_citadel_admin_socket, + citproto_begin_admin_session, do_command_loop, do_async_loop, CitadelServiceUDS); + chmod(file_citadel_admin_socket, S_IRWXU); /* for your eyes only */ /* * Bind the server to our favorite TCP port (usually 504). */ - CtdlRegisterServiceHook(config.c_port_number, + CtdlRegisterServiceHook(CtdlGetConfigInt("c_port_number"), NULL, citproto_begin_session, do_command_loop, do_async_loop, CitadelServiceTCP); - - /* - * Run any upgrade entry points - */ - CtdlLogPrintf(CTDL_INFO, "Upgrading modules.\n"); - upgrade_modules(); - - /* * Load any server-side extensions available here. */ - CtdlLogPrintf(CTDL_INFO, "Initializing server extensions\n"); - size = strlen(ctdl_home_directory) + 9; + syslog(LOG_INFO, "main: initializing server extensions"); initialise_modules(0); - - /* * If we need host auth, start our chkpwd daemon. */ - if (config.c_auth_mode == 1) { + if (CtdlGetConfigInt("c_auth_mode") == AUTHMODE_HOST) { start_chkpwd_daemon(); } + /* + * check, whether we're fired up another time after a crash. + * if, post an aide message, so the admin has a chance to react. + */ + checkcrash(); + /* * Now that we've bound the sockets, change to the Citadel user id and its * corresponding group ids @@ -306,24 +325,26 @@ int main(int argc, char **argv) if (drop_root_perms) { cdb_chmod_data(); /* make sure we own our data files */ +#ifdef HAVE_GETPWUID_R #ifdef SOLARIS_GETPWUID - pwp = getpwuid_r(config.c_ctdluid, &pw, pwbuf, sizeof(pwbuf)); -#else - getpwuid_r(config.c_ctdluid, &pw, pwbuf, sizeof(pwbuf), &pwp); -#endif + pwp = getpwuid_r(ctdluid, &pw, pwbuf, sizeof(pwbuf)); +#else // SOLARIS_GETPWUID + getpwuid_r(ctdluid, &pw, pwbuf, sizeof(pwbuf), &pwp); +#endif // SOLARIS_GETPWUID +#else // HAVE_GETPWUID_R + pwp = NULL; +#endif // HAVE_GETPWUID_R + if (pwp == NULL) - CtdlLogPrintf(CTDL_CRIT, "WARNING: getpwuid(%ld): %s\n" - "Group IDs will be incorrect.\n", (long)CTDLUID, - strerror(errno)); + syslog(LOG_ERR, "main: WARNING, getpwuid(%ld): %m Group IDs will be incorrect.", (long)CTDLUID); else { initgroups(pw.pw_name, pw.pw_gid); if (setgid(pw.pw_gid)) - CtdlLogPrintf(CTDL_CRIT, "setgid(%ld): %s\n", (long)pw.pw_gid, - strerror(errno)); + syslog(LOG_ERR, "main: setgid(%ld): %m", (long)pw.pw_gid); } - CtdlLogPrintf(CTDL_INFO, "Changing uid to %ld\n", (long)CTDLUID); + syslog(LOG_INFO, "main: changing uid to %ld", (long)CTDLUID); if (setuid(CTDLUID) != 0) { - CtdlLogPrintf(CTDL_CRIT, "setuid() failed: %s\n", strerror(errno)); + syslog(LOG_ERR, "main: setuid() failed: %m"); } #if defined (HAVE_SYS_PRCTL_H) && defined (PR_SET_DUMPABLE) prctl(PR_SET_DUMPABLE, 1); @@ -331,132 +352,14 @@ int main(int argc, char **argv) } /* We want to check for idle sessions once per minute */ - CtdlRegisterSessionHook(terminate_idle_sessions, EVT_TIMER); + CtdlRegisterSessionHook(terminate_idle_sessions, EVT_TIMER, PRIO_CLEANUP + 1); go_threading(); - - master_cleanup(exit_signal); - return(0); -} - - - -void go_threading(void) -{ - int i; - struct CtdlThreadNode *last_worker; - - /* - * Initialise the thread system - */ - ctdl_thread_internal_init(); - /* - * Now create a bunch of worker threads. - */ - CtdlLogPrintf(CTDL_DEBUG, "Starting %d worker threads\n", config.c_min_workers); - begin_critical_section(S_THREAD_LIST); - i=0; /* Always start at least 1 worker thread */ - do - { - ctdl_internal_create_thread("Worker Thread", CTDLTHREAD_BIGSTACK + CTDLTHREAD_WORKER, worker_thread, NULL); - } while (++i < config.c_min_workers); - end_critical_section(S_THREAD_LIST); - - /* Second call to module init functions now that threading is up */ - initialise_modules(1); - - /* - * This thread is now used for garbage collection of other threads in the thread list - */ - CtdlLogPrintf(CTDL_INFO, "Startup thread %d becoming garbage collector,\n", pthread_self()); - - /* - * We do a lot of locking and unlocking of the thread list in here. - * We do this so that we can repeatedly release time for other threads - * that may be waiting on the thread list. - * We are a low priority thread so we can afford to do this - */ - - while (CtdlThreadGetCount()) - { - if (exit_signal) - CtdlThreadStopAll(); - check_sched_shutdown(); - if (CT->state > CTDL_THREAD_STOP_REQ) - { - begin_critical_section(S_THREAD_LIST); - ctdl_thread_internal_calc_loadavg(); - end_critical_section(S_THREAD_LIST); - } - - /* Reduce the size of the worker thread pool if necessary. */ - if ((CtdlThreadGetWorkers() > config.c_min_workers) && (CtdlThreadWorkerAvg < 20) && (CT->state > CTDL_THREAD_STOP_REQ)) - { - /* Ask a worker thread to stop as we no longer need it */ - begin_critical_section(S_THREAD_LIST); - last_worker = CtdlThreadList; - while (last_worker) - { - pthread_mutex_lock(&last_worker->ThreadMutex); - if (last_worker->flags & CTDLTHREAD_WORKER && last_worker->state > CTDL_THREAD_STOPPING) - { - pthread_mutex_unlock(&last_worker->ThreadMutex); - break; - } - pthread_mutex_unlock(&last_worker->ThreadMutex); - last_worker = last_worker->next; - } - end_critical_section(S_THREAD_LIST); - if (last_worker) - { -#ifdef WITH_THREADLOG - CtdlLogPrintf(CTDL_DEBUG, "Thread system, stopping excess worker thread \"%s\" (%ld).\n", - last_worker->name, - last_worker->tid - ); -#endif - CtdlThreadStop(last_worker); - } - } - - /* - * If all our workers are working hard, start some more to help out - * with things - */ - /* FIXME: come up with a better way to dynamically alter the number of threads - * based on the system load - */ -// if ((CtdlThreadGetWorkers() < config.c_max_workers) && (CtdlThreadGetWorkers() < num_sessions)) - // && (CtdlThreadLoadAvg < 90) ) - if ((CtdlThreadGetWorkers() < config.c_max_workers) && (CtdlThreadGetWorkerAvg() > 60) && (CtdlThreadGetLoadAvg() < 90) && (CT->state > CTDL_THREAD_STOP_REQ)) - { - for (i=0; i<5 ; i++) -// for (i=0; i< (num_sessions - CtdlThreadGetWorkers()) ; i++) -// for (i=0; i< (10 - (55 - CtdlThreadWorkerAvg) / CtdlThreadWorkerAvg / CtdlThreadGetWorkers()) ; i++) - { -// begin_critical_section(S_THREAD_LIST); - CtdlThreadCreate("Worker Thread", - CTDLTHREAD_BIGSTACK + CTDLTHREAD_WORKER, - worker_thread, - NULL - ); -// end_critical_section(S_THREAD_LIST); - } - } - - CtdlThreadGC(); - - if (CtdlThreadGetCount() <= 1) // Shutting down clean up the garbage collector - { - CtdlThreadGC(); - } - - if (CtdlThreadGetCount()) - CtdlThreadSleep(1); + int exit_code = master_cleanup(exit_signal); + ctdl_lockfile(0); + if (restart_server) { + execv(argv[0], argv); } - /* - * If the above loop exits we must be shutting down since we obviously have no threads - */ - ctdl_thread_internal_cleanup(); + return(exit_code); }