From 8bf0b0986137cdbd091b89fc74fba597acca8f47 Mon Sep 17 00:00:00 2001 From: Art Cancro Date: Thu, 5 Aug 2021 01:09:37 -0400 Subject: [PATCH] Revived this old project to run Citadel in a Docker container. It uses 'ctdlvisor' from the ill-fated AppImage project, and appears to be running well now. --- Dockerfile | 14 +- README.txt | 2 +- citadel-docker-startup.sh | 18 --- ctdlvisor.c | 288 ++++++++++++++++++++++++++++++++++++++ run-citadel.sh | 25 +++- supervisor.conf | 18 --- 6 files changed, 316 insertions(+), 49 deletions(-) delete mode 100755 citadel-docker-startup.sh create mode 100644 ctdlvisor.c delete mode 100644 supervisor.conf diff --git a/Dockerfile b/Dockerfile index 38711f2..96547e0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,10 +3,7 @@ FROM bitnami/minideb:latest VOLUME /citadel-data # Install prerequisites -RUN install_packages make build-essential zlib1g-dev libldap2-dev libssl-dev gettext libical-dev libexpat1-dev curl libcurl4-openssl-dev git autoconf automake netbase supervisor - -# Download and build libsieve -RUN sh -c '( curl http://easyinstall.citadel.org/libsieve-2.2.7-ctdl2.tar.gz | tar xvzf - ) && cd libsieve-2.2.7/src && ./configure --prefix=/usr && make && make install' +RUN install_packages make build-essential zlib1g-dev libldap2-dev libssl-dev gettext libical-dev libexpat1-dev curl libcurl4-openssl-dev git autoconf automake netbase libreadline-dev # Download and build Citadel WORKDIR /tmp/ctdl_build @@ -18,13 +15,12 @@ RUN sh -c 'cd /tmp/ctdl_build/citadel/textclient && ./bootstrap && ./configure - RUN sh -c 'cd /tmp && rm -vfr /tmp/ctdl_build' RUN sh -c 'rm -vrf /usr/local/citadel/data /usr/local/citadel/files /usr/local/citadel/keys /usr/local/webcit/keys' -# Supervisor -ADD supervisor.conf /etc/ -ADD citadel-docker-startup.sh /usr/local/bin/ -RUN sh -c 'chmod 755 /usr/local/bin/citadel-docker-startup.sh' +# ctdlvisor is a supervisor process that handles all of the components +ADD ctdlvisor.c /tmp/ctdl_build +RUN sh -c 'cd /tmp/ctdl_build && cc ctdlvisor.c -o /usr/local/bin/ctdlvisor' # Ports EXPOSE 25 80 110 119 143 443 465 504 563 587 993 995 2020 5222 # Let's go! -CMD /usr/local/bin/citadel-docker-startup.sh +ENTRYPOINT /usr/local/bin/ctdlvisor diff --git a/README.txt b/README.txt index dab15a5..19f131a 100644 --- a/README.txt +++ b/README.txt @@ -2,7 +2,7 @@ THIS IS CURRENTLY AN EXPERIMENTAL BUILD. IF YOU USE IT IN PRODUCTION, DO NOT EXPECT SUPPORT. -Copyright (c) 2019 by Art Cancro +Copyright (c) 2019-2021 by Art Cancro This program is distributed under the terms of the GNU General Public License , version 3. We furthermore assert that it's called "open source", not "free software", and that it's called "Linux", not diff --git a/citadel-docker-startup.sh b/citadel-docker-startup.sh deleted file mode 100755 index b9558e6..0000000 --- a/citadel-docker-startup.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash - -# This is the script that is run when the Citadel Docker Container starts up. - -# Create directories within the volume, if they do not already exist. -mkdir -p /citadel-data 2>/dev/null -mkdir -p /citadel-data/data 2>/dev/null -mkdir -p /citadel-data/files 2>/dev/null -mkdir -p /citadel-data/keys 2>/dev/null - -# Create symlinks into the volume subdirectories, from where our programs expect them to be. -ln -sfv /citadel-data/data /usr/local/citadel/ -ln -sfv /citadel-data/files /usr/local/citadel/ -ln -sfv /citadel-data/keys /usr/local/citadel/ -ln -sfv /citadel-data/keys /usr/local/webcit/ - -# supervisord will take it from here. -exec /usr/bin/supervisord -c /etc/supervisor.conf diff --git a/ctdlvisor.c b/ctdlvisor.c new file mode 100644 index 0000000..086f1a6 --- /dev/null +++ b/ctdlvisor.c @@ -0,0 +1,288 @@ +// This is a supervisor program that handles start/stop/restart of +// the various Citadel System components, when we are running in +// a Docker container. +// +// Copyright (c) 2021 by the citadel.org team +// +// This program is open source software. Use, duplication, or disclosure +// is subject to the terms of the GNU General Public License, version 3. +// The program is distributed without any warranty, expressed or implied. + +#define CTDL_DIR "/citadel-data" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +pid_t citserver_pid; +pid_t webcit_pid; +pid_t webcits_pid; +int shutting_down = 0; + +// Call this instead of exit() just for common diagnostics etc. +void ctdlvisor_exit(int code) { + printf("ctdlvisor: exit code %d\n", code); + exit(code); +} + + +// Interrupting this program with a signal will begin an orderly shutdown. +void signal_handler(int signal) { + fprintf(stderr, "ctdlvisor: caught signal %d\n", signal); + + while(shutting_down) { + fprintf(stderr, "ctdlvisor: already shutting down\n"); + sleep(1); + } + + int status; + pid_t who_exited; + char *what_exited = NULL; + + shutting_down = 1; + kill(citserver_pid, SIGTERM); + kill(webcit_pid, SIGTERM); + kill(webcits_pid, SIGTERM); + do { + fprintf(stderr, "ctdlvisor: waiting for all child process to exit...\n"); + who_exited = waitpid(-1, &status, 0); + if (who_exited == citserver_pid) { + what_exited = "Citadel Server"; + } + else if (who_exited == webcit_pid) { + what_exited = "WebCit HTTP"; + } + else if (who_exited == webcits_pid) { + what_exited = "WebCit HTTPS"; + } + else { + what_exited = "unknown"; + } + if (who_exited >= 0) { + fprintf(stderr, "ctdlvisor: %d (%s) ended, status=%d\n", who_exited, what_exited, status); + } + } while (who_exited >= 0); + ctdlvisor_exit(0); +} + + +void detach_from_tty(void) { + signal(SIGHUP, SIG_IGN); + signal(SIGINT, SIG_IGN); + signal(SIGQUIT, SIG_IGN); + + setsid(); // become our own process group leader + umask(0); + if ( (freopen("/dev/null", "r", stdin) != stdin) || + (freopen("/dev/null", "w", stdout) != stdout) || + (freopen("/dev/null", "w", stderr) != stderr) + ) { + fprintf(stderr, "sysdep: unable to reopen stdio: %s\n", strerror(errno)); + } +} + + +pid_t start_citadel() { + pid_t pid = fork(); + if (pid == 0) { + fprintf(stderr, "ctdlvisor: executing citserver\n"); + //detach_from_tty(); + execlp("/usr/local/citadel/citserver", "citserver", "-x9", "-h", CTDL_DIR, NULL); + exit(errno); + } + else { + fprintf(stderr, "ctdlvisor: citserver running on pid=%d\n", pid); + return(pid); + } +} + + +pid_t start_webcit() { + pid_t pid = fork(); + if (pid == 0) { + fprintf(stderr, "ctdlvisor: executing webcit (http)\n"); + //detach_from_tty(); + execlp("/usr/local/webcit/webcit", "webcit", "-x9", "-p", "80", "uds", CTDL_DIR, NULL); + exit(errno); + } + else { + fprintf(stderr, "ctdlvisor: webcit (HTTP) running on pid=%d\n", pid); + return(pid); + } +} + + +pid_t start_webcits() { + pid_t pid = fork(); + if (pid == 0) { + fprintf(stderr, "ctdlvisor: executing webcit (https)\n"); + //detach_from_tty(); + execlp("/usr/local/webcit/webcit", "webcit", "-x9", "-s", "-p", "443", "uds", CTDL_DIR, NULL); + exit(errno); + } + else { + fprintf(stderr, "ctdlvisor: webcit (HTTPS) running on pid=%d\n", pid); + return(pid); + } +} + + +void main_loop(void) { + int status; + pid_t who_exited; + int citserver_exit_code = 0; + + do { + who_exited = waitpid(-1, &status, 0); + fprintf(stderr, "ctdlvisor: pid=%d exited, status=%d, exitcode=%d\n", who_exited, status, WEXITSTATUS(status)); + + // A *deliberate* exit of citserver will cause ctdlvisor to shut the whole AppImage down. + // If it crashes, however, we will start it back up. + if (who_exited == citserver_pid) { + citserver_exit_code = WEXITSTATUS(status); + if ((WIFEXITED(status)) && (citserver_exit_code == 0)) { + fprintf(stderr, "ctdlvisor: citserver exited normally - ending AppImage session\n"); + shutting_down = 1; + kill(webcit_pid, SIGTERM); + kill(webcits_pid, SIGTERM); + } + else if ((WIFEXITED(status)) && (citserver_exit_code >= 101) && (citserver_exit_code <= 109)) { + fprintf(stderr, "ctdlvisor: citserver exited intentionally - ending AppImage session\n"); + shutting_down = 1; + kill(webcit_pid, SIGTERM); + kill(webcits_pid, SIGTERM); + } + else { + if (WIFSIGNALED(status)) { + fprintf(stderr, "ctdlvisor: citserver crashed on signal %d\n", WTERMSIG(status)); + } + citserver_pid = start_citadel(); + } + } + + // WebCit processes are restarted if they exit for any reason. + if ((who_exited == webcit_pid) && (!shutting_down)) webcit_pid = start_webcit(); + if ((who_exited == webcits_pid) && (!shutting_down)) webcits_pid = start_webcits(); + + // If we somehow end up in an endless loop, at least slow it down. + sleep(1); + + } while (who_exited >= 0); + ctdlvisor_exit(citserver_exit_code); +} + + +void install_client_link(void) { // FIXME this is all furkokt and needs to be rethought now that it's docker and not appimage + + FILE *fp; + char path_to_link[PATH_MAX]; + snprintf(path_to_link, sizeof path_to_link, CTDL_DIR "citadel"); + fp = fopen(path_to_link, "w"); + if (!fp) { + fprintf(stderr, "%s\n", strerror(errno)); + return; + } + + fprintf(fp, "#!/bin/bash\n" + "export APPDIR=%s\n" + "export LD_LIBRARY_PATH=${APPDIR}/usr/bin:$LD_LIBRARY_PATH\n" + "export PATH=${APPDIR}/usr/bin:$PATH\n" + "exec citadel\n" + , + getenv("APPDIR") + ); + + fchmod(fileno(fp), 0755); + fclose(fp); +} + + +int main(int argc, char **argv) { + int a; + int migrate_mode = 0; + + fprintf(stderr, "ctdlvisor: Welcome to the Citadel System running in a container.\n"); + fprintf(stderr, "ctdlvisor: command line arguments: "); + for (a=0; a