Rewrite of the installation script and accompanying documentation. (#15)

Rewrite of the installation script and accompanying documentation.
This commit is contained in:
Dolf Starreveld
2018-03-03 06:41:15 -08:00
committed by Scott Lamb
parent 0d69f4f49b
commit 760b8d95fb
13 changed files with 1134 additions and 365 deletions

123
scripts/build.sh Executable file
View File

@@ -0,0 +1,123 @@
#!/bin/bash
#
# This file is part of Moonfire NVR, a security camera network video recorder.
# Copyright (C) 2016-17 Scott Lamb <slamb@slamb.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# In addition, as a special exception, the copyright holders give
# permission to link the code of portions of this program with the
# OpenSSL library under certain conditions as described in each
# individual source file, and distribute linked combinations including
# the two.
#
# You must obey the GNU General Public License in all respects for all
# of the code used other than OpenSSL. If you modify file(s) with this
# exception, you may extend this exception to your version of the
# file(s), but you are not obligated to do so. If you do not wish to do
# so, delete this exception statement from your version. If you delete
# this exception statement from all source files in the program, then
# also delete it here.
#
# 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.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
. `dirname ${BASH_SOURCE[0]}`/script-functions.sh
initEnvironmentVars
# Process command line options
#
while getopts ":Bt" opt; do
case $opt in
B) BUILD_ONLY=1
;;
t) IGNORE_TESTS=1
;;
:)
echo "Option -$OPTARG requires an argument." >&2
exit 1
;;
\?)
echo "Invalid option: -$OPTARG" >&2
exit 1
;;
esac
done
# Setup cargo if files are present
#
initCargo
# Check environment
#
rv=$(getVersion rustc 0.0)
if ! versionAtLeast "$rv" "$RUSTC_MIN_VERSION"; then
echo "rustc not present or version less than $RUSTC_MIN_VERSION"
exit 1
fi
cv=$(getVersion cargo 0.0)
if ! versionAtLeast "$cv" "$CARGO_MIN_VERSION"; then
echo "cargo not present or version less than $CARGO_MIN_VERSION"
exit 1
fi
yv=$(getVersion yarn 0.0)
if ! versionAtLeast "$yv" "$YARN_MIN_VERSION"; then
echo "yarn not present or version less than $YARN_MIN_VERSION"
exit 1
fi
# Building main server component
#
if [ "${FORCE_CLEAN:-0}" -eq 1 ]; then
echo "Forcing clean server build..."; echo
cargo clean
fi
echo "Building test version..."; echo
if ! cargo test; then
echo "test failed."
echo "Try to run the following manually for more info"
echo "RUST_TEST_THREADS=1 cargo test --verbose"
echo
if [ "${IGNORE_TESTS:-0}" -ne 1 ]; then
exit 1
fi
fi
if ! cargo build --release; then
echo "Server/release build failed."
echo "Try to run the following manually for more info"
echo "RUST_TEST_THREADS=1 cargo build --release --verbose"
echo
exit 1
fi
# Building UI components
#
echo "Building UI components..."; echo
if ! yarn build; then
echo "UI build failed."
echo "yarn build"
echo
exit 1
fi
# Stop if build only is desired
#
if [ "${BUILD_ONLY:-0}" != 0 ]; then
echo "Build (only) complete, exiting"; echo
exit 0
fi
. `dirname ${BASH_SOURCE[0]}`/install.sh

3
scripts/clean.sh Executable file
View File

@@ -0,0 +1,3 @@
#!/bin/sh
#
rm -fr node_modules

139
scripts/install.sh Executable file
View File

@@ -0,0 +1,139 @@
#!/bin/bash
#
# This file is part of Moonfire NVR, a security camera network video recorder.
# Copyright (C) 2016-17 Scott Lamb <slamb@slamb.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# In addition, as a special exception, the copyright holders give
# permission to link the code of portions of this program with the
# OpenSSL library under certain conditions as described in each
# individual source file, and distribute linked combinations including
# the two.
#
# You must obey the GNU General Public License in all respects for all
# of the code used other than OpenSSL. If you modify file(s) with this
# exception, you may extend this exception to your version of the
# file(s), but you are not obligated to do so. If you do not wish to do
# so, delete this exception statement from your version. If you delete
# this exception statement from all source files in the program, then
# also delete it here.
#
# 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.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
. `dirname ${BASH_SOURCE[0]}`/script-functions.sh
# Determine directory path of this script
#
initEnvironmentVars
# Process command line options
#
while getopts ":s" opt; do
case $opt in
s) NO_SERVICE=1
;;
:)
echo "Option -$OPTARG requires an argument." >&2
exit 1
;;
\?)
echo "Invalid option: -$OPTARG" >&2
exit 1
;;
esac
done
sudo install -m 755 target/release/moonfire-nvr ${SERVICE_BIN}
if [ -x "${SERVICE_BIN}" ]; then
echo "Server Binary installed..."; echo
else
echo "Server build failed to install..."; echo
exit 1
fi
if [ ! -d "${LIB_DIR}" ]; then
sudo mkdir "${LIB_DIR}"
fi
if [ -d "ui-dist" ]; then
sudo mkdir -p "${LIB_DIR}/ui"
sudo cp -R ui-dist/. "${LIB_DIR}/ui/"
sudo chown -R ${NVR_USER}:${NVR_GROUP} "${LIB_DIR}/ui/"
echo "Server UI installed..."; echo
else
echo "Server UI failed to build or install..."; echo
exit 1
fi
if [ "${NO_SERVICE:-0}" != 0 ]; then
echo "Not configuring systemctl service. Done"; echo
exit 0
fi
# Make sure user was created
#
if ! userExists "${NVR_USER}"; then
echo "NVR_USER=${NVR_USER} was not created. Do so manually"
echo "or run the setup script."
exit 1
fi
# Prepare service files for socket when using priviliged port
#
SOCKET_SERVICE_PATH="/etc/systemd/system/${SERVICE_NAME}.socket"
if [ $NVR_PORT -lt 1024 ]; then
echo_fatal "NVR_PORT ($NVR_PORT) < 1024: priviliged ports not supported"
exit 1
fi
# Prepare service files for moonfire
#
SERVICE_PATH="/etc/systemd/system/${SERVICE_NAME}.service"
sudo sh -c "cat > ${SERVICE_PATH}" <<NVR_EOF
[Unit]
Description=${SERVICE_DESC}
After=network-online.target
[Service]
ExecStart=${SERVICE_BIN} run \\
--sample-file-dir=${SAMPLE_MEDIA_DIR}/${SAMPLE_FILE_DIR} \\
--db-dir=${DB_DIR} \\
--ui-dir=${LIB_DIR}/ui \\
--http-addr=0.0.0.0:${NVR_PORT}
Environment=TZ=:/etc/localtime
Environment=MOONFIRE_FORMAT=google-systemd
Environment=MOONFIRE_LOG=info
Environment=RUST_BACKTRACE=1
Type=simple
User=${NVR_USER}
Nice=-20
Restart=on-abnormal
CPUAccounting=true
MemoryAccounting=true
BlockIOAccounting=true
[Install]
WantedBy=multi-user.target
NVR_EOF
# Configure and start service
#
if [ -f "${SERVICE_PATH}" ]; then
echo 'Configuring system daemon...'; echo
sudo systemctl daemon-reload
sudo systemctl enable ${SERVICE_NAME}
sudo systemctl restart ${SERVICE_NAME}
echo 'Getting system daemon status...'; echo
sudo systemctl status ${SERVICE_NAME}
fi

236
scripts/script-functions.sh Executable file
View File

@@ -0,0 +1,236 @@
#
# This file is part of Moonfire NVR, a security camera network video recorder.
# Copyright (C) 2016-2017 Scott Lamb <slamb@slamb.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# In addition, as a special exception, the copyright holders give
# permission to link the code of portions of this program with the
# OpenSSL library under certain conditions as described in each
# individual source file, and distribute linked combinations including
# the two.
#
# You must obey the GNU General Public License in all respects for all
# of the code used other than OpenSSL. If you modify file(s) with this
# exception, you may extend this exception to your version of the
# file(s), but you are not obligated to do so. If you do not wish to do
# so, delete this exception statement from your version. If you delete
# this exception statement from all source files in the program, then
# also delete it here.
#
# 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.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
if [ -z "$BASH_VERSION" ]; then
echo "Script must run using bash (/bin/bash), not dash (/bin/sh)."
exit 1
fi
# Useful constants
#
NODE_MIN_VERSION="8"
YARN_MIN_VERSION="1.0"
CARGO_MIN_VERSION="0.2"
RUSTC_MIN_VERSION="1.17"
FFMPEG_MIN_VERSION="55.1.101"
FFMPEG_RELEASE_VERSION="3.4"
normalizeDirPath()
{
echo "$( cd -P "$1" && pwd )"
}
resolvePath()
{
local d="$1"
while [ -h "$d" ]; do # resolve $d until file no longer a symlink
DIR="$( cd -P "$( dirname "$d" )" && pwd )"
d="$(readlink "$d")"
# if $d was rel symlink, resolve relative to path of symlink
[[ "$d" != /* ]] && ="$DIR/$d"
done
echo "$d"
}
functionsInit()
{
local p="$(resolvePath "${BASH_SOURCE[0]}")"
MOONFIRE_DIR="$(normalizeDirPath "`dirname "${p}"`/..")"
}
catPrefix()
{
sed -e "s/^/$2/" < "$1"
}
# Read possible user config and then compute all derived environment
# variables used by the script(s).
#
initEnvironmentVars()
{
test -z "${MOONFIRE_DIR}" && functionsInit
if [ -r "${MOONFIRE_DIR}/prep.config" ]; then
. "${MOONFIRE_DIR}/prep.config"
fi
SRC_DIR="$(resolvePath "$MOONFIRE_DIR/src")"
NVR_USER="${NVR_USER:-moonfire-nvr}"
NVR_GROUP="${NVR_GROUP:-$NVR_USER}"
NVR_PORT="${NVR_PORT:-8080}"
NVR_HOME_BASE="${NVR_HOME_BASE:-/var/lib}"
NVR_HOME="${NVR_HOME_BASE}/${NVR_USER}"
DB_NAME="${DB_NAME:-db}"
DB_DIR="${DB_DIR:-$NVR_HOME}/${DB_NAME}"
SAMPLE_FILE_DIR="${SAMPLE_FILE_DIR:-sample}"
SAMPLE_MEDIA_DIR="${SAMPLE_MEDIA_DIR:-$NVR_HOME}"
SERVICE_NAME="${SERVICE_NAME:-moonfire-nvr}"
SERVICE_DESC="${SERVICE_DESC:-Moonfire NVR}"
SERVICE_BIN="${SERVICE_BIN:-/usr/local/bin/moonfire-nvr}"
LIB_DIR="${UI_DIR:-/usr/local/lib/moonfire-nvr}"
}
# Create file with confguration variables that are user changeable.
# Not all variables are included here, only the likely ones to be
# modified.
# If the file already exists, it will not be modified
#
makePrepConfig()
{
test -z "${MOONFIRE_DIR}" && functionsInit
if [ ! -f "${MOONFIRE_DIR}/prep.config" ]; then
cat >"${MOONFIRE_DIR}/prep.config" <<-EOF_CONFIG
NVR_USER=$NVR_USER
NVR_PORT=$NVR_PORT
SAMPLE_FILE_DIR=$SAMPLE_FILE_DIR
#SAMPLE_MEDIA_DIR=/mount/media
SERVICE_NAME=$SERVICE_NAME
SERVICE_DESC="$SERVICE_DESC"
EOF_CONFIG
echo "File prep.config newly created. Inspect and change as necessary."
echo "When done, re-run this setup script. Currently it contains:"
catPrefix "${MOONFIRE_DIR}/prep.config" " "
exit 0
else
echo "Setting up with variables:"
catPrefix "${MOONFIRE_DIR}/prep.config" " "
echo ""
fi
}
# Extract version data from stdin, possibly grepping first against
# single argument.
#
extractVersion()
{
local pattern="$1"
if [ -n "${pattern}" ]; then
grep "$pattern" | sed -e 's/[^0-9.]*\([0-9. ]*\).*/\1/' | tr -d ' '
else
sed -e 's/[^0-9.]*\([0-9. ]*\).*/\1/' | tr -d ' '
fi
}
getAVersion()
{
local v=`$1 $2 2>/dev/null | extractVersion`
if [ "X${v}" = "X" ]; then echo "$3"; else echo "${v}"; fi
}
getVersion()
{
getAVersion $1 --version $2
}
getClassicVersion()
{
getAVersion $1 -version $2
}
versionAtLeast()
{
v1=(${1//./ } 0 0 0); v1=("${v1[@]:0:3}")
v2=(${2//./ } 0 0 0); v2=("${v2[@]:0:3}")
for i in 0 1 2; do
if [ "${v1[$i]}" -gt "${v2[$i]}" ]; then return 0; fi
if [ "${v1[$i]}" -lt "${v2[$i]}" ]; then return 1; fi
done
return 0
}
initCargo()
{
if [ -r ~/.cargo/env ]; then
source ~/.cargo/env
fi
}
userExists()
{
return $(id -u "$1" >/dev/null 2>&1)
}
groupExists()
{
return $(id -g "$1" >/dev/null 2>&1)
}
moonfire()
{
case "$1" in
start)
sudo systemctl start "$2"
;;
stop)
sudo systemctl stop "$2"
;;
esac
}
# Add/update cameras in the database
#
# $1: path to cameras.sql file
# $2: moonfire service name
# $3: moonfire user name
# $4: moonfire database path
#
addCameras()
{
local cpath="${CAMERAS_PATH:-$1}"
if [ -r "${cpath}" ]; then
echo 'Add cameras...'; echo
# Before adding cameras, must stop service
moonfire stop "${SERVICE_NAME:-$2}" >/dev/null 2>&1
sudo -u ${NVR_USER:-$3} -H sqlite3 "${DB_PATH:-$4}" < "${cpath}"
moonfire start "${SERVICE_NAME:-$2}" >/dev/null 2>&1
fi
}
echo_stderr()
{
echo "$@" 1>&2
}
echo_warn()
{
echo_stderr "WARNING: $@"
echo_stderr
}
echo_fatal()
{
echo_stderr "ERROR: $@"
echo_stderr
exit 1;
}

257
scripts/setup-ubuntu.sh Executable file
View File

@@ -0,0 +1,257 @@
#!/bin/bash
#
# This file is part of Moonfire NVR, a security camera network video recorder.
# Copyright (C) 2016-2017 Scott Lamb <slamb@slamb.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# In addition, as a special exception, the copyright holders give
# permission to link the code of portions of this program with the
# OpenSSL library under certain conditions as described in each
# individual source file, and distribute linked combinations including
# the two.
#
# You must obey the GNU General Public License in all respects for all
# of the code used other than OpenSSL. If you modify file(s) with this
# exception, you may extend this exception to your version of the
# file(s), but you are not obligated to do so. If you do not wish to do
# so, delete this exception statement from your version. If you delete
# this exception statement from all source files in the program, then
# also delete it here.
#
# 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.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
. `dirname ${BASH_SOURCE[0]}`/script-functions.sh
initEnvironmentVars
makePrepConfig
# Process command line options
#
while getopts ":f" opt; do
case $opt in
f) DONT_BUILD_FFMPEG=1
;;
:)
echo_fatal "Option -$OPTARG requires an argument." >&2
;;
\?)
echo_fatal "Invalid option: -$OPTARG" >&2
;;
esac
done
# Setup all apt packages we need
#
echo 'Preparing and downloading packages we need...'; echo
PKGS="build-essential pkg-config sqlite3"
#PKGS="$PKGS libavcodec-dev libavformat-dev libavutil-dev"
PKGS="$PKGS libncurses5-dev libncursesw5-dev"
PKGS="$PKGS libsqlite3-dev libssl-dev"
# Add yarn before NodeSource so it can all go in one update
#
yv=$(getVersion yarn "NA")
if [ ${yv} = "NA" ]; then
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg |\
sudo apt-key add -
echo "deb https://dl.yarnpkg.com/debian/ stable main" |\
sudo tee /etc/apt/sources.list.d/yarn.list
PKGS="$PKGS yarn"
fi
# Check for minimum node version
#
nv=$(getVersion node 0)
if ! versionAtLeast "$nv" "$NODE_MIN_VERSION"; then
# Nodesource will also make sure we have apt-transport-https
# and will run apt-get-update when done
#
curl -sL https://deb.nodesource.com/setup_${NODE_MIN_VERSION}.x |
sudo -E bash -
PKGS="$PKGS nodejs"
DO_UPDATE=0
else
PKGS="$PKGS apt-transport-https"
fi
# Run apt-get update if still necessary
#
if [ ${DO_UPDATE:-1} ]; then sudo apt-get update -y; fi
# Install necessary pakackes
#
sudo apt-get install -y $PKGS
sudo apt-get autoremove -y
# Check for ffmpeg and install by building if necessary
# This needs to be done before building moonfire so it can
# find the right versions of the libraries.
#
ffv=`ffmpeg -version 2>/dev/null | extractVersion libavutil`
ffv=${ffv:-0}
if ! versionAtLeast "$ffv" "$FFMPEG_MIN_VERSION"; then
if [ "${DONT_BUILD_FFMPEG:-0}" -ne 0 ]; then
echo "ffmpeg version (${ffv}) installed is too old for moonfire."
echo "Suggest you manually install at least version $FFMPEG_MIN_VERSION of libavutil."
echo "ffmpeg versions 2.x and 3.x all should work."
else
OLDDIR=`pwd`
cd ..
if [ -d FFmpeg ]; then
echo "Removing older FFmpeg directory..."; echo
rm -fr FFmpeg
fi
echo "Fetching FFmpeg source..."; echo
git clone --depth 1 -b "release/${FFMPEG_RELEASE_VERSION}" https://github.com/FFmpeg/FFmpeg.git
cd FFmpeg
pt=`uname -p 2>& /dev/null`
if [ -z "${pt##*86*}" ]; then
sudo apt-get install -y yasm
fi
./configure --enable-shared
make
sudo make install
sudo ldconfig
cd "$OLDDIR"
OLDDIR=
fi
else
echo "FFmpeg is already usable..."; echo
fi
# If cargo appears installed, initialize for using it so rustc can be found
#
initCargo
# Make sure we have rust and cargo
rv=$(getVersion rustc 0.0)
if ! versionAtLeast "$rv" "$RUSTC_MIN_VERSION"; then
echo "Installing latest rust and cargo..."; echo
curl https://sh.rustup.rs -sSf | sh -s - -y
initCargo
fi
cv=$(getVersion cargo "NA")
if [ ${cv} = "NA" ]; then
echo "Cargo is not (properly) installed, but rust is."
echo "Suggest you install the latest rustup, or manually install cargo."
echo "Install using: curl https://sh.rustup.rs -sSf | sh -s -y"
exit 1
fi
# Now make sure we have dev environment and tools for the UI portion
#
echo "Installing all dependencies with yarn..."
yarn install
finish()
{
if [ -z "${OLDDIR}" ]; then
cd "${OLDDIR}"
fi
}
trap finish EXIT
# Create user and groups if not there
#
echo
echo "Create user/group and directories we need..."; echo
if ! groupExists "${NVR_GROUP}"; then
sudo addgroup --quiet --system ${NVR_GROUP}
fi
if ! userExists "${NVR_USER}"; then
sudo adduser --quiet --system ${NVR_USER} \
--ingroup "${NVR_GROUP}" --home "${NVR_HOME}"
fi
if [ ! -d "${NVR_HOME}" ]; then
sudo mkdir "${NVR_HOME}"
fi
sudo chown ${NVR_USER}:${NVR_GROUP} "${NVR_HOME}"
# Correct possible timezone issues
#
echo "Correcting possible /etc/localtime setup issue..."; echo
if [ ! -L /etc/localtime ] && [ -f /etc/timezone ] &&
[ -f "/usr/share/zoneinfo/`cat /etc/timezone`" ]; then
sudo rm /etc/localtime
sudo ln -s /usr/share/zoneinfo/`cat /etc/timezone` /etc/localtime
fi
# Prepare for sqlite directory and set schema into db
#
DB_NAME=db
DB_PATH="${DB_DIR}/${DB_NAME}"
if [ ! -d "${DB_DIR}" ]; then
echo 'Create database...'; echo
sudo -u "${NVR_USER}" -H mkdir "${DB_DIR}"
fi
if [ ! -f "${DB_PATH}" ]; then
sudo -u "${NVR_USER}" -H sqlite3 "${DB_PATH}" < "${SRC_DIR}/schema.sql"
fi
CAMERAS_PATH="${MOONFIRE_DIR}/cameras.sql"
if [ ! -r "${CAMERAS_PATH}" ]; then
CAMERAS_PATH="${MOONFIRE_DIR}/../cameras.sql"
if [ ! -r "${CAMERAS_PATH}" ]; then
CAMERAS_PATH=
fi
fi
if [ ! -z "${CAMERAS_PATH}" ]; then
echo "Adding camera confguration to db..."; echo
addCameras
else
echo_warn "No cameras auto configured. Use \"moonfire-nvr config\" to do it later..."
fi
# Make sure samples directory is ready
#
if [ -z "${SAMPLE_MEDIA_DIR}" ]; then
echo "SAMPLE_MEDIA_DIR variable not configured. Check configuration."
exit 1
fi
SAMPLE_FILE_PATH="${SAMPLE_MEDIA_DIR}/${SAMPLE_FILE_DIR}"
if [ "${SAMPLE_FILE_PATH##${NVR_HOME}}" != "${SAMPLE_FILE_PATH}" ]; then
# Under the home directory, create if not there
if [ ! -d "${SAMPLE_FILE_PATH}" ]; then
echo "Created samples directory: $SAMPLE_FILE_PATH"; echo
sudo -u ${NVR_USER} -H mkdir "${SAMPLE_FILE_PATH}"
fi
else
if [ ! -d "${SAMPLE_FILE_PATH}" ]; then
read -r -d '' MSG <<-MSG1
Samples directory $SAMPLE_FILE_PATH does not exist.
If a mounted file system, make sure /etc/fstab is properly configured,
and file system is mounted and directory created.
MSG1
echo_fatal "$MSG"
fi
fi
# Make sure all sample directories and files owned by moonfire
#
sudo chown -R ${NVR_USER}.${NVR_USER} "${SAMPLE_FILE_PATH}"
echo "Fix ownership of sample files..."; echo
cat <<-'INSTRUCTIONS'
Unless there are errors above, everything you need should have been installed
and you are now ready to build, install, and then use moonfire.
Build by executing the script: scripts/build.sh
Install by executing the script: scripts/install.sh (run automatically by build
step).
INSTRUCTIONS