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