Back to index

citadel  8.12
server_main.c
Go to the documentation of this file.
00001 /*
00002  * citserver's main() function lives here.
00003  * 
00004  * Copyright (c) 1987-2012 by the citadel.org team
00005  *
00006  * This program is open source software; you can redistribute it and/or modify
00007  * it under the terms of the GNU General Public License version 3.
00008  *
00009  * This program is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  * GNU General Public License for more details.
00013  */
00014 
00015 #include "sysdep.h"
00016 #include <stdlib.h>
00017 #include <unistd.h>
00018 #include <stdio.h>
00019 #include <fcntl.h>
00020 #include <ctype.h>
00021 #include <signal.h>
00022 #include <sys/types.h>
00023 #include <sys/stat.h>
00024 #include <sys/wait.h>
00025 #include <sys/socket.h>
00026 #include <syslog.h>
00027 
00028 #if TIME_WITH_SYS_TIME
00029 # include <sys/time.h>
00030 # include <time.h>
00031 #else
00032 # if HAVE_SYS_TIME_H
00033 #  include <sys/time.h>
00034 # else
00035 #  include <time.h>
00036 # endif
00037 #endif
00038 
00039 #include <limits.h>
00040 #include <netinet/in.h>
00041 #include <netdb.h>
00042 #include <sys/un.h>
00043 #include <string.h>
00044 #include <pwd.h>
00045 #include <errno.h>
00046 #include <stdarg.h>
00047 #include <grp.h>
00048 #include <pwd.h>
00049 #ifdef HAVE_SYS_PRCTL_H
00050 #include <sys/prctl.h>
00051 #endif
00052 #include <libcitadel.h>
00053 #include "citadel.h"
00054 #include "server.h"
00055 #include "serv_extensions.h"
00056 #include "sysdep_decls.h"
00057 #include "threads.h"
00058 #include "citserver.h"
00059 #include "support.h"
00060 #include "config.h"
00061 #include "control.h"
00062 #include "database.h"
00063 #include "user_ops.h"
00064 #include "housekeeping.h"
00065 #include "svn_revision.h"
00066 #include "citadel_dirs.h"
00067 
00068 #include "context.h"
00069 
00070 #include "modules_init.h"
00071 #include "ecrash.h"
00072 
00073 #ifdef HAVE_SYS_SELECT_H
00074 #include <sys/select.h>
00075 #endif
00076 
00077 #ifndef HAVE_SNPRINTF
00078 #include "snprintf.h"
00079 #endif
00080 const char *CitadelServiceUDS="citadel-UDS";
00081 const char *CitadelServiceTCP="citadel-TCP";
00082 
00083 
00084 
00085 void go_threading(void);
00086 
00087 /*
00088  * Here's where it all begins.
00089  */
00090 int main(int argc, char **argv)
00091 {
00092        char facility[32];
00093        int a;               /* General-purpose variables */
00094        struct passwd pw, *pwp = NULL;
00095        char pwbuf[SIZ];
00096        int drop_root_perms = 1;
00097        int relh=0;
00098        int home=0;
00099        int dbg=0;
00100        char relhome[PATH_MAX]="";
00101        char ctdldir[PATH_MAX]=CTDLDIR;
00102        int syslog_facility = LOG_DAEMON;
00103        const char *eDebuglist[] = {NULL, NULL};
00104 #ifdef HAVE_RUN_DIR
00105        struct stat filestats;
00106 #endif
00107 #ifdef HAVE_BACKTRACE
00108        eCrashParameters params;
00109 //     eCrashSymbolTable symbol_table;
00110 #endif
00111 
00112        /* initialize the master context */
00113        InitializeMasterCC();
00114        InitializeMasterTSD();
00115 
00116        /* parse command-line arguments */
00117        while ((a=getopt(argc, argv, "l:dh:x:t:Dr")) != EOF) switch(a) {
00118 
00119               case 'l':
00120                      safestrncpy(facility, optarg, sizeof(facility));
00121                      syslog_facility = SyslogFacility(facility);
00122                      break;
00123 
00124               /* run in the background if -d was specified */
00125               case 'd':
00126                      running_as_daemon = 1;
00127                      break;
00128 
00129               case 'h':
00130                      relh = optarg[0] != '/';
00131                      if (!relh) {
00132                             safestrncpy(ctdl_home_directory, optarg, sizeof ctdl_home_directory);
00133                      }
00134                      else {
00135                             safestrncpy(relhome, optarg, sizeof relhome);
00136                      }
00137                      home=1;
00138                      break;
00139 
00140               case 'x':
00141                      eDebuglist [0] = optarg;
00142                      break;
00143 
00144               case 't':     /* deprecated */
00145                      break;
00146 
00147               case 'D':
00148                      dbg = 1;
00149                      break;
00150 
00151               /* -r tells the server not to drop root permissions.
00152                * Don't use this unless you know what you're doing.
00153                */
00154               case 'r':
00155                      drop_root_perms = 0;
00156                      break;
00157 
00158               default:
00159               /* any other parameter makes it crash and burn */
00160                      fprintf(stderr,      "citserver: usage: "
00161                                    "citserver "
00162                                    "[-l LogFacility] "
00163                                    "[-d] [-D] [-r] "
00164                                    "[-h HomeDir]\n"
00165                      );
00166                      exit(1);
00167        }
00168 
00169        openlog("citserver",
00170               ( running_as_daemon ? (LOG_PID) : (LOG_PID | LOG_PERROR) ),
00171               syslog_facility
00172        );
00173 
00174        calc_dirs_n_files(relh, home, relhome, ctdldir, dbg);
00175        /* daemonize, if we were asked to */
00176        if (running_as_daemon) {
00177               start_daemon(0);
00178               drop_root_perms = 1;
00179        }
00180 
00181 #ifdef HAVE_BACKTRACE
00182        bzero(&params, sizeof(params));
00183        params.filename = file_pid_paniclog;
00184        panic_fd=open(file_pid_paniclog, O_APPEND|O_CREAT|O_DIRECT);
00185        params.filep = fopen(file_pid_paniclog, "a+");
00186        params.debugLevel = ECRASH_DEBUG_VERBOSE;
00187        params.dumpAllThreads = TRUE;
00188        params.useBacktraceSymbols = 1;
00189        params.signals[0]=SIGSEGV;
00190        params.signals[1]=SIGILL;
00191        params.signals[2]=SIGBUS;
00192        params.signals[3]=SIGABRT;
00193        eCrash_Init(&params);
00194        eCrash_RegisterThread("MasterThread", 0);
00195 #endif
00196 
00197        /* Tell 'em who's in da house */
00198        syslog(LOG_NOTICE, " ");
00199        syslog(LOG_NOTICE, " ");
00200        syslog(LOG_NOTICE,
00201               "*** Citadel server engine v%d.%02d (build %s) ***",
00202               (REV_LEVEL/100), (REV_LEVEL%100), svn_revision());
00203        syslog(LOG_NOTICE, "Copyright (C) 1987-2012 by the Citadel development team.");
00204        syslog(LOG_NOTICE, "This program is distributed under the terms of the GNU "
00205                                    "General Public License.");
00206        syslog(LOG_NOTICE, " ");
00207        syslog(LOG_DEBUG, "Called as: %s", argv[0]);
00208        syslog(LOG_INFO, "%s", libcitadel_version_string());
00209 
00210        /* Load site-specific configuration */
00211        syslog(LOG_INFO, "Loading citadel.config");
00212        get_config();
00213 
00214        /* get_control() MUST MUST MUST be called BEFORE the databases are opened!! */
00215        syslog(LOG_INFO, "Acquiring control record");
00216        get_control();
00217 
00218        put_config();
00219 
00220 #ifdef HAVE_RUN_DIR
00221        /* on some dists rundir gets purged on startup. so we need to recreate it. */
00222 
00223        if (stat(ctdl_run_dir, &filestats)==-1){
00224 #ifdef HAVE_GETPWUID_R
00225 #ifdef SOLARIS_GETPWUID
00226               pwp = getpwuid_r(config.c_ctdluid, &pw, pwbuf, sizeof(pwbuf));
00227 #else // SOLARIS_GETPWUID
00228               getpwuid_r(config.c_ctdluid, &pw, pwbuf, sizeof(pwbuf), &pwp);
00229 #endif // SOLARIS_GETPWUID
00230 #else // HAVE_GETPWUID_R
00231               pwp = NULL;
00232 #endif // HAVE_GETPWUID_R
00233 
00234               if ((mkdir(ctdl_run_dir, 0755) != 0) && (errno != EEXIST))
00235                      syslog(LOG_EMERG, 
00236                                   "unable to create run directory [%s]: %s", 
00237                                   ctdl_run_dir, strerror(errno));
00238 
00239               if (chown(ctdl_run_dir, config.c_ctdluid, (pwp==NULL)?-1:pw.pw_gid) != 0)
00240                      syslog(LOG_EMERG, 
00241                                   "unable to set the access rights for [%s]: %s", 
00242                                   ctdl_run_dir, strerror(errno));
00243        }
00244                      
00245 
00246 #endif
00247 
00248        /* Initialize... */
00249        init_sysdep();
00250 
00251        /*
00252         * Do non system dependent startup functions.
00253         */
00254        master_startup();
00255 
00256        /*
00257         * Check that the control record is correct and place sensible values if it isn't
00258         */
00259        check_control();
00260        
00261        /*
00262         * Run any upgrade entry points
00263         */
00264        syslog(LOG_INFO, "Upgrading modules.");
00265        upgrade_modules();
00266        
00267 /*
00268  * Load the user for the masterCC or create them if they don't exist
00269  */
00270        if (CtdlGetUser(&masterCC.user, "SYS_Citadel"))
00271        {
00272               /* User doesn't exist. We can't use create user here as the user number needs to be 0 */
00273               strcpy (masterCC.user.fullname, "SYS_Citadel") ;
00274               CtdlPutUser(&masterCC.user);
00275               CtdlGetUser(&masterCC.user, "SYS_Citadel"); /* Just to be safe */
00276        }
00277        
00278        /*
00279         * Bind the server to a Unix-domain socket (user client access)
00280         */
00281        CtdlRegisterServiceHook(0,
00282                             file_citadel_socket,
00283                             citproto_begin_session,
00284                             do_command_loop,
00285                             do_async_loop,
00286                             CitadelServiceUDS);
00287 
00288        /*
00289         * Bind the server to a Unix-domain socket (admin client access)
00290         */
00291        CtdlRegisterServiceHook(0,
00292                             file_citadel_admin_socket,
00293                             citproto_begin_admin_session,
00294                             do_command_loop,
00295                             do_async_loop,
00296                             CitadelServiceUDS);
00297        chmod(file_citadel_admin_socket, S_IRWXU);       /* for your eyes only */
00298 
00299        /*
00300         * Bind the server to our favorite TCP port (usually 504).
00301         */
00302        CtdlRegisterServiceHook(config.c_port_number,
00303                             NULL,
00304                             citproto_begin_session,
00305                             do_command_loop,
00306                             do_async_loop,
00307                             CitadelServiceTCP);
00308 
00309                             
00310        
00311        
00312        /*
00313         * Load any server-side extensions available here.
00314         */
00315        syslog(LOG_INFO, "Initializing server extensions");
00316        
00317        initialise_modules(0);
00318 
00319        eDebuglist[1] = getenv("CITADEL_LOGDEBUG");
00320        CtdlSetDebugLogFacilities(eDebuglist, 2);
00321 
00322        /*
00323         * If we need host auth, start our chkpwd daemon.
00324         */
00325        if (config.c_auth_mode == AUTHMODE_HOST) {
00326               start_chkpwd_daemon();
00327        }
00328 
00329 
00330        /*
00331         * check, whether we're fired up another time after a crash.
00332         * if, post an aide message, so the admin has a chance to react.
00333         */
00334        checkcrash ();
00335 
00336 
00337        /*
00338         * Now that we've bound the sockets, change to the Citadel user id and its
00339         * corresponding group ids
00340         */
00341        if (drop_root_perms) {
00342               cdb_chmod_data();    /* make sure we own our data files */
00343 
00344 #ifdef HAVE_GETPWUID_R
00345 #ifdef SOLARIS_GETPWUID
00346               pwp = getpwuid_r(config.c_ctdluid, &pw, pwbuf, sizeof(pwbuf));
00347 #else // SOLARIS_GETPWUID
00348               getpwuid_r(config.c_ctdluid, &pw, pwbuf, sizeof(pwbuf), &pwp);
00349 #endif // SOLARIS_GETPWUID
00350 #else // HAVE_GETPWUID_R
00351               pwp = NULL;
00352 #endif // HAVE_GETPWUID_R
00353 
00354               if (pwp == NULL)
00355                      syslog(LOG_CRIT, "WARNING: getpwuid(%ld): %s"
00356                                "Group IDs will be incorrect.\n", (long)CTDLUID,
00357                             strerror(errno));
00358               else {
00359                      initgroups(pw.pw_name, pw.pw_gid);
00360                      if (setgid(pw.pw_gid))
00361                             syslog(LOG_CRIT, "setgid(%ld): %s", (long)pw.pw_gid,
00362                                    strerror(errno));
00363               }
00364               syslog(LOG_INFO, "Changing uid to %ld", (long)CTDLUID);
00365               if (setuid(CTDLUID) != 0) {
00366                      syslog(LOG_CRIT, "setuid() failed: %s", strerror(errno));
00367               }
00368 #if defined (HAVE_SYS_PRCTL_H) && defined (PR_SET_DUMPABLE)
00369               prctl(PR_SET_DUMPABLE, 1);
00370 #endif
00371        }
00372 
00373        /* We want to check for idle sessions once per minute */
00374        CtdlRegisterSessionHook(terminate_idle_sessions, EVT_TIMER, PRIO_CLEANUP + 1);
00375 
00376        go_threading();
00377        
00378        master_cleanup(exit_signal);
00379        return(0);
00380 }