From 153c59307dcddcbf1f0f643163207348971e6a8b Mon Sep 17 00:00:00 2001 From: Tiago Jacobs <tiago.jacobs@gmail.com> Date: Fri, 24 Jul 2020 02:42:51 -0300 Subject: [PATCH] Improvements on bbb-libreoffice --- .../PresentationUrlDownloadService.java | 1 - .../imp/OfficeToPdfConversionService.java | 35 ++++++++++++++++--- .../assets/bbb-libreoffice.service | 6 ++-- .../assets/libreoffice_container.sh | 18 ++++++---- bbb-libreoffice/docker/Dockerfile | 11 +++--- bbb-libreoffice/install.sh | 11 +++--- bbb-libreoffice/uninstall.sh | 14 ++++---- .../grails-app/conf/bigbluebutton.properties | 16 +++++++++ .../grails-app/conf/spring/doc-conversion.xml | 3 ++ .../web/controllers/ApiController.groovy | 4 +++ bigbluebutton-web/run.sh | 12 +++++++ 11 files changed, 102 insertions(+), 29 deletions(-) diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/presentation/PresentationUrlDownloadService.java b/bbb-common-web/src/main/java/org/bigbluebutton/presentation/PresentationUrlDownloadService.java index ecbfac0d8f..6d446c3201 100755 --- a/bbb-common-web/src/main/java/org/bigbluebutton/presentation/PresentationUrlDownloadService.java +++ b/bbb-common-web/src/main/java/org/bigbluebutton/presentation/PresentationUrlDownloadService.java @@ -13,7 +13,6 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; -import com.sun.org.apache.xpath.internal.operations.Bool; import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.io.FileUtils; import org.apache.commons.io.FilenameUtils; diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/presentation/imp/OfficeToPdfConversionService.java b/bbb-common-web/src/main/java/org/bigbluebutton/presentation/imp/OfficeToPdfConversionService.java index fb24ce2b2b..4329308ed7 100755 --- a/bbb-common-web/src/main/java/org/bigbluebutton/presentation/imp/OfficeToPdfConversionService.java +++ b/bbb-common-web/src/main/java/org/bigbluebutton/presentation/imp/OfficeToPdfConversionService.java @@ -44,6 +44,9 @@ public class OfficeToPdfConversionService { private final ArrayList<ExternalOfficeManager> officeManagers; private ExternalOfficeManager currentManager = null; private boolean skipOfficePrecheck = false; + private int sofficeBasePort = 0; + private int sofficeManagers = 0; + private String sofficeWorkingDirBase = null; public OfficeToPdfConversionService() throws OfficeException { officeManagers = new ArrayList<>(); @@ -163,17 +166,36 @@ public class OfficeToPdfConversionService { this.skipOfficePrecheck = skipOfficePrecheck; } + public void setSofficeBasePort(int sofficeBasePort) { + this.sofficeBasePort = sofficeBasePort; + } + + public void setSofficeManagers(int sofficeServiceManagers) { + this.sofficeManagers = sofficeServiceManagers; + } + + public void setSofficeWorkingDirBase(String sofficeWorkingDirBase) { + this.sofficeWorkingDirBase = sofficeWorkingDirBase; + } + public void start() { - for(int managerIndex = 0; managerIndex < 4; managerIndex ++) { + log.info("Starting LibreOffice pool with " + sofficeManagers + " managers, starting from port " + sofficeBasePort); + + for(int managerIndex = 0; managerIndex < sofficeManagers; managerIndex ++) { Integer instanceNumber = managerIndex + 1; // starts at 1 try { - final File workingDir = new File("/var/tmp/soffice_0" +instanceNumber); + final File workingDir = new File(sofficeWorkingDirBase + String.format("%02d", instanceNumber)); + + if(!workingDir.exists()) { + workingDir.mkdir(); + } + ExternalOfficeManager officeManager = ExternalOfficeManager .builder() .connectTimeout(2000L) .retryInterval(500L) - .portNumber(8200 + instanceNumber) + .portNumber(sofficeBasePort + managerIndex) .connectOnStart(false) // If it's true and soffice is not available, exception is thrown here ( we don't want exception here - we want the manager alive trying to reconnect ) .workingDir(workingDir) .build(); @@ -189,9 +211,14 @@ public class OfficeToPdfConversionService { } catch (Exception e) { log.error("Could not start Office Manager " + instanceNumber + ". Details: " + e.getMessage()); } + } - currentManager = officeManagers.get(0); + if (officeManagers.size() == 0) { + log.error("No office managers could be started"); + return; } + + currentManager = officeManagers.get(0); } public void stop() { diff --git a/bbb-libreoffice/assets/bbb-libreoffice.service b/bbb-libreoffice/assets/bbb-libreoffice.service index c4eb323257..a61bdc6473 100644 --- a/bbb-libreoffice/assets/bbb-libreoffice.service +++ b/bbb-libreoffice/assets/bbb-libreoffice.service @@ -1,12 +1,12 @@ [Unit] -Description=BigBlueButton Libre Office container +Description=BigBlueButton Libre Office container %i Requires=network.target [Service] Type=simple WorkingDirectory=/tmp -ExecStart=/usr/share/bbb-libreoffice/libreoffice_container.sh INSTANCE_NUMBER -ExecStop=/usr/bin/docker kill bbb-libreoffice-INSTANCE_NUMBER +ExecStart=/usr/share/bbb-libreoffice/libreoffice_container.sh %i +ExecStop=/usr/bin/docker kill bbb-libreoffice-%i Restart=always RestartSec=60 SuccessExitStatus= diff --git a/bbb-libreoffice/assets/libreoffice_container.sh b/bbb-libreoffice/assets/libreoffice_container.sh index d82cd6aa6c..941ebe1413 100755 --- a/bbb-libreoffice/assets/libreoffice_container.sh +++ b/bbb-libreoffice/assets/libreoffice_container.sh @@ -1,4 +1,6 @@ #!/bin/bash +set -e + INSTANCE_NUMBER=$1 if [ -z "$INSTANCE_NUMBER" ]; then @@ -7,22 +9,26 @@ fi; _kill() { CHECK_CONTAINER=`docker inspect bbb-libreoffice-${INSTANCE_NUMBER} &> /dev/null && echo 1 || echo 0` - if [ "$CHECK_CONTAINER" = "1" ]; then + if [ "$CHECK_CONTAINER" = "1" ]; then echo "Killing container" - docker kill bbb-libreoffice-${INSTANCE_NUMBER}; - sleep 1 - fi; + docker kill bbb-libreoffice-${INSTANCE_NUMBER}; + sleep 1 + fi; } trap _kill SIGINT -if (($INSTANCE_NUMBER >= 1 && $INSTANCE_NUMBER <= 10)); then +if (($INSTANCE_NUMBER >= 1)); then PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" _kill - docker run --name bbb-libreoffice-${INSTANCE_NUMBER} -p 82${INSTANCE_NUMBER}:8000 -v/var/tmp/soffice${INSTANCE_NUMBER}:/var/tmp/soffice${INSTANCE_NUMBER} --rm bbb-libreoffice & + let PORT=8200+${INSTANCE_NUMBER} + + SOFFICE_WORK_DIR="/var/tmp/soffice_"`printf "%02d\n" $INSTANCE_NUMBER` + + docker run --name bbb-libreoffice-${INSTANCE_NUMBER} -p $PORT:8000 -v${SOFFICE_WORK_DIR}:${SOFFICE_WORK_DIR} --rm bbb-libreoffice & wait $! else diff --git a/bbb-libreoffice/docker/Dockerfile b/bbb-libreoffice/docker/Dockerfile index a34bc249df..30f4f67e6c 100644 --- a/bbb-libreoffice/docker/Dockerfile +++ b/bbb-libreoffice/docker/Dockerfile @@ -4,16 +4,19 @@ ENV DEBIAN_FRONTEND noninteractive RUN apt update -RUN addgroup --system --gid 996 libreoffice -RUN adduser --disabled-password --system --disabled-login --shell /sbin/nologin --gid 996 --uid 996 libreoffice +ARG user_id +RUN echo "User id = $user_id" + +RUN addgroup --system --gid $user_id libreoffice + +# We need to ensure that this user id is the same as the user bigbluebutton in the host +RUN adduser --disabled-password --system --disabled-login --shell /sbin/nologin --gid $user_id --uid $user_id libreoffice RUN apt -y install locales-all fontconfig libxt6 libxrender1 RUN apt -y install libreoffice --no-install-recommends RUN dpkg-reconfigure fontconfig && fc-cache -f -s -v -RUN for i in `seq -w 1 10` ; do mkdir -m 777 /var/tmp/soffice${i}; done - VOLUME ["/usr/share/fonts/"] RUN chown libreoffice /home/libreoffice/ diff --git a/bbb-libreoffice/install.sh b/bbb-libreoffice/install.sh index 1dc146b7fe..fb74d1aa23 100755 --- a/bbb-libreoffice/install.sh +++ b/bbb-libreoffice/install.sh @@ -25,7 +25,7 @@ fi IMAGE_CHECK=`docker image inspect bbb-libreoffice &> /dev/null && echo 1 || echo 0` if [ "$IMAGE_CHECK" = "0" ]; then echo "Docker image doesn't exists, building" - docker build -t bbb-libreoffice docker/ + docker build -t bbb-libreoffice --build-arg user_id=`id -u bigbluebutton` docker/ else echo "Docker image already exists"; fi @@ -38,11 +38,12 @@ if [ "$FOLDER_CHECK" = "0" ]; then chmod 700 /usr/share/bbb-libreoffice/libreoffice_container.sh chown -R root /usr/share/bbb-libreoffice/ + cp assets/bbb-libreoffice.service /lib/systemd/system/bbb-libreoffice@.service + systemctl daemon-reload + for i in `seq 1 4` ; do - cat assets/bbb-libreoffice.service | sed 's/INSTANCE_NUMBER/0'${i}'/g' > /lib/systemd/system/bbb-libreoffice-0${i}.service - systemctl daemon-reload - systemctl enable bbb-libreoffice-0${i} - systemctl start bbb-libreoffice-0${i} + systemctl enable bbb-libreoffice@${i} + systemctl start bbb-libreoffice@${i} done else diff --git a/bbb-libreoffice/uninstall.sh b/bbb-libreoffice/uninstall.sh index 2708246601..4bc1dd3ac4 100755 --- a/bbb-libreoffice/uninstall.sh +++ b/bbb-libreoffice/uninstall.sh @@ -1,13 +1,15 @@ #!/bin/bash +set -e + if [ "$EUID" -ne 0 ]; then - echo "Please run this script as root ( or with sudo )" ; - exit 1; + echo "Please run this script as root ( or with sudo )" ; + exit 1; fi; IMAGE_CHECK=`docker image inspect bbb-libreoffice 2>&1 > /dev/null && echo 1 || echo 0` if [ "$IMAGE_CHECK" = "1" ]; then - echo "Stopping services" - systemctl --no-pager --no-legend --value --state=running | grep bbb-libreoffice | awk -F '.service' '{print $1}' | xargs -n 1 systemctl stop + echo "Stopping services" + systemctl --no-pager --no-legend --value --state=running | grep bbb-libreoffice | awk -F '.service' '{print $1}' | xargs --no-run-if-empty -n 1 systemctl stop echo "Removing image" docker image rm bbb-libreoffice @@ -17,13 +19,13 @@ fi FOLDER_CHECK=`[ -d /usr/share/bbb-libreoffice/ ] && echo 1 || echo 0` if [ "$FOLDER_CHECK" = "1" ]; then echo "Stopping services" - systemctl --no-pager --no-legend --value --state=running | grep bbb-libreoffice | awk -F '.service' '{print $1}' | xargs -n 1 systemctl stop + systemctl --no-pager --no-legend --value --state=running | grep bbb-libreoffice | awk -F '.service' '{print $1}' | xargs --no-run-if-empty -n 1 systemctl stop echo "Removing install folder" rm -rf /usr/share/bbb-libreoffice/ echo "Removing service definitions" - rm /lib/systemd/system/bbb-libreoffice-0* + rm /lib/systemd/system/bbb-libreoffice@.service find /etc/systemd/ | grep bbb-libreoffice | xargs --no-run-if-empty -n 1 -I __ rm __ systemctl daemon-reload fi; diff --git a/bigbluebutton-web/grails-app/conf/bigbluebutton.properties b/bigbluebutton-web/grails-app/conf/bigbluebutton.properties index 9f032fe2d3..0d79486ed4 100755 --- a/bigbluebutton-web/grails-app/conf/bigbluebutton.properties +++ b/bigbluebutton-web/grails-app/conf/bigbluebutton.properties @@ -48,6 +48,21 @@ presCheckExec=/usr/share/prescheck/prescheck.sh # Office doc to PDF right away. skipOfficePrecheck=true +#---------------------------------------------------- +# Number of soffice processes that are running on this machine +sofficeManagers=4 + +#---------------------------------------------------- +# Port number of the first soffice process +sofficeBasePort=8201 + +#---------------------------------------------------- +# Working directory prefix for each soffice process. +# The value of this is appended with the number of the +# soffice process (starting at 1), padded to two digits +# (e.g. /var/tmp/soffice_01 for the first process). +sofficeWorkingDirBase=/var/tmp/soffice_ + #---------------------------------------------------- # These will be copied in cases where the conversion process # fails to generate a slide from the uploaded presentation @@ -261,6 +276,7 @@ apiVersion=2.0 # Salt which is used by 3rd-party apps to authenticate api calls securitySalt=330a8b08c3b4c61533e1d0c5ce1ac88f + # Directory where we drop the <meeting-id-recorded>.done file recordStatusDir=/var/bigbluebutton/recording/status/recorded diff --git a/bigbluebutton-web/grails-app/conf/spring/doc-conversion.xml b/bigbluebutton-web/grails-app/conf/spring/doc-conversion.xml index 6658ebc5ef..e7bc7f34c4 100755 --- a/bigbluebutton-web/grails-app/conf/spring/doc-conversion.xml +++ b/bigbluebutton-web/grails-app/conf/spring/doc-conversion.xml @@ -39,6 +39,9 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>. init-method="start" destroy-method="stop"> <property name="officeDocumentValidator" ref="officeDocumentValidator"/> <property name="skipOfficePrecheck" value="${skipOfficePrecheck}"/> + <property name="sofficeBasePort" value="${sofficeBasePort:8201}"/> + <property name="sofficeManagers" value="${sofficeManagers:4}"/> + <property name="sofficeWorkingDirBase" value="${sofficeWorkingDirBase:/var/tmp/soffice_}"/> </bean> <bean id="pageExtractor" class="org.bigbluebutton.presentation.imp.PageExtractorImp"/> diff --git a/bigbluebutton-web/grails-app/controllers/org/bigbluebutton/web/controllers/ApiController.groovy b/bigbluebutton-web/grails-app/controllers/org/bigbluebutton/web/controllers/ApiController.groovy index ae32f13d3f..94b49905d8 100755 --- a/bigbluebutton-web/grails-app/controllers/org/bigbluebutton/web/controllers/ApiController.groovy +++ b/bigbluebutton-web/grails-app/controllers/org/bigbluebutton/web/controllers/ApiController.groovy @@ -2125,6 +2125,10 @@ class ApiController { uploadFailReasons.add("failed_to_download_file") uploadFailed = true } + } else { + log.error("Null presentation directory meeting=[${meetingId}], presentationDir=[${presentationDir}], presId=[${presId}]") + uploadFailReasons.add("null_presentation_dir") + uploadFailed = true } } diff --git a/bigbluebutton-web/run.sh b/bigbluebutton-web/run.sh index 80d6820071..4772aea81f 100755 --- a/bigbluebutton-web/run.sh +++ b/bigbluebutton-web/run.sh @@ -1,2 +1,14 @@ #!/usr/bin/env bash +IS_BBB_WEB_RUNNING=`netstat -an | grep LISTEN | grep 8090 > /dev/null && echo 1 || echo 0` + +if [ "$IS_BBB_WEB_RUNNING" = "1" ]; then + echo "bbb-web is running, exiting" + exit 1 +fi + +if [ "`whoami`" != "bigbluebutton" ]; then + echo "ERROR: bbb-web must run as bigbluebutton user ( because of the uploaded files permissions )" + exit 1 +fi + grails prod run-app --port 8090 -- GitLab