3ac69266d64099991dff643937446517b03d6a15
[citadel.git] / citadel / citserver.c
1 /* 
2  * Main source module for the Citadel server
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
15 #include <stdlib.h>
16 #include <unistd.h>
17 #include <stdio.h>
18 #include <sys/stat.h>
19 #include "sysdep.h"
20 #include <time.h>
21 #if HAVE_BACKTRACE
22 #include <execinfo.h>
23 #endif
24 #include <libcitadel.h>
25
26 #include "ctdl_module.h"
27 #include "housekeeping.h"
28 #include "locate_host.h"
29 #include "citserver.h"
30 #include "user_ops.h"
31 #include "control.h"
32 #include "config.h"
33
34 char *unique_session_numbers;
35 int ScheduledShutdown = 0;
36 time_t server_startup_time;
37 int panic_fd;
38 int openid_level_supported = 0;
39
40 /*
41  * print the actual stack frame.
42  */
43 void cit_backtrace(void)
44 {
45 #ifdef HAVE_BACKTRACE
46         void *stack_frames[50];
47         size_t size, i;
48         char **strings;
49
50         const char *p = IOSTR;
51         if (p == NULL) p = "";
52         size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
53         strings = backtrace_symbols(stack_frames, size);
54         for (i = 0; i < size; i++) {
55                 if (strings != NULL) {
56                         syslog(LOG_DEBUG, "citserver: %s %s", p, strings[i]);
57                 }
58                 else {
59                         syslog(LOG_DEBUG, "citserver: %s %p", p, stack_frames[i]);
60                 }
61         }
62         free(strings);
63 #endif
64 }
65
66 void cit_oneline_backtrace(void)
67 {
68 #ifdef HAVE_BACKTRACE
69         void *stack_frames[50];
70         size_t size, i;
71         char **strings;
72         StrBuf *Buf;
73
74         size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
75         strings = backtrace_symbols(stack_frames, size);
76         if (size > 0)
77         {
78                 Buf = NewStrBuf();
79                 for (i = 1; i < size; i++) {
80                         if (strings != NULL)
81                                 StrBufAppendPrintf(Buf, "%s : ", strings[i]);
82                         else
83                                 StrBufAppendPrintf(Buf, "%p : ", stack_frames[i]);
84                 }
85                 free(strings);
86                 syslog(LOG_DEBUG, "citserver: %s %s", IOSTR, ChrPtr(Buf));
87                 FreeStrBuf(&Buf);
88         }
89 #endif
90 }
91
92
93 /*
94  * print the actual stack frame.
95  */
96 void cit_panic_backtrace(int SigNum)
97 {
98 #ifdef HAVE_BACKTRACE
99         void *stack_frames[10];
100         size_t size, i;
101         char **strings;
102
103         printf("caught signal 11\n");
104         size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
105         strings = backtrace_symbols(stack_frames, size);
106         for (i = 0; i < size; i++) {
107                 if (strings != NULL) {
108                         syslog(LOG_DEBUG, "%s", strings[i]);
109                 }
110                 else {
111                         syslog(LOG_DEBUG, "%p", stack_frames[i]);
112                 }
113         }
114         free(strings);
115 #endif
116         exit(-1);
117 }
118
119 /*
120  * Various things that need to be initialized at startup
121  */
122 void master_startup(void) {
123         struct timeval tv;
124         unsigned int seed;
125         FILE *urandom;
126         struct ctdlroom qrbuf;
127         int rv;
128         struct passwd *pw;
129         gid_t gid;
130         
131         syslog(LOG_DEBUG, "master_startup() started");
132         time(&server_startup_time);
133
134         syslog(LOG_INFO, "Checking directory access");
135         if ((pw = getpwuid(ctdluid)) == NULL) {
136                 gid = getgid();
137         } else {
138                 gid = pw->pw_gid;
139         }
140
141         if (create_run_directories(CTDLUID, gid) != 0) {
142                 syslog(LOG_EMERG, "failed to access & create directories");
143                 exit(1);
144         }
145         syslog(LOG_INFO, "Opening databases");
146         open_databases();
147
148         /* Load site-specific configuration */
149         syslog(LOG_INFO, "Initializing configuration system");
150         initialize_config_system();
151         validate_config();
152         migrate_legacy_control_record();
153
154         /* Check floor reference counts */
155         check_ref_counts();
156
157         syslog(LOG_INFO, "Creating base rooms (if necessary)");
158         CtdlCreateRoom(CtdlGetConfigStr("c_baseroom"),  0, "", 0, 1, 0, VIEW_BBS);
159         CtdlCreateRoom(AIDEROOM,                        3, "", 0, 1, 0, VIEW_BBS);
160         CtdlCreateRoom(SYSCONFIGROOM,                   3, "", 0, 1, 0, VIEW_BBS);
161         CtdlCreateRoom(CtdlGetConfigStr("c_twitroom"),  0, "", 0, 1, 0, VIEW_BBS);
162
163         /* The "Local System Configuration" room doesn't need to be visible */
164         if (CtdlGetRoomLock(&qrbuf, SYSCONFIGROOM) == 0) {
165                 qrbuf.QRflags2 |= QR2_SYSTEM;
166                 CtdlPutRoomLock(&qrbuf);
167         }
168
169         /* Aide needs to be public postable, else we're not RFC conformant. */
170         if (CtdlGetRoomLock(&qrbuf, AIDEROOM) == 0) {
171                 qrbuf.QRflags2 |= QR2_SMTP_PUBLIC;
172                 CtdlPutRoomLock(&qrbuf);
173         }
174
175         syslog(LOG_INFO, "Seeding the pseudo-random number generator...");
176         urandom = fopen("/dev/urandom", "r");
177         if (urandom != NULL) {
178                 rv = fread(&seed, sizeof seed, 1, urandom);
179                 if (rv == -1) {
180                         syslog(LOG_ERR, "citserver: failed to read random seed: %m");
181                 }
182                 fclose(urandom);
183         }
184         else {
185                 gettimeofday(&tv, NULL);
186                 seed = tv.tv_usec;
187         }
188         srand(seed);
189         srandom(seed);
190
191         syslog(LOG_DEBUG, "master_startup() finished");
192 }
193
194
195 /*
196  * Cleanup routine to be called when the server is shutting down.  Returns the needed exit code.
197  */
198 int master_cleanup(int exitcode) {
199         struct CleanupFunctionHook *fcn;
200         static int already_cleaning_up = 0;
201
202         if (already_cleaning_up) while(1) usleep(1000000);
203         already_cleaning_up = 1;
204
205         /* Run any cleanup routines registered by loadable modules */
206         for (fcn = CleanupHookTable; fcn != NULL; fcn = fcn->next) {
207                 (*fcn->h_function_pointer)();
208         }
209
210         /* Close the AdjRefCount queue file */
211         AdjRefCount(-1, 0);
212
213         /* Do system-dependent stuff */
214         sysdep_master_cleanup();
215
216         /* Close the configuration system */
217         shutdown_config_system();
218         
219         /* Close databases */
220         syslog(LOG_INFO, "Closing databases\n");
221         close_databases();
222
223         /* If the operator requested a halt but not an exit, halt here. */
224         if (shutdown_and_halt) {
225                 syslog(LOG_ERR, "citserver: Halting server without exiting.");
226                 fflush(stdout); fflush(stderr);
227                 while(1) {
228                         sleep(32767);
229                 }
230         }
231         
232         /* Now go away. */
233         syslog(LOG_ERR, "citserver: Exiting with status %d", exitcode);
234         fflush(stdout); fflush(stderr);
235         
236         if (restart_server != 0) {
237                 exitcode = 1;
238         }
239         else if ((running_as_daemon != 0) && ((exitcode == 0) )) {
240                 exitcode = CTDLEXIT_SHUTDOWN;
241         }
242         return(exitcode);
243 }
244
245
246
247 /*
248  * returns an asterisk if there are any instant messages waiting,
249  * space otherwise.
250  */
251 char CtdlCheckExpress(void) {
252         if (CC->FirstExpressMessage == NULL) {
253                 return(' ');
254         }
255         else {
256                 return('*');
257         }
258 }
259
260
261 /*
262  * Check originating host against the public_clients file.  This determines
263  * whether the client is allowed to change the hostname for this session
264  * (for example, to show the location of the user rather than the location
265  * of the client).
266  */
267 int CtdlIsPublicClient(void)
268 {
269         char buf[1024];
270         char addrbuf[1024];
271         FILE *fp;
272         int i;
273         char *public_clientspos;
274         char *public_clientsend;
275         char *paddr = NULL;
276         struct stat statbuf;
277         static time_t pc_timestamp = 0;
278         static char public_clients[SIZ];
279         static char public_clients_file[SIZ];
280
281 #define LOCALHOSTSTR "127.0.0.1"
282
283         snprintf(public_clients_file, sizeof public_clients_file, "%s/public_clients", ctdl_etc_dir);
284
285         /*
286          * Check the time stamp on the public_clients file.  If it's been
287          * updated since the last time we were here (or if this is the first
288          * time we've been through the loop), read its contents and learn
289          * the IP addresses of the listed hosts.
290          */
291         if (stat(public_clients_file, &statbuf) != 0) {
292                 /* No public_clients file exists, so bail out */
293                 syslog(LOG_WARNING, "Warning: '%s' does not exist", public_clients_file);
294                 return(0);
295         }
296
297         if (statbuf.st_mtime > pc_timestamp) {
298                 begin_critical_section(S_PUBLIC_CLIENTS);
299                 syslog(LOG_INFO, "Loading %s", public_clients_file);
300
301                 public_clientspos = &public_clients[0];
302                 public_clientsend = public_clientspos + SIZ;
303                 safestrncpy(public_clientspos, LOCALHOSTSTR, sizeof public_clients);
304                 public_clientspos += sizeof(LOCALHOSTSTR) - 1;
305                 
306                 if (hostname_to_dotted_quad(addrbuf, CtdlGetConfigStr("c_fqdn")) == 0) {
307                         *(public_clientspos++) = '|';
308                         paddr = &addrbuf[0];
309                         while (!IsEmptyStr (paddr) && 
310                                (public_clientspos < public_clientsend))
311                                 *(public_clientspos++) = *(paddr++);
312                 }
313
314                 fp = fopen(public_clients_file, "r");
315                 if (fp != NULL) 
316                         while ((fgets(buf, sizeof buf, fp)!=NULL) &&
317                                (public_clientspos < public_clientsend)){
318                                 char *ptr;
319                                 ptr = buf;
320                                 while (!IsEmptyStr(ptr)) {
321                                         if (*ptr == '#') {
322                                                 *ptr = 0;
323                                                 break;
324                                         }
325                                 else ptr++;
326                                 }
327                                 ptr--;
328                                 while (ptr>buf && isspace(*ptr)) {
329                                         *(ptr--) = 0;
330                                 }
331                                 if (hostname_to_dotted_quad(addrbuf, buf) == 0) {
332                                         *(public_clientspos++) = '|';
333                                         paddr = addrbuf;
334                                         while (!IsEmptyStr(paddr) && 
335                                                (public_clientspos < public_clientsend)){
336                                                 *(public_clientspos++) = *(paddr++);
337                                         }
338                                 }
339                         }
340                 if (fp != NULL) fclose(fp);
341                 pc_timestamp = time(NULL);
342                 end_critical_section(S_PUBLIC_CLIENTS);
343         }
344
345         syslog(LOG_DEBUG, "Checking whether %s is a local or public client", CC->cs_addr);
346         for (i=0; i<num_parms(public_clients); ++i) {
347                 extract_token(addrbuf, public_clients, i, '|', sizeof addrbuf);
348                 if (!strcasecmp(CC->cs_addr, addrbuf)) {
349                         syslog(LOG_DEBUG, "... yes its local.");
350                         return(1);
351                 }
352         }
353
354         /* No hits.  This is not a public client. */
355         syslog(LOG_DEBUG, "... no it isn't.");
356         return(0);
357 }
358
359
360
361
362
363 void citproto_begin_session() {
364         if (CC->nologin==1) {
365                 cprintf("%d %s: Too many users are already online (maximum is %d)\n",
366                         ERROR + MAX_SESSIONS_EXCEEDED,
367                         CtdlGetConfigStr("c_nodename"), CtdlGetConfigInt("c_maxsessions")
368                 );
369                 CC->kill_me = KILLME_MAX_SESSIONS_EXCEEDED;
370         }
371         else {
372                 cprintf("%d %s Citadel server ready.\n", CIT_OK, CtdlGetConfigStr("c_nodename"));
373                 CC->can_receive_im = 1;
374         }
375 }
376
377
378 void citproto_begin_admin_session() {
379         CC->internal_pgm = 1;
380         cprintf("%d %s Citadel server ADMIN CONNECTION ready.\n", CIT_OK, CtdlGetConfigStr("c_nodename"));
381 }
382
383
384 /*
385  * This loop performs all asynchronous functions.
386  */
387 void do_async_loop(void) {
388         PerformSessionHooks(EVT_ASYNC);
389 }
390