Initial version of new room sharing poller. I don't really like this because it...
[citadel.git] / citadel / server_main.c
1 /*
2  * citserver's main() function lives here.
3  * 
4  * Copyright (c) 1987-2017 by the citadel.org team
5  *
6  * This program is open source software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 3.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  */
14 #include <stdlib.h>
15 #include <unistd.h>
16 #include <stdio.h>
17 #include <sys/types.h>
18 #include <sys/stat.h>
19 #include <grp.h>
20 #include <sys/file.h>
21 #include <libcitadel.h>
22
23 #include "citserver.h"
24 #include "svn_revision.h"
25 #include "modules_init.h"
26 #include "config.h"
27 #include "control.h"
28 #include "serv_extensions.h"
29 #include "citadel_dirs.h"
30 #include "user_ops.h"
31 #include "ecrash.h"
32
33 uid_t ctdluid = 0;
34 const char *CitadelServiceUDS="citadel-UDS";
35 const char *CitadelServiceTCP="citadel-TCP";
36
37
38
39 void go_threading(void);
40
41
42
43 /*
44  * Create or remove a lock file, so we only have one Citadel Server running at a time.
45  */
46 void ctdl_lockfile(int yo) {
47         static char lockfilename[SIZ];
48         static FILE *fp;
49
50
51         if (yo) {
52                 syslog(LOG_DEBUG, "main: creating lockfile");
53                 snprintf(lockfilename, sizeof lockfilename, "%s/citadel.lock", ctdl_run_dir);
54                 fp = fopen(lockfilename, "w");
55                 if (!fp) {
56                         syslog(LOG_ERR, "%s: %s", lockfilename, strerror(errno));
57                         exit(CTDLEXIT_DB);
58                 }
59                 if (flock(fileno(fp), (LOCK_EX|LOCK_NB)) != 0) {
60                         syslog(LOG_ERR, "main: cannot lock %s , is another citserver running?", lockfilename);
61                         exit(CTDLEXIT_DB);
62                 }
63                 return;
64         }
65
66         syslog(LOG_DEBUG, "main: removing lockfile");
67         unlink(lockfilename);
68         flock(fileno(fp), LOCK_UN);
69         fclose(fp);
70 }
71
72
73
74
75
76
77 /*
78  * Here's where it all begins.
79  */
80 int main(int argc, char **argv)
81 {
82         size_t basesize = 64;
83         char facility[32];
84         int a;                  /* General-purpose variables */
85         struct passwd pw, *pwp = NULL;
86         char pwbuf[SIZ];
87         int drop_root_perms = 1;
88         int relh=0;
89         int home=0;
90         int dbg=0;
91         char relhome[PATH_MAX]="";
92         char ctdldir[PATH_MAX]=CTDLDIR;
93         int syslog_facility = LOG_DAEMON;
94         const char *eDebuglist[] = {NULL, NULL};
95         uid_t u = 0;
96         struct passwd *p = NULL;
97 #ifdef HAVE_RUN_DIR
98         struct stat filestats;
99 #endif
100 #ifdef HAVE_BACKTRACE
101         eCrashParameters params;
102 //      eCrashSymbolTable symbol_table;
103 #endif
104
105         /* initialize the master context */
106         InitializeMasterCC();
107         InitializeMasterTSD();
108
109         /* parse command-line arguments */
110         while ((a=getopt(argc, argv, "l:dh:x:t:B:Dru:")) != EOF) switch(a) {
111
112                 case 'l':
113                         safestrncpy(facility, optarg, sizeof(facility));
114                         syslog_facility = SyslogFacility(facility);
115                         break;
116
117                 /* run in the background if -d was specified */
118                 case 'd':
119                         running_as_daemon = 1;
120                         break;
121
122                 case 'h':
123                         relh = optarg[0] != '/';
124                         if (!relh) {
125                                 safestrncpy(ctdl_home_directory, optarg, sizeof ctdl_home_directory);
126                         }
127                         else {
128                                 safestrncpy(relhome, optarg, sizeof relhome);
129                         }
130                         home=1;
131                         break;
132
133                 case 'x':
134                         eDebuglist [0] = optarg;
135                         break;
136
137                 case 't':       /* deprecated */
138                         break;
139                 case 'B': /* Basesize */
140                         basesize = atoi(optarg);
141                         break;
142
143                 case 'D':
144                         dbg = 1;
145                         break;
146
147                 /* -r tells the server not to drop root permissions.
148                  * Don't use this unless you know what you're doing.
149                  */
150                 case 'r':
151                         drop_root_perms = 0;
152                         break;
153
154                 /* -u tells the server what uid to run under... */
155                 case 'u':
156                         u = atoi(optarg);
157                         if (u > 0) {
158                                 ctdluid = u;
159                         }
160                         else {
161                                 p = getpwnam(optarg);
162                                 if (p) {
163                                         u = p->pw_uid;
164                                 }
165                         }
166                         if (u > 0) {
167                                 ctdluid = u;
168                         }
169                         break;
170
171                 default:
172                 /* any other parameter makes it crash and burn */
173                         fprintf(stderr, "citserver: usage: "
174                                         "citserver "
175                                         "[-l LogFacility] "
176                                         "[-d] [-D] [-r] "
177                                         "[-u user] "
178                                         "[-h HomeDir]\n"
179                         );
180                         exit(1);
181         }
182
183         /* Last ditch effort to determine the user name ... if there's a user called "citadel" then use that */
184         if (ctdluid == 0) {
185                 p = getpwnam("citadel");
186                 if (!p) {
187                         p = getpwnam("bbs");
188                 }
189                 if (!p) {
190                         p = getpwnam("guest");
191                 }
192                 if (p) {
193                         u = p->pw_uid;
194                 }
195                 if (u > 0) {
196                         ctdluid = u;
197                 }
198         }
199
200         if ((ctdluid == 0) && (drop_root_perms == 0)) {
201                 fprintf(stderr, "citserver: cannot determine user to run as; please specify -r or -u options\n");
202                 exit(CTDLEXIT_UNUSER);
203         }
204
205         StartLibCitadel(basesize);
206         openlog("citserver",
207                 ( running_as_daemon ? (LOG_PID) : (LOG_PID | LOG_PERROR) ),
208                 syslog_facility
209         );
210
211         calc_dirs_n_files(relh, home, relhome, ctdldir, dbg);
212         /* daemonize, if we were asked to */
213         if (running_as_daemon) {
214                 start_daemon(0);
215                 drop_root_perms = 1;
216         }
217
218         /* Tell 'em who's in da house */
219         syslog(LOG_NOTICE, " ");
220         syslog(LOG_NOTICE, " ");
221         syslog(LOG_NOTICE, "*** Citadel server engine ***\n");
222         syslog(LOG_NOTICE, "Version %d (build %s) ***", REV_LEVEL, svn_revision());
223         syslog(LOG_NOTICE, "Copyright (C) 1987-2017 by the Citadel development team.");
224         syslog(LOG_NOTICE, "This program is distributed under the terms of the GNU "
225                                         "General Public License.");
226         syslog(LOG_NOTICE, " ");
227         syslog(LOG_DEBUG, "Called as: %s", argv[0]);
228         syslog(LOG_INFO, "%s", libcitadel_version_string());
229
230 #ifdef HAVE_RUN_DIR
231         /* on some dists rundir gets purged on startup. so we need to recreate it. */
232
233         if (stat(ctdl_run_dir, &filestats)==-1){
234 #ifdef HAVE_GETPWUID_R
235 #ifdef SOLARIS_GETPWUID
236                 pwp = getpwuid_r(ctdluid, &pw, pwbuf, sizeof(pwbuf));
237 #else // SOLARIS_GETPWUID
238                 getpwuid_r(ctdluid, &pw, pwbuf, sizeof(pwbuf), &pwp);
239 #endif // SOLARIS_GETPWUID
240 #else // HAVE_GETPWUID_R
241                 pwp = NULL;
242 #endif // HAVE_GETPWUID_R
243
244                 if ((mkdir(ctdl_run_dir, 0755) != 0) && (errno != EEXIST)) {
245                         syslog(LOG_ERR, "main: unable to create run directory [%s]: %s", ctdl_run_dir, strerror(errno));
246                 }
247
248                 if (chown(ctdl_run_dir, ctdluid, (pwp==NULL)?-1:pw.pw_gid) != 0) {
249                         syslog(LOG_ERR, "main: unable to set the access rights for [%s]: %s", ctdl_run_dir, strerror(errno));
250                 }
251         }
252 #endif
253
254         ctdl_lockfile(1);
255
256         /* Initialize... */
257         init_sysdep();
258
259         /*
260          * Do non system dependent startup functions.
261          */
262         master_startup();
263
264         /*
265          * Check that the control record is correct and place sensible values if it isn't
266          */
267         check_control();
268         
269         /*
270          * Run any upgrade entry points
271          */
272         syslog(LOG_INFO, "main: upgrading modules");
273         upgrade_modules();
274         
275 /*
276  * Load the user for the masterCC or create them if they don't exist
277  */
278         if (CtdlGetUser(&masterCC.user, "SYS_Citadel"))
279         {
280                 /* User doesn't exist. We can't use create user here as the user number needs to be 0 */
281                 strcpy (masterCC.user.fullname, "SYS_Citadel") ;
282                 CtdlPutUser(&masterCC.user);
283                 CtdlGetUser(&masterCC.user, "SYS_Citadel"); /* Just to be safe */
284         }
285         
286         /*
287          * Bind the server to a Unix-domain socket (user client access)
288          */
289         CtdlRegisterServiceHook(0,
290                                 file_citadel_socket,
291                                 citproto_begin_session,
292                                 do_command_loop,
293                                 do_async_loop,
294                                 CitadelServiceUDS);
295
296         /*
297          * Bind the server to a Unix-domain socket (admin client access)
298          */
299         CtdlRegisterServiceHook(0,
300                                 file_citadel_admin_socket,
301                                 citproto_begin_admin_session,
302                                 do_command_loop,
303                                 do_async_loop,
304                                 CitadelServiceUDS);
305         chmod(file_citadel_admin_socket, S_IRWXU);      /* for your eyes only */
306
307         /*
308          * Bind the server to our favorite TCP port (usually 504).
309          */
310         CtdlRegisterServiceHook(CtdlGetConfigInt("c_port_number"),
311                                 NULL,
312                                 citproto_begin_session,
313                                 do_command_loop,
314                                 do_async_loop,
315                                 CitadelServiceTCP);
316
317                                 
318         
319         
320         /*
321          * Load any server-side extensions available here.
322          */
323         syslog(LOG_INFO, "main: initializing server extensions");
324         
325         initialise_modules(0);
326
327         eDebuglist[1] = getenv("CITADEL_LOGDEBUG");
328         CtdlSetDebugLogFacilities(eDebuglist, 2);
329
330         /*
331          * If we need host auth, start our chkpwd daemon.
332          */
333         if (CtdlGetConfigInt("c_auth_mode") == AUTHMODE_HOST) {
334                 start_chkpwd_daemon();
335         }
336
337
338         /*
339          * check, whether we're fired up another time after a crash.
340          * if, post an aide message, so the admin has a chance to react.
341          */
342         checkcrash ();
343
344
345         /*
346          * Now that we've bound the sockets, change to the Citadel user id and its
347          * corresponding group ids
348          */
349         if (drop_root_perms) {
350                 cdb_chmod_data();       /* make sure we own our data files */
351
352 #ifdef HAVE_GETPWUID_R
353 #ifdef SOLARIS_GETPWUID
354                 pwp = getpwuid_r(ctdluid, &pw, pwbuf, sizeof(pwbuf));
355 #else // SOLARIS_GETPWUID
356                 getpwuid_r(ctdluid, &pw, pwbuf, sizeof(pwbuf), &pwp);
357 #endif // SOLARIS_GETPWUID
358 #else // HAVE_GETPWUID_R
359                 pwp = NULL;
360 #endif // HAVE_GETPWUID_R
361
362                 if (pwp == NULL)
363                         syslog(LOG_ERR, "main: WARNING, getpwuid(%ld): %s" "Group IDs will be incorrect.", (long)CTDLUID, strerror(errno));
364                 else {
365                         initgroups(pw.pw_name, pw.pw_gid);
366                         if (setgid(pw.pw_gid))
367                                 syslog(LOG_ERR, "main: setgid(%ld): %s", (long)pw.pw_gid, strerror(errno));
368                 }
369                 syslog(LOG_INFO, "main: changing uid to %ld", (long)CTDLUID);
370                 if (setuid(CTDLUID) != 0) {
371                         syslog(LOG_ERR, "main: setuid() failed: %s", strerror(errno));
372                 }
373 #if defined (HAVE_SYS_PRCTL_H) && defined (PR_SET_DUMPABLE)
374                 prctl(PR_SET_DUMPABLE, 1);
375 #endif
376         }
377
378         /* We want to check for idle sessions once per minute */
379         CtdlRegisterSessionHook(terminate_idle_sessions, EVT_TIMER, PRIO_CLEANUP + 1);
380
381         go_threading();
382         
383         int exit_code = master_cleanup(exit_signal);
384         ctdl_lockfile(0);
385         return(exit_code);
386 }