X-Git-Url: https://code.citadel.org/?p=citadel.git;a=blobdiff_plain;f=webcit%2Fsysdep.c;fp=webcit%2Fsysdep.c;h=7c699699afb83939feddd2410f390111ff82ece5;hp=0000000000000000000000000000000000000000;hb=8611acef7ff4213c28ad20ff755d34509870bc96;hpb=76a0f8571de023fe6bd20390174a2302e0a0b982 diff --git a/webcit/sysdep.c b/webcit/sysdep.c new file mode 100644 index 000000000..7c699699a --- /dev/null +++ b/webcit/sysdep.c @@ -0,0 +1,594 @@ +/* + * WebCit "system dependent" code. + * + * Copyright (c) 1996-2021 by the citadel.org team + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_PTHREAD_H +#include +#endif +#include "webcit.h" +#include "sysdep.h" + +#ifdef HAVE_SYS_SELECT_H +#include +#endif + +#include "webserver.h" +#include "modules_init.h" +#if HAVE_BACKTRACE +#include +#endif + +pthread_mutex_t Critters[MAX_SEMAPHORES]; /* Things needing locking */ +pthread_key_t MyConKey; /* TSD key for MyContext() */ +pthread_key_t MyReq; /* TSD key for MyReq() */ +int msock; /* master listening socket */ +int time_to_die = 0; /* Nonzero if server is shutting down */ + +extern void *context_loop(ParsedHttpHdrs *Hdr); +extern void *housekeeping_loop(void); +extern void do_housekeeping(void); + +char file_etc_mimelist[PATH_MAX]=""; + +char etc_dir[PATH_MAX]; +char static_dir[PATH_MAX]; /* calculated on startup */ +char static_local_dir[PATH_MAX]; /* calculated on startup */ +char static_icon_dir[PATH_MAX]; /* where should we find our mime icons? */ +char *static_dirs[]={ /* needs same sort order as the web mapping */ + "./static", + "./static.local", + "./tiny_mce", + "./static/webcit_icons" +}; + +int ExitPipe[2]; +HashList *GZMimeBlackList = NULL; /* mimetypes which shouldn't be gzip compressed */ + +void LoadMimeBlacklist(void) +{ + StrBuf *MimeBlackLine; + IOBuffer IOB; + eReadState state; + + memset(&IOB, 0, sizeof(IOBuffer)); + IOB.fd = open(file_etc_mimelist, O_RDONLY); + + IOB.Buf = NewStrBuf(); + MimeBlackLine = NewStrBuf(); + GZMimeBlackList = NewHash(1, NULL); + + do + { + state = StrBufChunkSipLine(MimeBlackLine, &IOB); + + switch (state) + { + case eMustReadMore: + if (StrBuf_read_one_chunk_callback(IOB.fd, 0, &IOB) <= 0) + state = eReadFail; + break; + case eReadSuccess: + if ((StrLength(MimeBlackLine) > 1) && + (*ChrPtr(MimeBlackLine) != '#')) + { + Put(GZMimeBlackList, SKEY(MimeBlackLine), + (void*) 1, reference_free_handler); + } + FlushStrBuf(MimeBlackLine); + break; + case eReadFail: + break; + case eBufferNotEmpty: + break; + } + } + while (state != eReadFail); + + close(IOB.fd); + + FreeStrBuf(&IOB.Buf); + FreeStrBuf(&MimeBlackLine); +} + +void CheckGZipCompressionAllowed(const char *MimeType, long MLen) +{ + void *v; + + if (WC->Hdr->HR.gzip_ok) + WC->Hdr->HR.gzip_ok = GetHash(GZMimeBlackList, MimeType, MLen, &v) == 0; +} + +void InitialiseSemaphores(void) +{ + int i; + + /* Set up a bunch of semaphores to be used for critical sections */ + for (i=0; i 0) && (ssock < 0) && (time_to_die == 0)); + + if ((msock == -1)||(time_to_die)) + {/* ok, we're going down. */ + int shutdown = 0; + + /* The first thread to get here will have to do the cleanup. + * Make sure it's really just one. + */ + begin_critical_section(S_SHUTDOWN); + if (msock == -1) + { + msock = -2; + shutdown = 1; + } + end_critical_section(S_SHUTDOWN); + if (shutdown == 1) + {/* we're the one to cleanup the mess. */ + http_destroy_modules(&Hdr); + syslog(LOG_DEBUG, "I'm master shutdown: tagging sessions to be killed.\n"); + shutdown_sessions(); + syslog(LOG_DEBUG, "master shutdown: waiting for others\n"); + sleeeeeeeeeep(1); /* wait so some others might finish... */ + syslog(LOG_DEBUG, "master shutdown: cleaning up sessions\n"); + do_housekeeping(); + syslog(LOG_DEBUG, "master shutdown: cleaning up libical\n"); + + ShutDownWebcit(); + + syslog(LOG_DEBUG, "master shutdown exiting.\n"); + exit(0); + } + break; + } + if (ssock < 0 ) continue; + + check_thread_pool_size(); + + /* Now do something. */ + if (msock < 0) { + if (ssock > 0) close (ssock); + syslog(LOG_DEBUG, "in between."); + pthread_exit(NULL); + } else { + /* Got it? do some real work! */ + /* Set the SO_REUSEADDR socket option */ + i = 1; + setsockopt(ssock, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i)); + + /* If we are an HTTPS server, go crypto now. */ +#ifdef HAVE_OPENSSL + if (is_https) { + if (starttls(ssock) != 0) { + fail_this_transaction = 1; + close(ssock); + } + } + else +#endif + { + int fdflags; + fdflags = fcntl(ssock, F_GETFL); + if (fdflags < 0) + syslog(LOG_WARNING, "unable to get server socket flags! %s \n", + strerror(errno)); + fdflags = fdflags | O_NONBLOCK; + if (fcntl(ssock, F_SETFL, fdflags) < 0) + syslog(LOG_WARNING, "unable to set server socket nonblocking flags! %s \n", + strerror(errno)); + } + + if (fail_this_transaction == 0) { + Hdr.http_sock = ssock; + + /* Perform an HTTP transaction... */ + context_loop(&Hdr); + + /* Shut down SSL/TLS if required... */ +#ifdef HAVE_OPENSSL + if (is_https) { + endtls(); + } +#endif + + /* ...and close the socket. */ + if (Hdr.http_sock > 0) { + lingering_close(ssock); + } + http_detach_modules(&Hdr); + + } + + } + + } while (!time_to_die); + + http_destroy_modules(&Hdr); + syslog(LOG_DEBUG, "Thread exiting.\n"); + pthread_exit(NULL); +} + + +/* + * Shut us down the regular way. + * signum is the signal we want to forward + */ +pid_t current_child; +void graceful_shutdown_watcher(int signum) { + syslog(LOG_INFO, "Watcher thread exiting.\n"); + write(ExitPipe[0], HKEY(" ")); + kill(current_child, signum); + if (signum != SIGHUP) + exit(0); +} + + +/* + * Shut us down the regular way. + * signum is the signal we want to forward + */ +pid_t current_child; +void graceful_shutdown(int signum) { + FILE *FD; + int fd; + + syslog(LOG_INFO, "WebCit is being shut down on signal %d.\n", signum); + fd = msock; + msock = -1; + time_to_die = 1; + FD=fdopen(fd, "a+"); + fflush (FD); + fclose (FD); + close(fd); + write(ExitPipe[0], HKEY(" ")); +} + + +/* + * Start running as a daemon. + */ +void start_daemon(char *pid_file) +{ + int status = 0; + pid_t child = 0; + FILE *fp; + int do_restart = 0; + + current_child = 0; + + /* Close stdin/stdout/stderr and replace them with /dev/null. + * We don't just call close() because we don't want these fd's + * to be reused for other files. + */ + chdir("/"); + + signal(SIGHUP, SIG_IGN); + signal(SIGINT, SIG_IGN); + signal(SIGQUIT, SIG_IGN); + + child = fork(); + if (child != 0) { + exit(0); + } + + setsid(); + umask(0); + freopen("/dev/null", "r", stdin); + freopen("/dev/null", "w", stdout); + freopen("/dev/null", "w", stderr); + signal(SIGTERM, graceful_shutdown_watcher); + signal(SIGHUP, graceful_shutdown_watcher); + + do { + current_child = fork(); + + + if (current_child < 0) { + perror("fork"); + ShutDownLibCitadel (); + exit(errno); + } + + else if (current_child == 0) { /* child process */ + signal(SIGHUP, graceful_shutdown); + + return; /* continue starting webcit. */ + } + else { /* watcher process */ + if (pid_file) { + fp = fopen(pid_file, "w"); + if (fp != NULL) { + fprintf(fp, "%d\n", getpid()); + fclose(fp); + } + } + waitpid(current_child, &status, 0); + } + + do_restart = 0; + + /* Did the main process exit with an actual exit code? */ + if (WIFEXITED(status)) { + + /* Exit code 0 means the watcher should exit */ + if (WEXITSTATUS(status) == 0) { + do_restart = 0; + } + + /* Exit code 101-109 means the watcher should exit */ + else if ( (WEXITSTATUS(status) >= 101) && (WEXITSTATUS(status) <= 109) ) { + do_restart = 0; + } + + /* Any other exit code means we should restart. */ + else { + do_restart = 1; + } + } + + /* Any other type of termination (signals, etc.) should also restart. */ + else { + do_restart = 1; + } + + } while (do_restart); + + if (pid_file) { + unlink(pid_file); + } + ShutDownLibCitadel (); + exit(WEXITSTATUS(status)); +} + + +/* + * Spawn an additional worker thread into the pool. + */ +void spawn_another_worker_thread() +{ + pthread_t SessThread; /* Thread descriptor */ + pthread_attr_t attr; /* Thread attributes */ + int ret; + + ++num_threads_existing; + ++num_threads_executing; + + /* set attributes for the new thread */ + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + + /* + * Our per-thread stacks need to be bigger than the default size, + * otherwise the MIME parser crashes on FreeBSD. + */ + if ((ret = pthread_attr_setstacksize(&attr, 1024 * 1024))) { + syslog(LOG_WARNING, "pthread_attr_setstacksize: %s\n", strerror(ret)); + pthread_attr_destroy(&attr); + } + + /* now create the thread */ + if (pthread_create(&SessThread, &attr, (void *(*)(void *)) worker_entry, NULL) != 0) { + syslog(LOG_WARNING, "Can't create thread: %s\n", strerror(errno)); + } + + /* free up the attributes */ + pthread_attr_destroy(&attr); +} + + +void +webcit_calc_dirs_n_files(int relh, const char *basedir, int home, char *webcitdir, char *relhome) +{ + char dirbuffer[PATH_MAX]=""; + /* calculate all our path on a central place */ + /* where to keep our config */ + +#define COMPUTE_DIRECTORY(SUBDIR) memcpy(dirbuffer,SUBDIR, sizeof dirbuffer);\ + snprintf(SUBDIR,sizeof SUBDIR, "%s%s%s%s%s%s%s", \ + (home&!relh)?webcitdir:basedir, \ + ((basedir!=webcitdir)&(home&!relh))?basedir:"/", \ + ((basedir!=webcitdir)&(home&!relh))?"/":"", \ + relhome, \ + (relhome[0]!='\0')?"/":"",\ + dirbuffer,\ + (dirbuffer[0]!='\0')?"/":""); + basedir=RUNDIR; + COMPUTE_DIRECTORY(socket_dir); + basedir=WWWDIR "/static"; + COMPUTE_DIRECTORY(static_dir); + basedir=WWWDIR "/static/icons"; + COMPUTE_DIRECTORY(static_icon_dir); + basedir=WWWDIR "/static.local"; + COMPUTE_DIRECTORY(static_local_dir); + StripSlashes(static_dir, 1); + StripSlashes(static_icon_dir, 1); + StripSlashes(static_local_dir, 1); + + basedir=ETCDIR; + COMPUTE_DIRECTORY(etc_dir); + StripSlashes(etc_dir, 1); + snprintf(file_etc_mimelist, + sizeof file_etc_mimelist, + "%s/nogz-mimetypes.txt", + etc_dir); + + /* we should go somewhere we can leave our coredump, if enabled... */ + syslog(LOG_INFO, "Changing directory to %s\n", socket_dir); + if (chdir(webcitdir) != 0) { + perror("chdir"); + } +} + +void drop_root(uid_t UID) +{ + struct passwd pw, *pwp = NULL; +#ifdef HAVE_GETPWUID_R + char pwbuf[SIZ]; +#endif + + /* + * Now that we've bound the sockets, change to the Citadel user id and its + * corresponding group ids + */ + if (UID != -1) { + +#ifdef HAVE_GETPWUID_R +#ifdef SOLARIS_GETPWUID + pwp = getpwuid_r(UID, &pw, pwbuf, sizeof(pwbuf)); +#else /* SOLARIS_GETPWUID */ + getpwuid_r(UID, &pw, pwbuf, sizeof(pwbuf), &pwp); +#endif /* SOLARIS_GETPWUID */ +#else /* HAVE_GETPWUID_R */ + pwp = NULL; +#endif /* HAVE_GETPWUID_R */ + + if (pwp == NULL) + syslog(LOG_CRIT, "WARNING: getpwuid(%d): %s\n" + "Group IDs will be incorrect.\n", UID, + strerror(errno)); + else { + initgroups(pw.pw_name, pw.pw_gid); + if (setgid(pw.pw_gid)) + syslog(LOG_CRIT, "setgid(%ld): %s\n", (long)pw.pw_gid, + strerror(errno)); + } + syslog(LOG_INFO, "Changing uid to %ld\n", (long)UID); + if (setuid(UID) != 0) { + syslog(LOG_CRIT, "setuid() failed: %s\n", strerror(errno)); + } +#if defined (HAVE_SYS_PRCTL_H) && defined (PR_SET_DUMPABLE) + prctl(PR_SET_DUMPABLE, 1); +#endif + } +} + + +/* + * print the actual stack frame. + */ +void wc_backtrace(long LogLevel) +{ +#ifdef HAVE_BACKTRACE + void *stack_frames[50]; + size_t size, i; + char **strings; + + + size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*)); + strings = backtrace_symbols(stack_frames, size); + for (i = 0; i < size; i++) { + if (strings != NULL) + syslog(LogLevel, "%s\n", strings[i]); + else + syslog(LogLevel, "%p\n", stack_frames[i]); + } + free(strings); +#endif +}