2 * WebCit "system dependent" code.
4 * Copyright (c) 1996-2012 by the citadel.org team
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.
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.
22 #include <sys/types.h>
25 #include <sys/socket.h>
27 #include <sys/syslog.h>
29 #if TIME_WITH_SYS_TIME
30 # include <sys/time.h>
34 # include <sys/time.h>
41 #include <sys/resource.h>
42 #include <netinet/in.h>
43 #include <netinet/tcp.h>
44 #include <arpa/inet.h>
58 #ifdef HAVE_SYS_SELECT_H
59 #include <sys/select.h>
62 #include "webserver.h"
63 #include "modules_init.h"
68 pthread_mutex_t Critters[MAX_SEMAPHORES]; /* Things needing locking */
69 pthread_key_t MyConKey; /* TSD key for MyContext() */
70 pthread_key_t MyReq; /* TSD key for MyReq() */
71 int msock; /* master listening socket */
72 int time_to_die = 0; /* Nonzero if server is shutting down */
74 extern void *context_loop(ParsedHttpHdrs *Hdr);
75 extern void *housekeeping_loop(void);
76 extern void do_housekeeping(void);
78 char ctdl_key_dir[PATH_MAX]=SSL_DIR;
79 char file_crpt_file_key[PATH_MAX]="";
80 char file_crpt_file_csr[PATH_MAX]="";
81 char file_crpt_file_cer[PATH_MAX]="";
82 char file_etc_mimelist[PATH_MAX]="";
84 const char editor_absolut_dir[PATH_MAX]=EDITORDIR; /* nailed to what configure gives us. */
85 const char markdown_editor_absolutedir[]=MARKDOWNEDITORDIR;
87 char etc_dir[PATH_MAX];
88 char static_dir[PATH_MAX]; /* calculated on startup */
89 char static_local_dir[PATH_MAX]; /* calculated on startup */
90 char static_icon_dir[PATH_MAX]; /* where should we find our mime icons? */
91 char *static_dirs[]={ /* needs same sort order as the web mapping */
92 (char*)static_dir, /* our templates on disk */
93 (char*)static_local_dir, /* user provided templates disk */
94 (char*)editor_absolut_dir, /* the editor on disk */
95 (char*)static_icon_dir, /* our icons... */
96 (char*)markdown_editor_absolutedir
100 HashList *GZMimeBlackList = NULL; /* mimetypes which shouldn't be gzip compressed */
102 void LoadMimeBlacklist(void)
104 StrBuf *MimeBlackLine;
108 memset(&IOB, 0, sizeof(IOBuffer));
109 IOB.fd = open(file_etc_mimelist, O_RDONLY);
111 IOB.Buf = NewStrBuf();
112 MimeBlackLine = NewStrBuf();
113 GZMimeBlackList = NewHash(1, NULL);
117 state = StrBufChunkSipLine(MimeBlackLine, &IOB);
122 if (StrBuf_read_one_chunk_callback(IOB.fd, 0, &IOB) <= 0)
126 if ((StrLength(MimeBlackLine) > 1) &&
127 (*ChrPtr(MimeBlackLine) != '#'))
129 Put(GZMimeBlackList, SKEY(MimeBlackLine),
130 (void*) 1, reference_free_handler);
132 FlushStrBuf(MimeBlackLine);
136 case eBufferNotEmpty:
140 while (state != eReadFail);
144 FreeStrBuf(&IOB.Buf);
145 FreeStrBuf(&MimeBlackLine);
148 void CheckGZipCompressionAllowed(const char *MimeType, long MLen)
153 if (WCC->Hdr->HR.gzip_ok)
154 WCC->Hdr->HR.gzip_ok = GetHash(GZMimeBlackList, MimeType, MLen, &v) == 0;
157 void InitialiseSemaphores(void)
161 /* Set up a bunch of semaphores to be used for critical sections */
162 for (i=0; i<MAX_SEMAPHORES; ++i) {
163 pthread_mutex_init(&Critters[i], NULL);
168 syslog(LOG_WARNING, "Failed to open exit pipe: %d [%s]\n",
177 * Obtain a semaphore lock to begin a critical section.
179 void begin_critical_section(int which_one)
181 pthread_mutex_lock(&Critters[which_one]);
185 * Release a semaphore lock to end a critical section.
187 void end_critical_section(int which_one)
189 pthread_mutex_unlock(&Critters[which_one]);
193 void ShutDownWebcit(void)
195 free_zone_directory ();
196 icaltimezone_release_zone_tab ();
197 icalmemory_free_ring ();
198 ShutDownLibCitadel ();
208 * Entry point for worker threads
210 void worker_entry(void)
214 int fail_this_transaction = 0;
217 memset(&Hdr, 0, sizeof(ParsedHttpHdrs));
218 Hdr.HR.eReqType = eGET;
219 http_new_modules(&Hdr);
222 /* Each worker thread blocks on accept() while waiting for something to do. */
223 fail_this_transaction = 0;
228 --num_threads_executing;
230 FD_SET(msock, &wset);
231 FD_SET(ExitPipe[1], &wset);
233 select(msock + 1, NULL, &wset, NULL, NULL);
237 ssock = accept(msock, NULL, 0);
238 ++num_threads_executing;
239 if (ssock < 0) fail_this_transaction = 1;
240 } while ((msock > 0) && (ssock < 0) && (time_to_die == 0));
242 if ((msock == -1)||(time_to_die))
243 {/* ok, we're going down. */
246 /* The first thread to get here will have to do the cleanup.
247 * Make sure it's really just one.
249 begin_critical_section(S_SHUTDOWN);
255 end_critical_section(S_SHUTDOWN);
257 {/* we're the one to cleanup the mess. */
258 http_destroy_modules(&Hdr);
259 syslog(LOG_DEBUG, "I'm master shutdown: tagging sessions to be killed.\n");
261 syslog(LOG_DEBUG, "master shutdown: waiting for others\n");
262 sleeeeeeeeeep(1); /* wait so some others might finish... */
263 syslog(LOG_DEBUG, "master shutdown: cleaning up sessions\n");
265 syslog(LOG_DEBUG, "master shutdown: cleaning up libical\n");
269 syslog(LOG_DEBUG, "master shutdown exiting.\n");
274 if (ssock < 0 ) continue;
276 check_thread_pool_size();
278 /* Now do something. */
280 if (ssock > 0) close (ssock);
281 syslog(LOG_DEBUG, "in between.");
284 /* Got it? do some real work! */
285 /* Set the SO_REUSEADDR socket option */
287 setsockopt(ssock, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i));
289 /* If we are an HTTPS server, go crypto now. */
292 if (starttls(ssock) != 0) {
293 fail_this_transaction = 1;
301 fdflags = fcntl(ssock, F_GETFL);
303 syslog(LOG_WARNING, "unable to get server socket flags! %s \n",
305 fdflags = fdflags | O_NONBLOCK;
306 if (fcntl(ssock, F_SETFL, fdflags) < 0)
307 syslog(LOG_WARNING, "unable to set server socket nonblocking flags! %s \n",
311 if (fail_this_transaction == 0) {
312 Hdr.http_sock = ssock;
314 /* Perform an HTTP transaction... */
317 /* Shut down SSL/TLS if required... */
324 /* ...and close the socket. */
325 if (Hdr.http_sock > 0) {
326 lingering_close(ssock);
328 http_detach_modules(&Hdr);
334 } while (!time_to_die);
336 http_destroy_modules(&Hdr);
337 syslog(LOG_DEBUG, "Thread exiting.\n");
343 * Shut us down the regular way.
344 * signum is the signal we want to forward
347 void graceful_shutdown_watcher(int signum) {
348 syslog(LOG_INFO, "Watcher thread exiting.\n");
349 write(ExitPipe[0], HKEY(" "));
350 kill(current_child, signum);
351 if (signum != SIGHUP)
357 * Shut us down the regular way.
358 * signum is the signal we want to forward
361 void graceful_shutdown(int signum) {
365 syslog(LOG_INFO, "WebCit is being shut down on signal %d.\n", signum);
373 write(ExitPipe[0], HKEY(" "));
378 * Start running as a daemon.
380 void start_daemon(char *pid_file)
389 /* Close stdin/stdout/stderr and replace them with /dev/null.
390 * We don't just call close() because we don't want these fd's
391 * to be reused for other files.
395 signal(SIGHUP, SIG_IGN);
396 signal(SIGINT, SIG_IGN);
397 signal(SIGQUIT, SIG_IGN);
406 freopen("/dev/null", "r", stdin);
407 freopen("/dev/null", "w", stdout);
408 freopen("/dev/null", "w", stderr);
409 signal(SIGTERM, graceful_shutdown_watcher);
410 signal(SIGHUP, graceful_shutdown_watcher);
413 current_child = fork();
416 if (current_child < 0) {
418 ShutDownLibCitadel ();
422 else if (current_child == 0) { /* child process */
423 signal(SIGHUP, graceful_shutdown);
425 return; /* continue starting webcit. */
427 else { /* watcher process */
429 fp = fopen(pid_file, "w");
431 fprintf(fp, "%d\n", getpid());
435 waitpid(current_child, &status, 0);
440 /* Did the main process exit with an actual exit code? */
441 if (WIFEXITED(status)) {
443 /* Exit code 0 means the watcher should exit */
444 if (WEXITSTATUS(status) == 0) {
448 /* Exit code 101-109 means the watcher should exit */
449 else if ( (WEXITSTATUS(status) >= 101) && (WEXITSTATUS(status) <= 109) ) {
453 /* Any other exit code means we should restart. */
459 /* Any other type of termination (signals, etc.) should also restart. */
464 } while (do_restart);
469 ShutDownLibCitadel ();
470 exit(WEXITSTATUS(status));
475 * Spawn an additional worker thread into the pool.
477 void spawn_another_worker_thread()
479 pthread_t SessThread; /* Thread descriptor */
480 pthread_attr_t attr; /* Thread attributes */
483 ++num_threads_existing;
484 ++num_threads_executing;
486 /* set attributes for the new thread */
487 pthread_attr_init(&attr);
488 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
491 * Our per-thread stacks need to be bigger than the default size,
492 * otherwise the MIME parser crashes on FreeBSD.
494 if ((ret = pthread_attr_setstacksize(&attr, 1024 * 1024))) {
495 syslog(LOG_WARNING, "pthread_attr_setstacksize: %s\n", strerror(ret));
496 pthread_attr_destroy(&attr);
499 /* now create the thread */
500 if (pthread_create(&SessThread, &attr, (void *(*)(void *)) worker_entry, NULL) != 0) {
501 syslog(LOG_WARNING, "Can't create thread: %s\n", strerror(errno));
504 /* free up the attributes */
505 pthread_attr_destroy(&attr);
510 webcit_calc_dirs_n_files(int relh, const char *basedir, int home, char *webcitdir, char *relhome)
512 char dirbuffer[PATH_MAX]="";
513 /* calculate all our path on a central place */
514 /* where to keep our config */
516 #define COMPUTE_DIRECTORY(SUBDIR) memcpy(dirbuffer,SUBDIR, sizeof dirbuffer);\
517 snprintf(SUBDIR,sizeof SUBDIR, "%s%s%s%s%s%s%s", \
518 (home&!relh)?webcitdir:basedir, \
519 ((basedir!=webcitdir)&(home&!relh))?basedir:"/", \
520 ((basedir!=webcitdir)&(home&!relh))?"/":"", \
522 (relhome[0]!='\0')?"/":"",\
524 (dirbuffer[0]!='\0')?"/":"");
526 COMPUTE_DIRECTORY(socket_dir);
527 basedir=WWWDIR "/static";
528 COMPUTE_DIRECTORY(static_dir);
529 basedir=WWWDIR "/static/icons";
530 COMPUTE_DIRECTORY(static_icon_dir);
531 basedir=WWWDIR "/static.local";
532 COMPUTE_DIRECTORY(static_local_dir);
533 StripSlashes(static_dir, 1);
534 StripSlashes(static_icon_dir, 1);
535 StripSlashes(static_local_dir, 1);
537 snprintf(file_crpt_file_key,
538 sizeof file_crpt_file_key,
541 snprintf(file_crpt_file_csr,
542 sizeof file_crpt_file_csr,
545 snprintf(file_crpt_file_cer,
546 sizeof file_crpt_file_cer,
552 COMPUTE_DIRECTORY(etc_dir);
553 StripSlashes(etc_dir, 1);
554 snprintf(file_etc_mimelist,
555 sizeof file_etc_mimelist,
556 "%s/nogz-mimetypes.txt",
559 /* we should go somewhere we can leave our coredump, if enabled... */
560 syslog(LOG_INFO, "Changing directory to %s\n", socket_dir);
561 if (chdir(webcitdir) != 0) {
566 void drop_root(uid_t UID)
568 struct passwd pw, *pwp = NULL;
571 * Now that we've bound the sockets, change to the Citadel user id and its
572 * corresponding group ids
576 #ifdef HAVE_GETPWUID_R
577 #ifdef SOLARIS_GETPWUID
578 pwp = getpwuid_r(UID, &pw, pwbuf, sizeof(pwbuf));
579 #else /* SOLARIS_GETPWUID */
580 getpwuid_r(UID, &pw, pwbuf, sizeof(pwbuf), &pwp);
581 #endif /* SOLARIS_GETPWUID */
582 #else /* HAVE_GETPWUID_R */
584 #endif /* HAVE_GETPWUID_R */
587 syslog(LOG_CRIT, "WARNING: getpwuid(%d): %s\n"
588 "Group IDs will be incorrect.\n", UID,
591 initgroups(pw.pw_name, pw.pw_gid);
592 if (setgid(pw.pw_gid))
593 syslog(LOG_CRIT, "setgid(%ld): %s\n", (long)pw.pw_gid,
596 syslog(LOG_INFO, "Changing uid to %ld\n", (long)UID);
597 if (setuid(UID) != 0) {
598 syslog(LOG_CRIT, "setuid() failed: %s\n", strerror(errno));
600 #if defined (HAVE_SYS_PRCTL_H) && defined (PR_SET_DUMPABLE)
601 prctl(PR_SET_DUMPABLE, 1);
608 * print the actual stack frame.
610 void wc_backtrace(long LogLevel)
612 #ifdef HAVE_BACKTRACE
613 void *stack_frames[50];
618 size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
619 strings = backtrace_symbols(stack_frames, size);
620 for (i = 0; i < size; i++) {
622 syslog(LogLevel, "%s\n", strings[i]);
624 syslog(LogLevel, "%p\n", stack_frames[i]);