From 437f3da360c99b1e999c1f0d6ce4e64e24332912 Mon Sep 17 00:00:00 2001 From: bryan Date: Mon, 10 May 2021 17:08:18 -0400 Subject: [PATCH] Refactoring --- README.md | 22 +- installJRMC | 2508 ++++++++++++++++++++++++--------------------------- 2 files changed, 1183 insertions(+), 1347 deletions(-) diff --git a/README.md b/README.md index 0cf0d83..df18145 100755 --- a/README.md +++ b/README.md @@ -11,21 +11,21 @@ This program will install [JRiver Media Center](https://www.jriver.com/) and ass `installJRMC [--option [ARGUMENT]]` -Running `installJRMC` without any options will install the latest version of JRiver Media Center from the official JRiver repository (Ubuntu/Debian) or my [unofficial repository](https://repos.bryanroessler.com/jriver/) (Fedora/CentOS) using the system package manager. If any other option is specified then the default install method will need to be specified using `--install-repo` (or `--install-rpm`). This makes it possible to install services, containers, repos, etc. independent from Media Center. +Running `installJRMC` without any options will install the latest version of JRiver Media Center from the official JRiver repository (Ubuntu/Debian) or my [unofficial repository](https://repos.bryanroessler.com/jriver/) (Fedora/CentOS) using the system package manager. If any other option is specified then the default install method will need to be specified using `--repo` or `--rpm`. This makes it possible to install services, containers, repos, etc. independent of Media Center. ## Options Here is a list of possible options that can be passed to the script. You can always find the latest supported options by running `installJRMC --help`. ```text ---install-repo - Install JRiver Media Center from repository using package manager (Default) +--repo + Install JRiver Media Center from repository using package manager DEB-based OSes: Official package repository RPM-based OSes: BryanC's unofficial repository ---install-rpm - (RPM-based OSes only) Build RPM from source DEB and install it +--rpm + Build RPM from source DEB and install it --rpmbuild - Build RPM from source DEB + Build RPM from source DEB but do not install --outputdir PATH Generate rpmbuild output in this directory (Default: $PWD/outputdir) --mcversion VERSION @@ -81,7 +81,7 @@ jriver-createrepo Install hourly service to build latest MC RPM and run createrepo ``` -It is possible to install multiple services at one time using multiple `--service` blocks: `installJRMC --install-repo --service jriver-x11vnc --service jriver-mediacenter` +It is possible to install multiple services at one time using multiple `--service` blocks: `installJRMC --repo --service jriver-x11vnc --service jriver-mediacenter` #### `jriver-x11vnc` versus `jriver-xvnc-mediacenter` @@ -105,11 +105,11 @@ It is possible to install multiple services at one time using multiple `--servic Installs the latest version of JRiver Media Center from the repository. -* `installJRMC --install-repo --service jriver-mediaserver` +* `installJRMC --repo --service jriver-mediaserver` Installs JRiver Media Center from the repository and starts/enables the /MediaServer service. -* `installJRMC --install-rpm --restorefile /path/to/license.mjr --mcversion 26.0.56` +* `installJRMC --rpm --restorefile /path/to/license.mjr --mcversion 26.0.56` Builds JRiver Media Center version 26.0.56 RPM from the source DEB, installs it (RPM distros only), and activates it using the specified .mjr license file. @@ -121,11 +121,11 @@ It is possible to install multiple services at one time using multiple `--servic Installs the jriver-createrepo timer and service to build the RPM, move it to the webroot, and run createrepo as `www-user` hourly. -* `installJRMC --install-repo --service jriver-x11vnc --service jriver-mediacenter --vncpass "letmein"` +* `installJRMC --repo --service jriver-x11vnc --service jriver-mediacenter --vncpass "letmein"` Installs services to share the existing local desktop via VNC and automatically run Media Center on startup. -* `installJRMC --install-repo --service jriver-xvnc-mediacenter --display ":2"` +* `installJRMC --repo --service jriver-xvnc-mediacenter --display ":2"` Installs an Xvnc server on display ':2' that starts Media Center. diff --git a/installJRMC b/installJRMC index cc7c679..e341e5d 100755 --- a/installJRMC +++ b/installJRMC @@ -10,1431 +10,1267 @@ shopt -s extglob # 1. Raspberry Pi OS support # 2. Interactive installation (ncurses?) # -# installJRMC can be run directly or sourced as a function (by sourcing this file) -# Arguments: -# see _printHelpAndExit() and _parseInput() for available options -# Returns: -# 0 if critical functions complete successfully ####################################### -installJRMC() { - _scriptversion="0.9.5" - _boardurl="https://yabb.jriver.com/interact/index.php/board,67.0.html" - _outputdir="$_basedir/output" - _createrepo_webroot="/srv/jriver" - _exec_user="$(whoami)" - _available_services=("jriver-createrepo" "jriver-x11vnc" "jriver-mediaserver" "jriver-mediacenter" "jriver-xvnc-mediacenter") - #_available_containers=("mediacenter-xvnc" "createrepo") +_scriptversion="0.9.6" +_boardurl="https://yabb.jriver.com/interact/index.php/board,67.0.html" +_outputdir="$PWD/output" +_createrepo_webroot="/srv/jriver" +_exec_user="$(whoami)" - _printHelpAndExit() { - debug "Running: ${FUNCNAME[0]}" +_printHelpAndExit() { + debug "Running: ${FUNCNAME[0]}" - cat <<- 'EOF' - USAGE: - installJRMC [[OPTION] [VALUE]]... + cat <<- 'EOF' + USAGE: + installJRMC [[OPTION] [VALUE]]... - OPTIONS - --install-repo - Install JRiver Media Center from repository using package manager (Default) - DEB-based OSes: Official package repository - RPM-based OSes: BryanC unofficial repository - --install-rpm - (RPM-based OSes only) Build RPM from source DEB and install it - --rpmbuild - Build RPM from source DEB - --outputdir PATH - Generate rpmbuild output in this directory (Default: $PWD/outputdir) - --mcversion VERSION - Build or install a specific version (Default: install the latest version) - --restorefile RESTOREFILE - Restore file location for registration (Default: skip registration) - --betapass PASSWORD - Enter beta team password for access to beta builds - --service-user USER - Install systemd services and containers for USER - --service, -s SERVICE - See SERVICES section below for a list of possible services to install - --container, -c CONTAINER (TODO: Under construction) - See CONTAINERS section below for a list of possible services to install - --createrepo - Build rpm, copy to webroot, and run createrepo - --createrepo-webroot PATH - The webroot directory to install the repo (Default: /srv/jriver/) - --createrepo-user USER - The web server user (Default: current user) - --version, -v - Print this script version and exit - --debug, -d - Print debug output - --help, -h - Print help dialog and exit - --uninstall, -u - Uninstall JRiver MC, cleanup service files, and remove firewall rules (does not remove library files) + If no options (besides -d) are provided, the script will default to --repo - SERVICES - jriver-mediaserver - Enable and start a mediaserver systemd service (requires an existing X server) - jriver-mediacenter - Enable and start a mediacenter systemd service (requires an existing X server) - jriver-x11vnc - Enable and start x11vnc for the local desktop (requires an existing X server) - Usually combined with jriver-mediaserver or jriver-mediacenter services - --vncpass and --display are also valid options (see below) - jriver-xvnc-mediacenter - Enable and start a new Xvnc session running JRiver Media Center - --vncpass PASSWORD - Set vnc password for x11vnc/Xvnc access. If no password is set, the script - will either use existing password stored in ~/.vnc/jrmc_passwd or use no password - --display DISPLAY - Display to start x11vnc/Xvnc (Default: The current display (x11vnc) or the - current display incremented by 1 (Xvnc)) - jriver-createrepo - Install hourly service to build latest MC RPM and run createrepo + OPTIONS + --repo + Install JRiver Media Center from repository using package manager + DEB-based OSes: JRiver official package repository + RPM-based OSes: BryanC unofficial repository + --rpm + Build RPM from source DEB and install it + --rpmbuild + Build RPM from source DEB (no installation) + --outputdir PATH + Generate rpmbuild output in this directory (Default: $PWD/outputdir) + --mcversion VERSION + Build or install a specific version (Default: install the latest version) + --restorefile RESTOREFILE + Restore file location for registration (Default: skip registration) + --betapass PASSWORD + Enter beta team password for access to beta builds + --service-user USER + Install systemd services and containers for USER (Default: current user) + --service, -s SERVICE + See SERVICES section below for a list of possible services to install + --container, -c CONTAINER (TODO: Under construction) + See CONTAINERS section below for a list of possible services to install + --createrepo + Build rpm, copy to webroot, and run createrepo + --createrepo-webroot PATH + The webroot directory to install the repo (Default: /srv/jriver/) + --createrepo-user USER + The web server user (Default: current user) + --version, -v + Print this script version and exit + --debug, -d + Print debug output + --help, -h + Print help dialog and exit + --uninstall, -u + Uninstall JRiver MC, cleanup service files, and remove firewall rules (does not remove library files) - CONTAINERS (TODO: Under construction) - mediacenter-xvnc - createrepo - EOF + SERVICES + jriver-mediaserver + Enable and start a mediaserver systemd service (requires an existing X server) + jriver-mediacenter + Enable and start a mediacenter systemd service (requires an existing X server) + jriver-x11vnc + Enable and start x11vnc for the local desktop (requires an existing X server) + Usually combined with jriver-mediaserver or jriver-mediacenter services + --vncpass and --display are also valid options (see below) + jriver-xvnc-mediacenter + Enable and start a new Xvnc session running JRiver Media Center + --vncpass PASSWORD + Set vnc password for x11vnc/Xvnc access. If no password is set, the script + will either use existing password stored in ~/.vnc/jrmc_passwd or use no password + --display DISPLAY + Display to start x11vnc/Xvnc (Default: The current display (x11vnc) or the + current display incremented by 1 (Xvnc)) + jriver-createrepo + Install hourly service to build latest MC RPM and run createrepo + + CONTAINERS (TODO: Under construction) + mediacenter-xvnc + createrepo + EOF # Exit using passed exit code [[ ! -v 1 ]] && exit 0 || exit "$1" } - _parseInput() { - debug "Running: ${FUNCNAME[0]}" +_init() { + debug "Running: ${FUNCNAME[0]}" - # set default behavior - if [[ $# -eq 0 ]] || [[ $# -eq 1 && "$1" =~ ^(--debug|-d)$ ]]; then - _repoinstall="true" - fi + _getOS - if _input=$(getopt -o +vdhus:c: -l install-repo,install-rpm,rpmbuild,outputdir:,mcversion:,restorefile:,betapass:,service-user:,service:,version,debug,help,uninstall,createrepo,createrepo-webroot:,createrepo-user:,vncpass:,display:,container: -- "$@"); then - eval set -- "$_input" - while true; do - case "$1" in - --install-repo) - _repoinstall="true" - ;; - --install-rpm) - _rpmbuild="true" - _rpminstall="true" - ;; - --rpmbuild) - _rpmbuild="true" - ;; - --outputdir) - shift && _outputdir="$1" - ;; - --mcversion) - shift && _mcversion="$1" - ;; - --restorefile) - shift && _restorefile="$1" - ;; - --betapass) - shift && _betapass="$1" - ;; - --service-user) - shift && _service_user="$1" - ;; - --service|-s) - shift && _services+=("$1") - ;; - --container|-c) - shift && _containers+=("$1") - ;; - --version|-v) - echo "Version: $_scriptversion" - exit 0 - ;; - --debug|-d) - echo "Debugging on" - echo "installJRMC version: $_scriptversion" - _debug="true" - ;; - --help|-h) - _printHelpAndExit 0 - ;; - --uninstall|-u) - _uninstall="true" - ;; - --createrepo) - _createrepo="true" - _rpmbuild="true" - ;; - --createrepo-webroot) - shift && _createrepo_webroot="$1" - ;; - --createrepo-user) - shift && _createrepo_user="$1" - ;; - --vncpass) - shift && _vncpass="$1" - ;; - --display) - shift && _display="$1" - ;; - --) - shift - break - ;; - esac - shift - done - else - err "Incorrect options provided" - _printHelpAndExit 1 - fi - } + _parseInput "$@" - err() { echo "Error: $*" >&2; } + # Agnostic commands + _bash_cmd(){ _ifSudo bash -c "$@"; } + _rm_cmd(){ _ifSudo rm -rf "$@"; } + _cp_cmd(){ _ifSudo cp -n "$@"; } + _mkdir_cmd(){ _ifSudo mkdir -p "$@"; } + _ln_cmd(){ _ifSudo ln -s "$@"; } + _systemctl_reload(){ _ifSudo systemctl daemon-reload; } + _systemctl_enable(){ _ifSudo systemctl enable --now "$@"; } + _systemctl_disable(){ _ifSudo systemctl disable --now "$@"; } - debug() { [[ -v _debug ]] && echo "Debug: $*"; } + # OS-specific commands + if [[ "$ID" =~ ^(fedora|centos)$ ]]; then + _pkg_install(){ _ifSudo dnf install -y "$@"; } + _pkg_reinstall(){ _ifSudo dnf reinstall -y "$@"; } + _pkg_install_nogpg(){ _ifSudo dnf install --nogpgcheck -y "$@"; } + _pkg_remove(){ _ifSudo dnf remove -y "$@"; } + _pkg_update(){ _ifSudo dnf makecache; } + _pkg_query(){ _ifSudo rpm -q "$@"; } + _firewall_cmd(){ _ifSudo firewall-cmd "$@"; } + elif [[ "$ID" =~ ^(debian|ubuntu|linuxmint)$ ]]; then + _pkg_install(){ _ifSudo apt-get install -y -q0 "$@"; } + _pkg_reinstall(){ _ifSudo apt-get reinstall -y -q0 "$@"; } + _pkg_install_nogpg(){ _ifSudo apt-get install -y -q0 "$@"; } + _pkg_remove(){ _ifSudo apt-get remove -y -q0 "$@"; } + _pkg_update(){ _ifSudo apt-get update -y -q0; } + _pkg_query(){ _ifSudo dpkg -s "$@"; } + _firewall_cmd(){ _ifSudo ufw "$@"; } + fi - ####################################### - # Prepend this to any command that you wish to execute with sudo - # Requires: - # _exec_user - ####################################### - _ifSudo() { - if [[ "$_exec_user" != "root" ]]; then - sudo "$@" - else - "$@" - fi - } + # Set defaults + if [[ $# -eq 0 ]] || [[ $# -eq 1 && "$1" =~ ^(--debug|-d)$ ]]; then + debug "No options passed, defaulting to --repo installation method" + _repoinstall="true" + fi + + _service_user="${_service_user:-$_exec_user}" + _createrepo_user="${_createrepo_user:-$_exec_user}" + + # Set package aliases + if [[ "$ID" =~ ^(debian|ubuntu|linuxmint)$ ]]; then + declare -Ag PKG_ALIASES + PKG_ALIASES["xorg-x11-utils"]="xorg-x11" + PKG_ALIASES["rpm-build"]="rpm" + PKG_ALIASES["createrepo_c"]="createrepo" + PKG_ALIASES["tigervnc-server"]="tigervnc-standalone-server" + fi + + # Install script dependencies + [[ "$ID" == "centos" ]] && _installPackage epel-release + [[ ! -v _mcversion ]] && _installPackage wget + + # Set MC version + _setVersion +} - ####################################### - # Sources /etc/os-release so we know which OS we're running on - # Used in: _buildCommands() - ####################################### - _getOS() { - debug "Running: ${FUNCNAME[0]}" +_main() { - if [[ -e "/etc/os-release" ]]; then - source "/etc/os-release" - else - err "No /etc/os-release found" - err "Your OS is unsupported" - _printHelpAndExit 1 - fi + _init "$@" - debug "Platform: $ID $VERSION_ID" - } + # Uninstall and exit + if [[ -v _uninstall ]]; then + _uninstall + exit $? + fi + # Install MC using package manager + if [[ -v _repoinstall ]]; then + _installMCFromRepo + _symlinkCerts + _restoreLicense + _openFirewall "jriver" + fi - ####################################### - # Creates some OS-specific functions to query, install, and remove packages and edit - # firewalls, run bash, remove and copy files, etc. - ####################################### - _buildCommands() { + # Build RPM from source deb package + if [[ -v _rpmbuild ]]; then + _acquireDeb + _buildRPM + fi - # Detect OS - _getOS + # Run createrepo + if [[ -v _createrepo ]]; then + _runCreaterepo "$_mcrpm" + fi - debug "Running: ${FUNCNAME[0]}" + # Install the rpm + if [[ -v _rpminstall ]]; then + _installPackage --noquery "$_mcrpm" + _symlinkCerts + _restoreLicense + _openFirewall "jriver" + fi - # Agnostic commands - _bash_cmd(){ _ifSudo bash -c "$@"; } - _rm_cmd(){ _ifSudo rm -rf "$@"; } - _cp_cmd(){ _ifSudo cp -n "$@"; } - _mkdir_cmd(){ _ifSudo mkdir -p "$@"; } - _ln_cmd(){ _ifSudo ln -s "$@"; } - _systemctl_reload(){ _ifSudo systemctl daemon-reload; } - _systemctl_start(){ - echo "Starting $*" - if ! _ifSudo systemctl start "$@"; then - err "Could not start $*" - err "Check service status using \"sudo systemctl status $*\"" - return 1 - fi - } - _systemctl_enable(){ _ifSudo systemctl enable "$@"; } - _systemctl_disable(){ _ifSudo systemctl disable --now "$@"; } - - # OS-specific commands - if [[ "$ID" =~ ^(fedora|centos)$ ]]; then - _pkg_install(){ _ifSudo dnf install -y "$@"; } - _pkg_reinstall(){ _ifSudo dnf reinstall -y "$@"; } - _pkg_install_nogpg(){ _ifSudo dnf install --nogpgcheck -y "$@"; } - _pkg_remove(){ _ifSudo dnf remove -y "$@"; } - _pkg_update(){ _ifSudo dnf makecache; } - _pkg_query(){ _ifSudo rpm -q "$@"; } - _firewall_cmd(){ _ifSudo firewall-cmd "$@"; } - elif [[ "$ID" =~ ^(debian|ubuntu|linuxmint)$ ]]; then - _pkg_install(){ _ifSudo apt-get install -y -q0 "$@"; } - _pkg_reinstall(){ _ifSudo apt-get reinstall -y -q0 "$@"; } - _pkg_install_nogpg(){ _ifSudo apt-get install -y -q0 "$@"; } - _pkg_remove(){ _ifSudo apt-get remove -y -q0 "$@"; } - _pkg_update(){ _ifSudo apt-get update -y -q0; } - _pkg_query(){ _ifSudo dpkg -s "$@"; } - _firewall_cmd(){ _ifSudo ufw "$@"; } - fi - - # Some additional commands specifically for createrepo (primarily to handle user rights) - # Could also go in runCreaterepo() but let's leave it here for now - if [[ $_createrepo_user != "root" ]]; then - if [[ -d "$_createrepo_webroot/repodata" ]]; then - _createrepo_cmd(){ sudo -u "$_createrepo_user" createrepo -q --update "$@"; } + # Install services + _setDisplay + for _service in "${_services[@]}"; do + _servicePrep "$_service" + if ! "_service_$_service"; then + if [[ $? -eq 127 ]]; then + err "Service $_service does not exist, check your service name" else - _createrepo_cmd(){ sudo -u "$_createrepo_user" createrepo -q "$@"; } + err "Failed to create service: $_service" fi - _cr_mkdir_cmd(){ sudo -u "$_createrepo_user" mkdir -p "$@"; } - _cr_cp_cmd(){ sudo -u "$_createrepo_user" cp -n "$@"; } - else - if [[ -d "$_createrepo_webroot/repodata" ]]; then - _createrepo_cmd(){ createrepo -q --update "$@"; } + fi + done + + # Install containers + for _container in "${_containers[@]}"; do + if ! "_container_$_container"; then + if [[ $? -eq 127 ]]; then + err "Container $_container does not exist, check your container name" else - _createrepo_cmd(){ createrepo -q "$@"; } + err "Failed to create container: $_container" fi fi - } + done +} - ####################################### - # Sets default user variables - ####################################### - _setUser() { - - [[ ! -v _service_user ]] && _service_user="$_exec_user" - [[ ! -v _createrepo_user ]] && _createrepo_user="$_exec_user" - } - - - ####################################### - # Installs a package using the system package manager - # Arguments: - # One or more package names - # Options: - # --noquery, -n: Do not query the package state (useful if installing a local RPM) - # Returns: - # Will exit 1 if failed - ####################################### - _installPackage() { - - debug "Running: ${FUNCNAME[0]}" "$@" - - if _input=$(getopt -o +n -l noquery -- "$@"); then - eval set -- "$_input" - while true; do - case "$1" in - --noquery|-n) - local _noquery="true" - ;; - --) - shift - break - ;; - esac - shift - done - else - err "Incorrect options provided" - _printHelpAndExit 1 +####################################### +# Helper functions +####################################### +err() { echo "Error: $*" >&2; } +debug() { + if [[ -v _debug ]]; then + if [[ $# -gt 0 ]]; then + echo "Debug: $*" fi - - # We will add packages to this array if their command is not available - local -a _pkg_array - local -a _url_pkg_array - - # parse arguments (packages) - for _pkg in "$@"; do - # Clean up package name and handle OS-specific tweaks - _packageQuirks "$_pkg" - # Insert the package name to test if already installed - if [[ "$_pkg" != "" ]]; then - if [[ -v _noquery ]] || ! _pkg_query "$_pkg" > /dev/null 2>&1; then - if [[ -v _url_pkg ]]; then - _url_pkg_array+=("$_url_pkg") - else - _pkg_array+=("$_pkg") - fi - fi - fi - done - - # Install from package name (with gpg check) - if [[ ${#_pkg_array[@]} -ge 1 ]]; then - echo "Installing:" "${_pkg_array[@]}" - if [[ -v _debug ]]; then - if ! _pkg_install "${_pkg_array[@]}"; then - err "Failed to install package. Attempting to continue..." - return 1 - fi - elif ! _pkg_install "${_pkg_array[@]}" > /dev/null 2>&1; then - err "Failed to install package. Attempting to continue..." - return 1 - fi - fi - - # Install from package url (without gpg check) - if [[ ${#_url_pkg_array[@]} -ge 1 ]]; then - echo "Installing:" "${_url_pkg_array[@]}" - if [[ -v _debug ]]; then - if ! _pkg_install_nogpg "${_url_pkg_array[@]}"; then - err "Failed to install package. Attempting to continue..." - return 1 - fi - elif ! _pkg_install_nogpg "${_url_pkg_array[@]}" > /dev/null 2>&1; then - err "Failed to install package. Attempting to continue..." - return 1 - fi - fi - } - - - ####################################### - # Handles OS-specific package name tweaks and source urls - # Arguments: - # A package name - # Globals: - # _pkg - # _url_pkg - ####################################### - _packageQuirks() { - - debug "Running: ${FUNCNAME[0]}" "$@" - - unset _url_pkg - - if [[ "$1" == "xorg-x11-utils" && "$ID" =~ ^(debian|ubuntu|linuxmint)$ ]]; then - _pkg="x11-utils" - elif [[ "$1" == "rpm-build" && "$ID" =~ ^(debian|ubuntu|linuxmint)$ ]]; then - _pkg="rpm" - elif [[ "$1" == "createrepo_c" && "$ID" =~ ^(debian|ubuntu|linuxmint)$ ]]; then - _pkg="createrepo" - elif [[ "$1" == "rpmfusion-free-release" ]]; then - if [[ "$ID" == "fedora" ]]; then - # As of MC26 and Fedora 32 I don't believe that the rpmfusion repo is necessary - #_url_pkg="https://download1.rpmfusion.org/free/$ID/rpmfusion-free-release-$VERSION_ID.noarch.rpm" - #_pkg="$1" - _pkg="" - elif [[ "$ID" == "centos" ]]; then - _url_pkg="https://download1.rpmfusion.org/free/el/rpmfusion-free-release-$VERSION_ID.noarch.rpm" - _pkg="$1" - else - _pkg="" - fi - elif [[ "$1" == "epel-release" && "$ID" != "centos" ]]; then - _pkg="" - elif [[ "$1" == "tigervnc-server" && "$ID" =~ ^(debian|ubuntu|linuxmint)$ ]]; then - _pkg="tigervnc-standalone-server" - else - _pkg="$1" - fi - } - - - ####################################### - # Get our MC working version from input argument or scraping Interact - # Requires: - # _boardurl - # _installPackage - # Globals: - # _mcversion - # _mversion - ####################################### - _setVersion() { - - debug "Running: ${FUNCNAME[0]}" - - if [[ ! -v _mcversion ]]; then - - _installPackage wget - - # Get latest version from Interact - echo "Scraping latest MC version number from Interact..." - if ! _mcversion=$(wget -qO- "$_boardurl" | grep -o "[0-9][0-9]\.[0-9]\.[0-9]\+" | head -n 1); then - err "MC version could not be scraped. Please specify a version manually using --mcversion or check your --boardurl" - _printHelpAndExit 1 - fi - fi - - echo "Using version: $_mcversion" - - # Extract major version number - _mversion="${_mcversion%%.*}" - - # Saving this substituion in case it's needed in the future - #_variation="${_mcversion##*.}" - } - - - ####################################### - # Check if an argument exists in an array - # Arguments: - # The first argument is the string to match to the following arguments - # Example: - # _inArray "zebra" "${animals_arr[@]}" - # Returns: - # 0 if a match is found, 1 if not - ####################################### - _inArray() { - - local _match="$1" - shift - - local _item - for _item in "$@"; do - [[ "$_item" == "$_match" ]] && return 0 - done - + else return 1 - } + fi +} - ####################################### - # Clean up nonsensical user input - # Notes: - # I try to keep this function as short as possible and provide better input options and - # sensible defaults than workarounds - # Test: - # _installJRMC should run sucessfully even without running _sanityChecks() - ####################################### - _sanityChecks() { - debug "Running: ${FUNCNAME[0]}" - - # Check for bad service name - _checkServices() { - - debug "Running: ${FUNCNAME[0]}" - - for _service in "${_services[@]}"; do - if ! _inArray "$_service" "${_available_services[@]}"; then - echo "Incorrect service type provided" - _printHelpAndExit 1 - fi - done - } +####################################### +# Prepend this to any command that you wish to execute with sudo +# Requires: +# _exec_user +####################################### +_ifSudo() { + if [[ "$_exec_user" != "root" ]]; then + sudo "$@" + else + "$@" + fi +} - _checkMCInstalled() { - debug "Running: ${FUNCNAME[0]}" +####################################### +# Parse CLI input from the user w/ getopt +####################################### +_parseInput() { + debug "Running: ${FUNCNAME[0]}" - for _service in "${_services[@]}"; do - if [[ "$_service" =~ ^(jriver-mediacenter|jriver-mediaserver|jriver-xvnc-mediacenter|jriver-x11vnc)$ ]] \ - && [[ ! -v _repoinstall && ! -v _rpminstall ]] \ - && [[ ! -x $(command -v "mediacenter$_mversion") ]]; then - err "You are attempting to install a service that depends on JRiver Media Center" - err "without installing JRiver Media Center" - err "Automatically enabling --repo-install" + if _input=$(getopt -o +vdhus:c: -l repo,rpm,rpmbuild,outputdir:,mcversion:,restorefile:,betapass:,service-user:,service:,version,debug,help,uninstall,createrepo,createrepo-webroot:,createrepo-user:,vncpass:,display:,container: -- "$@"); then + eval set -- "$_input" + while true; do + case "$1" in + --repo) _repoinstall="true" - fi - done - } + ;; + --rpm) + [[ ! "$ID" =~ ^(fedora|centos)$ ]] && \ + err "RPM installation not available on $ID" && \ + _printHelpAndExit 1 + _rpmbuild="true" + _rpminstall="true" + ;; + --rpmbuild) + _rpmbuild="true" + ;; + --outputdir) + shift && _outputdir="$1" + ;; + --mcversion) + shift && _mcversion="$1" + ;; + --restorefile) + shift && _restorefile="$1" + ;; + --betapass) + shift && _betapass="$1" + ;; + --service-user) + shift && _service_user="$1" + ;; + --service|-s) + shift && _services+=("$1") + ;; + --createrepo) + _rpmbuild="true" + _createrepo="true" + ;; + --createrepo-webroot) + shift && _createrepo_webroot="$1" + ;; + --createrepo-user) + shift && _createrepo_user="$1" + ;; + --vncpass) + shift && _vncpass="$1" + ;; + --display) + shift && _display="$1" + ;; + --container|-c) + shift && _containers+=("$1") + ;; + --version|-v) + echo "Version: $_scriptversion" + exit 0 + ;; + --debug|-d) + echo "Debugging on" + echo "installJRMC version: $_scriptversion" + _debug="true" + ;; + --help|-h) + _printHelpAndExit 0 + ;; + --uninstall|-u) + _uninstall="true" + ;; + --) + shift + break + ;; + esac + shift + done + else + err "Incorrect options provided" + _printHelpAndExit 1 + fi +} - _checkUser() { - debug "Running: ${FUNCNAME[0]}" +####################################### +# Get host OS from /etc/os-release +####################################### +_getOS() { + debug "Running: ${FUNCNAME[0]}" - if [[ "$_exec_user" == "root" && ! -v _service_user && "${_services[*]}" ]]; then - err "Attempting to install services as the root user" - err "This is not recommended and we are exiting now to prevent permission hell" - err "You can override this safety check by manually specifying --user root" + if [[ -e "/etc/os-release" ]]; then + source "/etc/os-release" + else + err "/etc/os-release not found" + err "Your OS is unsupported" + _printHelpAndExit 1 + fi + + debug "Platform: $ID $VERSION_ID" +} + + +####################################### +# Get our MC working version from input argument or scraping Interact +# Requires: +# _boardurl +####################################### +_setVersion() { + debug "Running: ${FUNCNAME[0]}" + + # If user does not specify a version, scrape the latest from Interact + if [[ ! -v _mcversion ]]; then + echo "Scraping latest MC version number from Interact..." + if ! _mcversion=$(wget -qO- "$_boardurl" | grep -o "[0-9][0-9]\.[0-9]\.[0-9]\+" | head -n 1); then + err "MC version could not be scraped. Please recheck the boardurl: $_boardurl or specify a version manually using --mcversion" + _printHelpAndExit 1 + fi + fi + + echo "Using version: $_mcversion" + + # Extract major version number + _mversion="${_mcversion%%.*}" + + # Saving this substituion in case it's needed in the future + #_variation="${_mcversion##*.}" +} + + +####################################### +# Installs a package using the system package manager +# Arguments: +# One or more package names +# Options: +# --noquery, -n: Do not query the package state (useful if installing a local RPM) +# Returns: +# Will exit 1 if failed +####################################### +_installPackage() { + debug "Running: ${FUNCNAME[0]}" "$@" + + if _input=$(getopt -o +n -l noquery -- "$@"); then + eval set -- "$_input" + while true; do + case "$1" in + --noquery|-n) + local _noquery="true" + ;; + --) + shift + break + ;; + esac + shift + done + else + err "Incorrect options provided" + _printHelpAndExit 1 + fi + + # We will add packages to this array if their command is not available + local -a _pkg_array + local -a _url_pkg_array + + # Parse packages + for _pkg in "$@"; do + [[ -v PKG_ALIASES && -v PKG_ALIASES["$_pkg"] ]] && _pkg=PKG_ALIASES["$_pkg"] + # Insert the package name to test if already installed + if [[ -v _noquery ]] || ! _pkg_query "$_pkg" > /dev/null 2>&1; then + if [[ -v _url_pkg ]]; then + _url_pkg_array+=("$_url_pkg") + else + _pkg_array+=("$_pkg") + fi + fi + done + + # Install from package name (with gpg check) + if [[ ${#_pkg_array[@]} -ge 1 ]]; then + echo "Installing:" "${_pkg_array[@]}" + if debug; then + if ! _pkg_install "${_pkg_array[@]}"; then + err "Failed to install package. Attempting to continue..." return 1 fi - } - - - # Enable/disable sanity checks - if _checkServices && _checkMCInstalled && _checkUser; then - debug "Sanity checks passed!" - return 0 - else - err "Sanity checks failed!" - exit 1 + elif ! _pkg_install "${_pkg_array[@]}" > /dev/null 2>&1; then + err "Failed to install package. Attempting to continue..." + return 1 fi - } + fi + + # Install from package url (without gpg check) + if [[ ${#_url_pkg_array[@]} -ge 1 ]]; then + echo "Installing:" "${_url_pkg_array[@]}" + if debug; then + if ! _pkg_install_nogpg "${_url_pkg_array[@]}"; then + err "Failed to install package. Attempting to continue..." + return 1 + fi + elif ! _pkg_install_nogpg "${_url_pkg_array[@]}" > /dev/null 2>&1; then + err "Failed to install package. Attempting to continue..." + return 1 + fi + fi +} - ####################################### - # Installs JRiver Media Center from a repository - # Returns: - # 0 if JRiver Media Center installed sucessfully - ####################################### - _installMCFromRepo() { +####################################### +# Installs JRiver Media Center from a repository +# Returns: +# 0 if JRiver Media Center installed sucessfully +####################################### +_installMCFromRepo() { + debug "Running: ${FUNCNAME[0]}" + + echo "Installing JRiver Media Center from repo..." + + ! debug && \ + echo "This may take a few minutes to complete"; \ + echo "Use --debug for verbose output" + + local _mcpkg + + # Add repository files + _addRepo() { debug "Running: ${FUNCNAME[0]}" - echo "Installing JRiver Media Center from repo..." - [[ ! -v _debug ]] && \ - echo "This may take a few minutes"; \ - echo "Use --debug for verbose output" - - local _mcpkg - - # Add repository files - _addRepo() { - debug "Running: ${FUNCNAME[0]}" - # Add repositories to OS-specific package managers - if [[ "$ID" =~ ^(fedora|centos)$ ]]; then - _bash_cmd 'cat <<- EOF > /etc/yum.repos.d/jriver.repo - [jriver] - name=JRiver Media Center repo by BryanC - baseurl=https://repos.bryanroessler.com/jriver - gpgcheck=0 - EOF' - elif [[ "$ID" =~ ^(debian|ubuntu|linuxmint)$ ]]; then - _installPackage wget - wget -q "http://dist.jriver.com/mediacenter@jriver.com.gpg.key" -O- | _ifSudo apt-key add - > /dev/null 2>&1 - _bash_cmd 'cat <<- EOF > /etc/apt/sources.list.d/jriver.list - deb [arch=amd64,i386,armhf] http://dist.jriver.com/latest/mediacenter/ buster main - EOF' - fi - } - _addRepo - - # Update package list - debug "Updating package list" - if ! _pkg_update > /dev/null 2>&1; then - err "Package update failed!" - exit 1 - fi - - # If user specifies a version, use that - if [[ -v _mcversion ]]; then - _setVersion - local _specific_version="true" - fi - - local _mcpkg - - # Fedora/CentOS use a universal package name -- easy if [[ "$ID" =~ ^(fedora|centos)$ ]]; then - _mcpkg="MediaCenter" + _bash_cmd 'cat <<- EOF > /etc/yum.repos.d/jriver.repo + [jriver] + name=JRiver Media Center repo by BryanC + baseurl=https://repos.bryanroessler.com/jriver + gpgcheck=0 + EOF' + elif [[ "$ID" =~ ^(debian|ubuntu|linuxmint)$ ]]; then + _installPackage wget + wget -q "http://dist.jriver.com/mediacenter@jriver.com.gpg.key" -O- | _ifSudo apt-key add - > /dev/null 2>&1 + _bash_cmd 'cat <<- EOF > /etc/apt/sources.list.d/jriver.list + deb [arch=amd64,i386,armhf] http://dist.jriver.com/latest/mediacenter/ buster main + EOF' fi + } + _addRepo - # Ubuntu/Debian incorporate the mversion into the package name -- more fun! - if [[ "$ID" =~ ^(debian|ubuntu|linuxmint)$ ]]; then - if [[ ! -v _mversion ]]; then - # Try parsing the latest mversion from the repo - if _mcpkg=$(apt-get install mediacenter?? -s -q0 | grep "selecting" | tail -1| awk '{print $3}'); then - _mcpkg="${_mcpkg%\'}" - _mcpkg="${_mcpkg#\'}" - # Scrape Interact - else - _setVersion - _mcpkg="mediacenter$_mversion" - fi + # Update package list + debug "Updating package list" + if ! _pkg_update > /dev/null 2>&1; then + err "Package update failed!" + exit 1 + fi + + local _mcpkg + + # Fedora/CentOS use a universal package name -- easy + if [[ "$ID" =~ ^(fedora|centos)$ ]]; then + _mcpkg="MediaCenter" + # Ubuntu/Debian incorporate the mversion into the package name -- more fun! + elif [[ "$ID" =~ ^(debian|ubuntu|linuxmint)$ ]]; then + if [[ ! -v _mversion ]]; then + # Try parsing the latest mversion from the repo + if _mcpkg=$(apt-get install mediacenter?? -s -q0 | grep "selecting" | tail -1| awk '{print $3}'); then + _mcpkg="${_mcpkg%\'}" + _mcpkg="${_mcpkg#\'}" + # Scrape Interact else _mcpkg="mediacenter$_mversion" fi - # Sanity check - if [[ ! "$_mcpkg" =~ ^mediacenter[0-9][0-9]$ ]]; then - err "Could not parse MC package name" - exit 1 - fi - fi - - if [[ -v _specific_version ]]; then - if [[ "$ID" =~ ^(fedora|centos)$ ]]; then - if [[ -v _debug ]]; then - _installPackage "$_mcpkg-$_mcversion" - else - _installPackage "$_mcpkg-$_mcversion" > /dev/null 2>&1 - fi - elif [[ "$ID" =~ ^(debian|ubuntu|linuxmint)$ ]]; then - if [[ -v _debug ]]; then - _installPackage "$_mcpkg=$_mcversion" - else - _installPackage "$_mcpkg=$_mcversion" > /dev/null 2>&1 - fi - fi else - if [[ -v _debug ]]; then - _installPackage "$_mcpkg" - else - _installPackage "$_mcpkg" > /dev/null 2>&1 - fi + _mcpkg="mediacenter$_mversion" fi - - # shellcheck disable=SC2181 - # Rationale: More compact to check this once - if [[ $? -eq 0 ]]; then - echo "JRiver Media Center installed successfully" - return 0 - else - err "JRiver Media Center installation failed" + # Sanity check + if [[ ! "$_mcpkg" =~ ^mediacenter[0-9][0-9]$ ]]; then + err "Could not parse MC package name" exit 1 fi - } + fi - - ####################################### - # Acquire the source DEB package from JRiver's servers - # Returns: - # 0 if DEB file downloaded successfully, 1 if failed - ####################################### - _acquireDeb() { - debug "Running: ${FUNCNAME[0]}" - - local _debfilename="$_outputdir/SOURCES/MediaCenter-${_mcversion}-amd64.deb" - - # If necessary, create SOURCES dir - [[ ! -d "$_outputdir/SOURCES" ]] && mkdir -p "$_outputdir/SOURCES" - - # If deb file already exists, skip download - if [[ -f "$_debfilename" ]]; then - echo "Using local DEB file: $_debfilename" - # Else check beta repo - elif [[ -v _betapass ]]; then - if wget -q -O "$_debfilename" \ - "https://files.jriver.com/mediacenter/channels/v${_mversion}/beta/${_betapass}/MediaCenter-${_mcversion}-amd64.deb"; then - true - fi - # Else check test repo - elif wget -q -O "$_debfilename" \ - "https://files.jriver.com/mediacenter/test/MediaCenter-${_mcversion}-amd64.deb"; then - true - # Else check latest repo - elif wget -q -O "$_debfilename" \ - "https://files.jriver.com/mediacenter/channels/v${_mversion}/latest/MediaCenter-${_mcversion}-amd64.deb"; then - true - else - err "Cannot find DEB file. Exiting..." - exit 1 - fi - - if [[ ! -f "$_debfilename" ]]; then - err "Downloaded DEB file missing or corrupted, exiting..." - exit 1 - fi - } - - - ####################################### - # Creates a SPEC file and builds the RPM from the source DEB using rpmbuild - # Requires: - # _outputdir - # ID - # _mcversion - # _mversion - # _installPackage - # Globals: - # _mcrpm - # Returns: - # 0 if rpmbuild is successful, 1 if not - ####################################### - _buildRPM() { - debug "Running: ${FUNCNAME[0]}" - - # install build dependencies - _installPackage wget dpkg rpm-build - - # If necessary, make build directories - [[ ! -d "$_outputdir/SPECS" ]] && mkdir -p "$_outputdir/SPECS" - - # rpmbuild uses rpm to check for build dependencies - # this will fail on non-rpm distros + if [[ -v _specific_version ]]; then if [[ "$ID" =~ ^(fedora|centos)$ ]]; then - local _build_requires=$'BuildRequires: rpm >= 4.11.0\nBuildRequires: dpkg' - fi - - # Create spec file - cat <<- EOF > "$_outputdir/SPECS/mediacenter.spec" - Name: MediaCenter - Version: $_mcversion - Release: 1 - Summary: JRiver Media Center - Group: Applications/Media - Source0: http://files.jriver.com/mediacenter/channels/v$_mversion/latest/MediaCenter-$_mcversion-amd64.deb - ${_build_requires:-} - BuildArch: x86_64 - %define _rpmfilename %%{ARCH}/%%{NAME}-%%{version}.%%{ARCH}.rpm - - AutoReq: 0 - Requires: glibc >= 2.19 - Requires: alsa-lib >= 1.0.28 - Requires: libuuid >= 2.25 - Requires: libX11 >= 1.6 - Requires: libX11-common >= 1.6 - Requires: libXext >= 1.3 - Requires: libxcb >= 1.1 - Requires: libXdmcp >= 1.1 - Requires: libstdc++ >= 4.9 - Requires: gtk3 >= 3.14 - Requires: mesa-libGL - Requires: libglvnd-glx - Requires: pango >= 1.36 - Requires: libXScrnSaver - Requires: xdg-utils - Requires: libgomp >= 4.9 - Requires: nss >= 3.26 - Requires: nspr >= 4.12 - Requires: ca-certificates - Requires: python3 - Recommends: vorbis-tools >= 1.4.0 - Recommends: lame >= 3.0 - - Provides: mediacenter$_mversion - - License: Copyright 1998-2020, JRiver, Inc. All rights reserved. Protected by U.S. patents #7076468 and #7062468 - URL: http://www.jriver.com/ - - %define __provides_exclude_from ^%{_libdir}/jriver/.*/.*\\.so.*$ - - %description - Media Center is more than a world class player. - - %global __os_install_post %{nil} - %prep - - %build - - %install - dpkg -x %{S:0} %{buildroot} - - %post -p /sbin/ldconfig - %postun -p /sbin/ldconfig - - %files - %{_bindir}/mediacenter$_mversion - %{_libdir}/jriver - %{_datadir} - %exclude %{_datadir}/applications/media_center_packageinstaller_$_mversion.desktop - /etc/security/limits.d/* - EOF - - declare -g _mcrpm="$_outputdir/RPMS/x86_64/MediaCenter-$_mcversion.x86_64.rpm" - - # skip rebuilding the rpm if it already exists - if [[ -f "$_mcrpm" ]]; then - echo "$_mcrpm already exists. Skipping build step..." - return 0 - else - # Run rpmbuild - echo "Building version $_mcversion, please wait..." - if [[ -v _debug ]]; then - rpmbuild --define="%_topdir $_outputdir" --define="%_libdir /usr/lib" -bb "$_outputdir/SPECS/mediacenter.spec" + if debug; then + _installPackage "$_mcpkg-$_mcversion" else - rpmbuild --quiet --define="%_topdir $_outputdir" --define="%_libdir /usr/lib" -bb "$_outputdir/SPECS/mediacenter.spec" > /dev/null 2>&1 - fi - - # Check return code - # shellcheck disable=SC2181 - if [[ $? -ne 0 || ! -f "$_mcrpm" ]]; then - err "Build failed. Exiting..." - exit 1 - else - echo "Build successful. The RPM file is located at: $_mcrpm" - fi - fi - } - - - ####################################### - # Copy the RPM to createrepo-webroot and runs createrepo as the createrepo-user - # Arguments: - # Requires one argument, the path to the RPM file (typically _mcrpm) - # Requires: - # _createrepo_webroot - # Returns: - # 0 if createrepo is successful, 1 if not - ####################################### - _runCreaterepo() { - debug "Running: ${FUNCNAME[0]}" - - _installPackage createrepo_c - - local _rpmfile="$1" - - # If the webroot does not exist, create it - if [[ ! -d "$_createrepo_webroot" ]]; then - if ! _cr_mkdir_cmd "$_createrepo_webroot"; then - err "Could not create the createrepo-webroot path!" - err "Make sure that the createrepo-webroot is writeable by createrepo-user" - return 1 - fi - fi - - # Copy built rpms to webroot - if ! _cr_cp_cmd -f "$_rpmfile" "$_createrepo_webroot"; then - err "Could not copy the RPM to the createrepo-webroot path" - err "Make sure that the createrepo-webroot path is writeable by createrepo-user" - return 1 - fi - - # Run createrepo - if _createrepo_cmd "$_createrepo_webroot"; then - echo "Successfully updated repo" - return 0 - else - err "Update repo failed" - return 1 - fi - } - - - ####################################### - # Symlink certificates where JRiver Media Center expects them to be on Fedora/CentOS - # Returns: - # 0 if symlinking is unecessary or successful, 1 if not - ####################################### - _symlinkCerts() { - debug "Running: ${FUNCNAME[0]}" - - if [[ ! -f /etc/ssl/certs/ca-certificates.crt && \ - -f /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem ]]; then - if ! _ln_cmd /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem /etc/ssl/certs/ca-certificates.crt; then - err "Symlinking certificates failed" - return 1 - fi - fi - } - - - ####################################### - # Automatically restore the mjr license file if it is found next to installJRMC or _restorefile - # is set - # Requires: - # _restorefile OR _basedir - # _mversion - # Returns: - # 0 if license restored successfully or skipped, 1 if unsuccessful - ####################################### - _restoreLicense() { - debug "Running: ${FUNCNAME[0]}" - - local _mjr - - # Allow user to drop an mjr file next to installJRMC - if [[ ! -v _restorefile ]]; then - for _mjr in "$_basedir"/*.mjr; do - [[ $_mjr -nt $_restorefile ]] && _restorefile="$_mjr" - done - fi - - # Restore license - if [[ -f "$_restorefile" ]]; then - if ! "mediacenter${_mversion}" /RestoreFromFile "$_restorefile"; then - err "Automatic license restore failed" - return 1 - fi - fi - } - - - ####################################### - # Opens ports using the system firewall tool - # Arguments - # Takes one argument, the pre-specified name of the service to enable - # Requires: - # ID - # _bash_cmd - # _firewall_cmd - # _port - # Returns: - # 0 if ports opened sucessfully, 1 if not - ####################################### - _openFirewall() { - debug "Running: ${FUNCNAME[0]}" "$@" - - # Create OS-specific port rules based on argument (service) name - local -a _f_ports # for firewall-cmd - local _u_ports # for ufw - if [[ "$1" == "jriver" ]]; then - _f_ports=("52100-52200/tcp" "1900/udp") - _u_ports="52100:52200/tcp|1900/udp" - elif [[ "$1" =~ ^(jriver-x11vnc|jriver-xvnc)$ ]]; then - _f_ports=("$_port/tcp") - _u_ports="$_port/tcp" - else - err "_openFirewall unrecognized service name" - exit 1 - fi - - # Open the ports - if [[ "$ID" =~ ^(fedora|centos)$ ]]; then - [[ ! -x $(command -v firewall-cmd) ]] && _installPackage firewalld - if ! _firewall_cmd --get-services | grep -q "$1"; then - _firewall_cmd --permanent --new-service="$1" > /dev/null 2>&1 - _firewall_cmd --permanent --service="$1" --set-description="$1 installed by installJRMC" > /dev/null 2>&1 - _firewall_cmd --permanent --service="$1" --set-short="$1" > /dev/null 2>&1 - for _f_port in "${_f_ports[@]}"; do - _firewall_cmd --permanent --service="$1" --add-port="$_f_port" > /dev/null 2>&1 - done - _firewall_cmd --add-service "$1" --permanent > /dev/null 2>&1 - _firewall_cmd --reload > /dev/null 2>&1 + _installPackage "$_mcpkg-$_mcversion" > /dev/null 2>&1 fi elif [[ "$ID" =~ ^(debian|ubuntu|linuxmint)$ ]]; then - # Debian ufw package state is broken on fresh installations - [[ ! -x $(command -v ufw) ]] && _installPackage ufw - if [[ ! -f "/etc/ufw/applications.d/$1" ]]; then - _bash_cmd "cat <<- EOF > /etc/ufw/applications.d/$1 - [$1] - title=$1 - description=$1 installed by installJRMC - ports=$_u_ports - EOF" + if debug; then + _installPackage "$_mcpkg=$_mcversion" + else + _installPackage "$_mcpkg=$_mcversion" > /dev/null 2>&1 fi - _firewall_cmd app update "$1" - _firewall_cmd allow "$1" > /dev/null 2>&1 fi + else + if debug; then + _installPackage "$_mcpkg" + else + _installPackage "$_mcpkg" > /dev/null 2>&1 + fi + fi - # shellcheck disable=SC2181 - # Rationale: much more concise to check exit codes at the end - if [[ $? -ne 0 ]]; then - err "Firewall ports could not be opened" + # shellcheck disable=SC2181 + if [[ $? -ne 0 ]]; then + err "JRiver Media Center installation failed" + exit 1 + fi + + echo "JRiver Media Center installed successfully" +} + + +####################################### +# Acquire the source DEB package from JRiver's servers +# Returns: +# 0 if DEB file downloaded successfully, 1 if failed +####################################### +_acquireDeb() { + debug "Running: ${FUNCNAME[0]}" + + local _debfilename="$_outputdir/SOURCES/MediaCenter-$_mcversion-amd64.deb" + + # If necessary, create SOURCES dir + [[ ! -d "$_outputdir/SOURCES" ]] && mkdir -p "$_outputdir/SOURCES" + + # If deb file already exists, skip download + if [[ -f "$_debfilename" ]]; then + echo "Using local DEB file: $_debfilename" + # Else check beta repo + elif [[ -v _betapass ]]; then + if wget -q -O "$_debfilename" \ + "https://files.jriver.com/mediacenter/channels/v$_mversion/beta/$_betapass/MediaCenter-$_mcversion-amd64.deb"; then + true + fi + # Else check test repo + elif wget -q -O "$_debfilename" \ + "https://files.jriver.com/mediacenter/test/MediaCenter-$_mcversion-amd64.deb"; then + true + # Else check latest repo + elif wget -q -O "$_debfilename" \ + "https://files.jriver.com/mediacenter/channels/v$_mversion/latest/MediaCenter-$_mcversion-amd64.deb"; then + true + else + err "Cannot find DEB file. Exiting..." + exit 1 + fi + + if [[ ! -f "$_debfilename" ]]; then + err "Downloaded DEB file missing or corrupted, exiting..." + exit 1 + fi +} + + +####################################### +# Creates a SPEC file and builds the RPM from the source DEB using rpmbuild +# Requires: +# _outputdir +# ID +# _mcversion +# _mversion +# _installPackage +# Globals: +# _mcrpm +# Returns: +# 0 if rpmbuild is successful, 1 if not +####################################### +_buildRPM() { + debug "Running: ${FUNCNAME[0]}" + + # install build dependencies + _installPackage wget dpkg rpm-build + + # If necessary, make build directories + [[ ! -d "$_outputdir/SPECS" ]] && mkdir -p "$_outputdir/SPECS" + + # rpmbuild uses rpm to check for build dependencies + # this will fail on non-rpm distros + if [[ "$ID" =~ ^(fedora|centos)$ ]]; then + local _build_requires=$'BuildRequires: rpm >= 4.11.0\nBuildRequires: dpkg' + fi + + # Create spec file + cat <<- EOF > "$_outputdir/SPECS/mediacenter.spec" + Name: MediaCenter + Version: $_mcversion + Release: 1 + Summary: JRiver Media Center + Group: Applications/Media + Source0: http://files.jriver.com/mediacenter/channels/v$_mversion/latest/MediaCenter-$_mcversion-amd64.deb + ${_build_requires:-} + BuildArch: x86_64 + %define _rpmfilename %%{ARCH}/%%{NAME}-%%{version}.%%{ARCH}.rpm + + AutoReq: 0 + Requires: glibc >= 2.19 + Requires: alsa-lib >= 1.0.28 + Requires: libuuid >= 2.25 + Requires: libX11 >= 1.6 + Requires: libX11-common >= 1.6 + Requires: libXext >= 1.3 + Requires: libxcb >= 1.1 + Requires: libXdmcp >= 1.1 + Requires: libstdc++ >= 4.9 + Requires: gtk3 >= 3.14 + Requires: mesa-libGL + Requires: libglvnd-glx + Requires: pango >= 1.36 + Requires: libXScrnSaver + Requires: xdg-utils + Requires: libgomp >= 4.9 + Requires: nss >= 3.26 + Requires: nspr >= 4.12 + Requires: ca-certificates + Requires: python3 + Recommends: vorbis-tools >= 1.4.0 + Recommends: lame >= 3.0 + + Provides: mediacenter$_mversion + + License: Copyright 1998-2021, JRiver, Inc. All rights reserved. Protected by U.S. patents #7076468 and #7062468 + URL: http://www.jriver.com/ + + %define __provides_exclude_from ^%{_libdir}/jriver/.*/.*\\.so.*$ + + %description + Media Center is more than a world class player. + + %global __os_install_post %{nil} + %prep + + %build + + %install + dpkg -x %{S:0} %{buildroot} + + %post -p /sbin/ldconfig + %postun -p /sbin/ldconfig + + %files + %{_bindir}/mediacenter$_mversion + %{_libdir}/jriver + %{_datadir} + %exclude %{_datadir}/applications/media_center_packageinstaller_$_mversion.desktop + /etc/security/limits.d/* + EOF + + declare -g _mcrpm="$_outputdir/RPMS/x86_64/MediaCenter-$_mcversion.x86_64.rpm" + + # skip rebuilding the rpm if it already exists + if [[ -f "$_mcrpm" ]]; then + echo "$_mcrpm already exists. Skipping build step..." + return 0 + fi + + # Run rpmbuild + echo "Building version $_mcversion, please wait..." + if debug; then + rpmbuild --define="%_topdir $_outputdir" --define="%_libdir /usr/lib" -bb "$_outputdir/SPECS/mediacenter.spec" + else + rpmbuild --quiet --define="%_topdir $_outputdir" --define="%_libdir /usr/lib" -bb "$_outputdir/SPECS/mediacenter.spec" > /dev/null 2>&1 + fi + + # Check return code + # shellcheck disable=SC2181 + if [[ $? -ne 0 || ! -f "$_mcrpm" ]]; then + err "Build failed. Exiting..." + exit 1 + else + echo "Build successful. The RPM file is located at: $_mcrpm" + fi +} + + +####################################### +# Copy the RPM to createrepo-webroot and runs createrepo as the createrepo-user +# Arguments: +# Requires one argument, the path to the RPM file (typically _mcrpm) +# Requires: +# _createrepo_webroot +# Returns: +# 0 if createrepo is successful, 1 if not +####################################### +_runCreaterepo() { + debug "Running: ${FUNCNAME[0]}" + + # Some additional commands specifically for createrepo (primarily to handle user rights) + if [[ $_createrepo_user != "root" ]]; then + if [[ -d "$_createrepo_webroot/repodata" ]]; then + _createrepo_cmd(){ sudo -u "$_createrepo_user" createrepo -q --update "$@"; } + else + _createrepo_cmd(){ sudo -u "$_createrepo_user" createrepo -q "$@"; } + fi + _cr_mkdir_cmd(){ sudo -u "$_createrepo_user" mkdir -p "$@"; } + _cr_cp_cmd(){ sudo -u "$_createrepo_user" cp -n "$@"; } + else + if [[ -d "$_createrepo_webroot/repodata" ]]; then + _createrepo_cmd(){ createrepo -q --update "$@"; } + else + _createrepo_cmd(){ createrepo -q "$@"; } + fi + fi + + _installPackage createrepo_c + + local _rpmfile="$1" + + # If the webroot does not exist, create it + if [[ ! -d "$_createrepo_webroot" ]]; then + if ! _cr_mkdir_cmd "$_createrepo_webroot"; then + err "Could not create the createrepo-webroot path!" + err "Make sure that the createrepo-webroot is writeable by createrepo-user: $_createrepo_user" return 1 fi - } + fi + + # Copy built rpms to webroot + if ! _cr_cp_cmd -f "$_rpmfile" "$_createrepo_webroot"; then + err "Could not copy the RPM to the createrepo-webroot path" + err "Make sure that the createrepo-webroot path is writeable by createrepo-user: $_createrepo_user" + return 1 + fi + + # Run createrepo + if _createrepo_cmd "$_createrepo_webroot"; then + echo "Successfully updated repo" + return 0 + else + err "Update repo failed" + return 1 + fi +} - ####################################### - # Create the x11vnc password file - # Globals: - # _novncauth - # Returns: - # 0 if password created sucessfully, 1 if not - ####################################### - _setX11VNCPass() { - debug "Running: ${FUNCNAME[0]}" +####################################### +# Symlink certificates where JRiver Media Center expects them to be on Fedora/CentOS +# Returns: +# 0 if symlinking is unecessary or successful, 1 if not +####################################### +_symlinkCerts() { + debug "Running: ${FUNCNAME[0]}" - _vncpassfile="$HOME/.vnc/jrmc_passwd" - - [[ ! -d "${_vncpassfile%/*}" ]] && mkdir -p "${_vncpassfile%/*}" - - if [[ -f "$_vncpassfile" ]]; then - if [[ ! -v _vncpass ]]; then - err "Refusing to overwrite existing $_vncpassfile with an empty password" - err "Remove existing $_vncpassfile or set --vncpass to use an empty password" - exit 1 - else - rm -f "$_vncpassfile" - fi + if [[ ! -f /etc/ssl/certs/ca-certificates.crt && \ + -f /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem ]]; then + if ! _ln_cmd /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem /etc/ssl/certs/ca-certificates.crt; then + err "Symlinking certificates failed" + return 1 fi + fi +} - if [[ -v _vncpass ]]; then - if ! x11vnc -storepasswd "$_vncpass" "$_vncpassfile"; then - err "Could not create VNC password file" - return 1 - fi + +####################################### +# Automatically restore the mjr license file if it is found next to installJRMC or _restorefile +# is set +# Requires: +# _restorefile OR _basedir +# _mversion +# Returns: +# 0 if license restored successfully or skipped, 1 if unsuccessful +####################################### +_restoreLicense() { + debug "Running: ${FUNCNAME[0]}" + + local _mjr + + # Allow user to drop an mjr file next to installJRMC + if [[ ! -v _restorefile ]]; then + for _mjr in "$PWD"/*.mjr; do + [[ $_mjr -nt $_restorefile ]] && _restorefile="$_mjr" + done + fi + + # Restore license + if [[ -f "$_restorefile" ]]; then + if ! "mediacenter$_mversion" /RestoreFromFile "$_restorefile"; then + err "Automatic license restore failed" + return 1 + fi + fi +} + + +####################################### +# Opens ports using the system firewall tool +# Arguments +# Service to enable (pre-defined) +# Requires: +# ID +# _bash_cmd +# _firewall_cmd +# _port +# Returns: +# 0 if ports opened sucessfully, 1 if not +####################################### +_openFirewall() { + debug "Running: ${FUNCNAME[0]}" "$@" + + # Create OS-specific port rules based on argument (service) name + local -a _f_ports # for firewall-cmd + local _u_ports # for ufw + if [[ "$1" == "jriver" ]]; then + _f_ports=("52100-52200/tcp" "1900/udp") + _u_ports="52100:52200/tcp|1900/udp" + elif [[ "$1" =~ ^(jriver-x11vnc|jriver-xvnc)$ ]]; then + _f_ports=("$_port/tcp" "1900/udp") + _u_ports="$_port/tcp|1900/udp" + fi + + # Open the ports + if [[ "$ID" =~ ^(fedora|centos)$ ]]; then + [[ ! -x $(command -v firewall-cmd) ]] && _installPackage firewalld + if ! _firewall_cmd --get-services | grep -q "$1"; then + _firewall_cmd --permanent --new-service="$1" > /dev/null 2>&1 + _firewall_cmd --permanent --service="$1" --set-description="$1 installed by installJRMC" > /dev/null 2>&1 + _firewall_cmd --permanent --service="$1" --set-short="$1" > /dev/null 2>&1 + for _f_port in "${_f_ports[@]}"; do + _firewall_cmd --permanent --service="$1" --add-port="$_f_port" > /dev/null 2>&1 + done + _firewall_cmd --add-service "$1" --permanent > /dev/null 2>&1 + _firewall_cmd --reload > /dev/null 2>&1 + fi + elif [[ "$ID" =~ ^(debian|ubuntu|linuxmint)$ ]]; then + # Debian ufw package state is broken on fresh installations + [[ ! -x $(command -v ufw) ]] && _installPackage ufw + if [[ ! -f "/etc/ufw/applications.d/$1" ]]; then + _bash_cmd "cat <<- EOF > /etc/ufw/applications.d/$1 + [$1] + title=$1 + description=$1 installed by installJRMC + ports=$_u_ports + EOF" + fi + _firewall_cmd app update "$1" + _firewall_cmd allow "$1" > /dev/null 2>&1 + fi + + # shellcheck disable=SC2181 + # Rationale: much more concise to check exit codes at the end + if [[ $? -ne 0 ]]; then + err "Firewall ports could not be opened" + return 1 + fi +} + + +####################################### +# Create the x11vnc password file +# Globals: +# _novncauth +# Returns: +# 0 if password created sucessfully, 1 if not +####################################### +_setX11VNCPass() { + debug "Running: ${FUNCNAME[0]}" + + _vncpassfile="$HOME/.vnc/jrmc_passwd" + + [[ ! -d "${_vncpassfile%/*}" ]] && mkdir -p "${_vncpassfile%/*}" + + if [[ -f "$_vncpassfile" ]]; then + if [[ ! -v _vncpass ]]; then + err "Refusing to overwrite existing $_vncpassfile with an empty password" + err "Remove existing $_vncpassfile or set --vncpass to use an empty password" + exit 1 else - _novncauth="true" + rm -f "$_vncpassfile" fi - } + fi - - ####################################### - # Create the Xvnc password file - # Returns: - # 0 if password created sucessfully, 1 if not - ####################################### - _setVNCPass() { - debug "Running: ${FUNCNAME[0]}" - - _vncpassfile="$HOME/.vnc/jrmc_passwd" - - [[ ! -d "${_vncpassfile%/*}" ]] && mkdir -p "${_vncpassfile%/*}" - - if [[ -f "$_vncpassfile" ]]; then - if [[ ! -v _vncpass ]]; then - err "Refusing to overwrite existing $_vncpassfile with an empty password" - err "Remove existing $_vncpassfile or set --vncpass to use an empty password" - exit 1 - else - rm -f "$_vncpassfile" - fi + if [[ -v _vncpass ]]; then + if ! x11vnc -storepasswd "$_vncpass" "$_vncpassfile"; then + err "Could not create VNC password file" + return 1 fi + else + _novncauth="true" + fi +} - if [[ -v _vncpass ]]; then - if ! echo "$_vncpass" | vncpasswd -f > "$_vncpassfile"; then - err "Could not create VNC password file" - return 1 - fi + +####################################### +# Create the Xvnc password file +# Returns: +# 0 if password created sucessfully, 1 if not +####################################### +_setVNCPass() { + debug "Running: ${FUNCNAME[0]}" + + _vncpassfile="$HOME/.vnc/jrmc_passwd" + + [[ ! -d "${_vncpassfile%/*}" ]] && mkdir -p "${_vncpassfile%/*}" + + if [[ -f "$_vncpassfile" ]]; then + if [[ ! -v _vncpass ]]; then + err "Refusing to overwrite existing $_vncpassfile with an empty password" + err "Remove existing $_vncpassfile or set --vncpass to use an empty password" + exit 1 else - _novncauth="true" + rm -f "$_vncpassfile" fi - } + fi - - ####################################### - # Set display and port variables - # Globals: - # _display - # _displaynum - # _next_display - # _next_displaynum - ####################################### - _setDisplay() { - debug "Running: ${FUNCNAME[0]}" - - # Check _display, else DISPLAY, else set to :0 by default - if [[ -v _display ]]; then - _next_display="$_display" - elif [[ -v DISPLAY ]]; then - _display="${DISPLAY}" - _displaynum="${_display#:}" # strip colon - _displaynum="${_displaynum%.*}" # strip suffix - _next_displaynum=$(( _displaynum + 1 )) - _next_display=":$_next_displaynum" - else - _display=":0" - _next_display=":1" + if [[ -v _vncpass ]]; then + if ! echo "$_vncpass" | vncpasswd -f > "$_vncpassfile"; then + err "Could not create VNC password file" + return 1 fi + else + _novncauth="true" + fi +} + +####################################### +# Set display and port variables +# Globals: +# _display +# _displaynum +# _next_display +# _next_displaynum +####################################### +_setDisplay() { + debug "Running: ${FUNCNAME[0]}" + + # Check _display, else DISPLAY, else set to :0 by default + if [[ -v _display ]]; then + _next_display="$_display" + elif [[ -v DISPLAY ]]; then + _display="${DISPLAY}" _displaynum="${_display#:}" # strip colon _displaynum="${_displaynum%.*}" # strip suffix _next_displaynum=$(( _displaynum + 1 )) - } - - - ####################################### - # Create associated service variables based on service name - # Requires: - # _service_user - # Globals: - # _service_fname - # _timer_fname - # _service_name - # _timer_name - # _user_specifier - ####################################### - _servicePrep() { - debug "Running: ${FUNCNAME[0]}" - - if [[ "$_service_user" == "root" ]]; then - _service_fname="/usr/lib/systemd/system/${1}.service" - _timer_fname="/usr/lib/systemd/system/${1}.timer" - _service_name="jriver-${1}.service" - _timer_name="jriver-${1}}.timer" - _user_specifier="" - else - _service_fname="/usr/lib/systemd/system/${1}@.service" - _timer_fname="/usr/lib/systemd/system/${1}@.timer" - _service_name="${1}@$_service_user.service" - _timer_name="${1}@$_service_user.timer" - _user_specifier="User=%I" - fi - } - - - ####################################### - # SERVICES - ####################################### - - ####################################### - # Starts and enables (at startup) a JRiver Media Center service - # Arguments: - # Passes arguments as startup options to /usr/bin/mediacenter26 - # Requires: - # XAUTHORITY - ####################################### - _service_jriver-mediacenter() { - debug "Running: ${FUNCNAME[0]}" - - _bash_cmd "cat <<- EOF > $_service_fname - [Unit] - Description=JRiver Media Center $_mversion - After=graphical.target - - [Service] - $_user_specifier - Type=simple - Environment=DISPLAY=$_display - Environment=XAUTHORITY=$XAUTHORITY - ExecStart=/usr/bin/mediacenter$_mversion $* - Restart=always - RestartSec=10 - KillSignal=SIGHUP - TimeoutStopSec=30 - - [Install] - WantedBy=graphical.target - EOF" - _systemctl_reload && \ - _systemctl_start "$_service_name" && \ - _systemctl_enable "$_service_name" && \ - _openFirewall "jriver" - } - - - ####################################### - # Starts and enables (at startup) a JRiver Media Server service - ####################################### - _service_jriver-mediaserver() { - debug "Running: ${FUNCNAME[0]}" - - _service_jriver-mediacenter "/MediaServer" - } - - - ####################################### - # Starts and enables (at startup) JRiver Media Center in a new Xvnc session - ####################################### - _service_jriver-xvnc-mediacenter() { - debug "Running: ${FUNCNAME[0]}" - - _installPackage tigervnc-server - - _setVNCPass - - local _port=$(( _next_displaynum + 5900 )) - - if [[ -v _novncauth ]]; then - _exec_start_cmd="/usr/bin/vncserver $_next_display -geometry 1440x900 -alwaysshared -name jriver$_next_display -SecurityTypes None -autokill -xstartup /usr/bin/mediacenter$_mversion" - else - _exec_start_cmd="/usr/bin/vncserver $_next_display -geometry 1440x900 -alwaysshared -rfbauth $HOME/.vnc/jrmc_passwd -autokill -xstartup /usr/bin/mediacenter$_mversion" - fi - - _bash_cmd "cat <<- EOF > $_service_fname - [Unit] - Description=Remote desktop service (VNC) - After=syslog.target network.target - - [Service] - Type=simple - $_user_specifier - ExecStartPre=/bin/sh -c '/usr/bin/vncserver -kill $_next_display > /dev/null 2>&1 || :' - ExecStart=$_exec_start_cmd - ExecStop=/usr/bin/vncserver -kill $_next_display - Restart=always - - [Install] - WantedBy=multi-user.target - EOF" - - _systemctl_reload && \ - _systemctl_start "$_service_name" && \ - _systemctl_enable "$_service_name" && \ - echo "Xvnc running on localhost:$_port" && \ - _openFirewall "jriver-xvnc" && \ - _openFirewall "jriver" - } - - - ####################################### - # Starts and enables (at startup) JRiver Media Server and x11vnc sharing the local desktop - ####################################### - _service_jriver-x11vnc() { - debug "Running: ${FUNCNAME[0]}" - - _installPackage x11vnc - - _setX11VNCPass - - local _port=$(( _displaynum + 5900 )) - - # Get current desktop resolution - # TODO: may need to break this out into its own function and get smarter at identifying multi-monitors - _getResolution() { - - debug "Running: ${FUNCNAME[0]}" - - _installPackage xorg-x11-utils - _res=$(xdpyinfo | grep dimensions | awk '{print $2}') - } - _getResolution - - if [[ -v _novncauth ]]; then - _exec_start_cmd="/usr/bin/x11vnc -display $_display -noscr -geometry $_res -auth guess -forever -bg -nopw" - else - _exec_start_cmd="/usr/bin/x11vnc -display $_display -noscr -geometry $_res -auth guess -forever -bg -rfbauth $HOME/.vnc/jrmc_passwd" - fi - - _bash_cmd "cat <<-EOF > $_service_fname - [Unit] - Description=x11vnc - After=multi.service - - [Service] - $_user_specifier - Type=forking - Environment=DISPLAY=$_display - ExecStart=$_exec_start_cmd - Restart=always - RestartSec=10 - - [Install] - WantedBy=multi-user.target - EOF" - - _systemctl_reload && \ - _systemctl_start "$_service_name" && \ - _systemctl_enable "$_service_name" && \ - echo "x11vnc running on localhost:$_port" && \ - _openFirewall "jriver-x11vnc" - } - - - ####################################### - # Starts and enables (at startup) an hourly service to build the latest version of JRiver Media - # Center RPM from the source DEB and create/update an RPM repository - ####################################### - _service_jriver-createrepo() { - debug "Running: ${FUNCNAME[0]}" - - _bash_cmd "cat <<-EOF > $_service_fname - [Unit] - Description=Builds JRiver Media Center RPM file, moves it to the repo dir, and runs createrepo - - [Service] - $_user_specifier - ExecStart=$_basedir/installJRMC --outputdir $_outputdir --createrepo --createrepo-webroot $_createrepo_webroot --createrepo-user $_createrepo_user - - [Install] - WantedBy=default.target - EOF" - - _bash_cmd "cat <<-EOF > $_timer_fname - [Unit] - Description=Run JRiver MC rpmbuild hourly - - [Timer] - OnCalendar=hourly - Persistent=true - - [Install] - WantedBy=timers.target - EOF" - - _systemctl_reload && \ - _systemctl_start "$_timer_name" && \ - _systemctl_enable "$_timer_name" - } - - - ####################################### - # CONTAINERS - ####################################### - _containerCreaterepo() { - : - } - - - _containerVNC() { - : - } - - - _containerMC() { - : - } - - - ####################################### - # Complete uninstall - ####################################### - _uninstall() { - debug "Running: ${FUNCNAME[0]}" - - read -r -p "Do you really want to uninstall JRiver Media Center? [y/N] " _response - _response=${_response,,} # tolower - [[ ! "$_response" =~ ^(yes|y)$ ]] && echo "Cancelling uninstall..." && exit 0 - - # Uninstall services - echo "Stopping and removing all associated Media Center services" - for _service in "${_available_services[@]}"; do - _servicePrep "$_service" - _systemctl_disable "$_service_name" - _systemctl_disable "$_timer_name" - [[ -f "$_service_fname" ]] && _rm_cmd "$_service_fname" - [[ -f "$_timer_fname" ]] && _rm_cmd "$_timer_fname" - done - - echo "Removing repo files" - [[ -f "/etc/yum.repos.d/jriver.repo" ]] \ - && _rm_cmd "/etc/yum.repos.d/jriver.repo" - [[ -f "/etc/apt/sources.list.d/jriver.list" ]] \ - && _rm_cmd "/etc/apt/sources.list.d/jriver.list" - - echo "Removing firewall rules" - if [[ -x $(command -v firewall-cmd) ]]; then - _firewall_cmd --permanent --remove-service=jriver - _firewall_cmd --permanent --delete-service=jriver - _firewall_cmd --reload - elif [[ -x $(command -v ufw) ]]; then - _firewall_cmd delete allow jriver - [[ -f "/etc/ufw/applications.d/jriver" ]] \ - && _rm_cmd /etc/ufw/applications.d/jriver - fi - - echo "Uninstalling Media Center" - if [[ "$ID" =~ ^(fedora|centos)$ ]]; then - _pkg_remove MediaCenter - elif [[ "$ID" =~ ^(debian|ubuntu|linuxmint)$ ]]; then - _pkg_remove "mediacenter$_mversion" - fi - - echo "JRiver Media Center has been completely uninstalled" - echo "If you wish to remove your library files: rm -rf $HOME/.jriver" - echo "If you wish to remove your rpmbuild output files: rm -rf $_outputdir" - } - - - _main() { - - # Parse input - _parseInput "$@" - - # Sanity checks - _sanityChecks - - # Set user variables - _setUser - - # Build some OS-specific commands based on the selected OS - _buildCommands - - # Install MC using package manager - if [[ -v _repoinstall ]]; then - [[ "$ID" =~ ^(fedora|centos)$ ]] && _installPackage rpmfusion-free-release epel-release - _installMCFromRepo - _symlinkCerts - _restoreLicense - _openFirewall "jriver" - fi - - # Set version to install/uninstall - _setVersion - - # Uninstall and exit - if [[ -v _uninstall ]]; then - _uninstall - exit $? - fi - - # Build RPM from source DEB - if [[ -v _rpmbuild ]]; then - _installPackage epel-release - _acquireDeb - _buildRPM - fi - - # Run createrepo - if [[ -v _createrepo ]]; then - _runCreaterepo "$_mcrpm" - fi - - # Install the rpm - if [[ -v _rpminstall ]]; then - if [[ "$ID" =~ ^(fedora|centos)$ ]]; then - _installPackage rpmfusion-free-release epel-release - _installPackage --noquery "$_mcrpm" - _symlinkCerts - _restoreLicense - _openFirewall "jriver" - fi - fi - - # Install services - _setDisplay - for _service in "${_services[@]}"; do - _servicePrep "$_service" - "_service_$_service" - done - - # Install containers - for _container in "${_containers[@]}"; do - "_container_$_container" - done - } + _next_display=":$_next_displaynum" + else + _display=":0" + _next_display=":1" + fi + + _displaynum="${_display#:}" # strip colon + _displaynum="${_displaynum%.*}" # strip suffix + _next_displaynum=$(( _displaynum + 1 )) } -# Allow this file to be executed directly if not being sourced -if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then - _basedir=$(dirname "$(readlink -f "$0")") - installJRMC - _main "$@" -fi + +####################################### +# Create associated service variables based on service name +# Requires: +# _service_user +# Globals: +# _service_fname +# _timer_fname +# _service_name +# _timer_name +# _user_specifier +####################################### +_servicePrep() { + debug "Running: ${FUNCNAME[0]}" + + if [[ "$_service_user" == "root" ]]; then + _service_fname="/usr/lib/systemd/system/${1}.service" + _timer_fname="/usr/lib/systemd/system/${1}.timer" + _service_name="jriver-${1}.service" + _timer_name="jriver-${1}}.timer" + _user_specifier="" + else + _service_fname="/usr/lib/systemd/system/${1}@.service" + _timer_fname="/usr/lib/systemd/system/${1}@.timer" + _service_name="${1}@$_service_user.service" + _timer_name="${1}@$_service_user.timer" + _user_specifier="User=%I" + fi +} + + +####################################### +# Starts and enables (at startup) a JRiver Media Center service +# Arguments: +# Passes arguments as startup options to /usr/bin/mediacenter26 +# Requires: +# XAUTHORITY +####################################### +_service_jriver-mediacenter() { + debug "Running: ${FUNCNAME[0]}" + + _bash_cmd "cat <<- EOF > $_service_fname + [Unit] + Description=JRiver Media Center $_mversion + After=graphical.target + + [Service] + $_user_specifier + Type=simple + Environment=DISPLAY=$_display + Environment=XAUTHORITY=$XAUTHORITY + ExecStart=/usr/bin/mediacenter$_mversion $* + Restart=always + RestartSec=10 + KillSignal=SIGHUP + TimeoutStopSec=30 + + [Install] + WantedBy=graphical.target + EOF" + + _systemctl_reload && \ + _systemctl_enable "$_service_name" && \ + _openFirewall "jriver" +} + + +####################################### +# Starts and enables (at startup) a JRiver Media Server service +####################################### +_service_jriver-mediaserver() { + debug "Running: ${FUNCNAME[0]}" + + _service_jriver-mediacenter "/MediaServer" +} + + +####################################### +# Starts and enables (at startup) JRiver Media Center in a new Xvnc session +####################################### +_service_jriver-xvnc-mediacenter() { + debug "Running: ${FUNCNAME[0]}" + + _installPackage tigervnc-server + + _setVNCPass + + local _port=$(( _next_displaynum + 5900 )) + + if [[ -v _novncauth ]]; then + _exec_start_cmd="/usr/bin/vncserver $_next_display -geometry 1440x900 -alwaysshared -name jriver$_next_display -SecurityTypes None -autokill -xstartup /usr/bin/mediacenter$_mversion" + else + _exec_start_cmd="/usr/bin/vncserver $_next_display -geometry 1440x900 -alwaysshared -rfbauth $HOME/.vnc/jrmc_passwd -autokill -xstartup /usr/bin/mediacenter$_mversion" + fi + + _bash_cmd "cat <<- EOF > $_service_fname + [Unit] + Description=Remote desktop service (VNC) + After=syslog.target network.target + + [Service] + Type=simple + $_user_specifier + ExecStartPre=/bin/sh -c '/usr/bin/vncserver -kill $_next_display > /dev/null 2>&1 || :' + ExecStart=$_exec_start_cmd + ExecStop=/usr/bin/vncserver -kill $_next_display + Restart=always + + [Install] + WantedBy=multi-user.target + EOF" + + _systemctl_reload && \ + _systemctl_enable "$_service_name" && \ + echo "Xvnc running on localhost:$_port" && \ + _openFirewall "jriver-xvnc" && \ + _openFirewall "jriver" +} + + +####################################### +# Starts and enables (at startup) JRiver Media Server and x11vnc sharing the local desktop +####################################### +_service_jriver-x11vnc() { + debug "Running: ${FUNCNAME[0]}" + + _installPackage x11vnc + + _setX11VNCPass + + local _port=$(( _displaynum + 5900 )) + + # Get current desktop resolution + # TODO: may need to break this out into its own function and get smarter at identifying multi-monitors + _getResolution() { + + debug "Running: ${FUNCNAME[0]}" + + _installPackage xorg-x11-utils + _res=$(xdpyinfo | grep dimensions | awk '{print $2}') + } + _getResolution + + if [[ -v _novncauth ]]; then + _exec_start_cmd="/usr/bin/x11vnc -display $_display -noscr -geometry $_res -auth guess -forever -bg -nopw" + else + _exec_start_cmd="/usr/bin/x11vnc -display $_display -noscr -geometry $_res -auth guess -forever -bg -rfbauth $HOME/.vnc/jrmc_passwd" + fi + + _bash_cmd "cat <<-EOF > $_service_fname + [Unit] + Description=x11vnc + After=multi.service + + [Service] + $_user_specifier + Type=forking + Environment=DISPLAY=$_display + ExecStart=$_exec_start_cmd + Restart=always + RestartSec=10 + + [Install] + WantedBy=multi-user.target + EOF" + + _systemctl_reload && \ + _systemctl_enable "$_service_name" && \ + echo "x11vnc running on localhost:$_port" && \ + _openFirewall "jriver-x11vnc" +} + + +####################################### +# Starts and enables (at startup) an hourly service to build the latest version of JRiver Media +# Center RPM from the source DEB and create/update an RPM repository +####################################### +_service_jriver-createrepo() { + debug "Running: ${FUNCNAME[0]}" + + _bash_cmd "cat <<-EOF > $_service_fname + [Unit] + Description=Builds JRiver Media Center RPM file, moves it to the repo dir, and runs createrepo + + [Service] + $_user_specifier + ExecStart=$PWD/installJRMC --outputdir $_outputdir --createrepo --createrepo-webroot $_createrepo_webroot --createrepo-user $_createrepo_user + + [Install] + WantedBy=default.target + EOF" + + _bash_cmd "cat <<-EOF > $_timer_fname + [Unit] + Description=Run JRiver MC rpmbuild hourly + + [Timer] + OnCalendar=hourly + Persistent=true + + [Install] + WantedBy=timers.target + EOF" + + _systemctl_reload && \ + _systemctl_enable "$_timer_name" +} + + +####################################### +# CONTAINERS +####################################### +_containerCreaterepo() { + : +} + + +_containerVNC() { + : +} + + +_containerMC() { + : +} + + +####################################### +# Complete uninstall +####################################### +_uninstall() { + debug "Running: ${FUNCNAME[0]}" + + read -r -p "Do you really want to uninstall JRiver Media Center? [y/N] " _response + _response=${_response,,} # tolower + [[ ! "$_response" =~ ^(yes|y)$ ]] && echo "Cancelling uninstall..." && exit 0 + + # Uninstall services + echo "Stopping and removing all associated Media Center services" + for _service in $(compgen -A "function" "_service"); do + _servicePrep "$_service" + _systemctl_disable "$_service_name" + _systemctl_disable "$_timer_name" + [[ -f "$_service_fname" ]] && _rm_cmd "$_service_fname" + [[ -f "$_timer_fname" ]] && _rm_cmd "$_timer_fname" + done + + echo "Removing repo files" + [[ -f "/etc/yum.repos.d/jriver.repo" ]] \ + && _rm_cmd "/etc/yum.repos.d/jriver.repo" + [[ -f "/etc/apt/sources.list.d/jriver.list" ]] \ + && _rm_cmd "/etc/apt/sources.list.d/jriver.list" + + echo "Removing firewall rules" + if [[ -x $(command -v firewall-cmd) ]]; then + _firewall_cmd --permanent --remove-service=jriver + _firewall_cmd --permanent --delete-service=jriver + _firewall_cmd --reload + elif [[ -x $(command -v ufw) ]]; then + _firewall_cmd delete allow jriver + [[ -f "/etc/ufw/applications.d/jriver" ]] \ + && _rm_cmd /etc/ufw/applications.d/jriver + fi + + echo "Uninstalling Media Center" + if [[ "$ID" =~ ^(fedora|centos)$ ]]; then + _pkg_remove MediaCenter + elif [[ "$ID" =~ ^(debian|ubuntu|linuxmint)$ ]]; then + _pkg_remove "mediacenter$_mversion" + fi + + echo "JRiver Media Center has been completely uninstalled" + echo "If you wish to remove your library files: rm -rf $HOME/.jriver" + echo "If you wish to remove your rpmbuild output files: rm -rf $_outputdir" +} + + +_main "$@"