diff --git a/README.md b/README.md old mode 100644 new mode 100755 index b3e0fb6..31faf64 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ wget https://github.com/mrworf/plexupdate/archive/master.zip && unzip master.zip Note that unzip is required (`sudo apt-get install unzip`). ####Using git to clone (Recommended) -Using git is way easier and recommended, if you ask me. +Using git is way easier and recommended, if you ask me. ``` git clone https://github.com/mrworf/plexupdate.git ``` @@ -54,11 +54,11 @@ There are also a few additional options for the more enterprising user. Setting Makes plexupdate.sh automatically update itself using git. Note! This will fail if git isn't available on the command line. - AUTOINSTALL Automatically installs the newly downloaded version. Currently works for Debian based systems as well as rpm based distros. Will fail miserably if you're not root. -- AUTODELETE +- AUTODELETE Once successfully downloaded and installed, it will delete the package (want not, waste not? ;-)) -- PUBLIC +- PUBLIC The default behavior of plexupdate.sh is to download the PlexPass edition of Plex Media Center. Setting this option to `yes` will make it download the public version instead. If this is yes, then `EMAIL` and `PASS` is no longer needed. -- FORCE +- FORCE Normally plexupdate.sh will avoid downloading a file it already has or if it's the same as the installed version. Using this option will force it to download again UNLESS the file already downloaded has the correct checksum. If you have AUTOINSTALL set, plexupdate.sh will then reinstall it. - FORCEALL Using this option will force plexupdate.sh to override the checksum check and will download the file again, and if you have AUTOINSTALL set, will reinstall it. @@ -74,16 +74,16 @@ Most of these options can be specified on the command-line as well, this is just ### Using it from CRON -If you want to use plexupdate as either a cron job or as a [systemd job](https://github.com/mrworf/plexupdate/wiki/Running-plexupdate-daily-as-a-systemd-timer), the -c option should do what you want. All non-error exit codes will be set to 0 and no output will be printed to stdout unless something has actually been done. (a new version was downloaded, installed, etc) +Generally, you copy the cronwrapper from the extras folder into the ```/etc/cron.weekly``` and edit the script to suite your needs. It will require you to create a configuration file (or at least, it's highly recommended). It also provides logging via syslog if needed. Should the script fail, it will print why and in most cases, this will result in an email to you. -If you don't even want to know when something has been done, you can combine this with the -q option and you will only receive output in the event of an error. Everything else will just silently finish without producing any output. +[Also checkout the newly written wiki for more details] ### Command Line Options Several new command line options are available. They can be specified in any order. - ```--config ``` - Defines the location the script should look for the config file. + Defines the location the script should look for the config file. - ```--email ``` Email to sign in to Plex.tv - ```--pass ``` @@ -95,8 +95,8 @@ Several new command line options are available. They can be specified in any ord - ```--port ``` This is the port that Plex Media Server uses. - ```--saveconfig``` - Saves the configuration as it is currently. This will take whatever is in the config file, plus whatever is specified on the command line and will save the config file with that information. Any information in the config file that plexupdate.sh does not understand or use WILL BE LOST. - + Saves the configuration as it is currently. This will take whatever is in the config file, plus whatever is specified on the command line and will save the config file with that information. Any information in the config file that plexupdate.sh does not understand or use WILL BE LOST. + ### Logs The script now outputs everything to a file (by default `/tmp/plexupdate.log`). This log ***MAY*** contain passwords, so if you post it online, ***USE CAUTION***. @@ -131,7 +131,7 @@ sudo plexupdate/plexupdate.sh -p -u -a ## What username and password are you talking about -The username and password for http://plex.tv +The username and password for http://plex.tv ## My password is rejected even though correct diff --git a/extras/cronwrapper b/extras/cronwrapper new file mode 100755 index 0000000..abcb3e4 --- /dev/null +++ b/extras/cronwrapper @@ -0,0 +1,41 @@ +#!/bin/bash + +CONFIGURED=false + +CONF= +SCRIPT=/home/john.doe/plexupdate/plexupdate.sh +LOGGING=false + +if ! $CONFIGURED; then + echo "ERROR: You have not configured this script" >&2 + exit 255 +fi + +if [ ! -z "$CONF" ]; then + # We have a config file, prefix it with parameter + CONF="--config \"${CONF}\"" +fi + +LOGFILE=$(mktemp /tmp/plexupdate.cron.XXXX) + +RET=0 +if $LOGGING; then + "${SCRIPT}" "${CONF}" 2>&1 | tee ${LOGFILE} | logger -t plexupdate -p daemon.info + if grep -q '^ERROR:' ${LOGFILE}; then + RET=1 + fi +else + "${SCRIPT}" "${CONF}" 2>&1 >${LOGFILE} + RET=$? +fi + +if [ $RET -ne 0 -a $RET -ne 2 -a $RET -ne 5 ]; then + # Make sure user gets an email about this + cat ${LOGFILE} >&2 +else + # Nah, not important + RET=0 +fi + +rm "${LOGFILE}" 2>/dev/null +exit $RET diff --git a/plexupdate.sh b/plexupdate.sh index e1f90bb..67f3772 100755 --- a/plexupdate.sh +++ b/plexupdate.sh @@ -40,89 +40,6 @@ if [ -z "${BASH_VERSINFO}" ]; then exit 255 fi -#################################################################### -# Functions for logging -if [ -z ${FILE_STDOUTLOG} ]; then - FILE_STDOUTLOG="/tmp/plexupdate.log" -fi - -# This is used in CRON mode to output only the lines from this run of the script -OUTPUTLINES=0 - -timestamp() { - date +"%F %T" -} - -Log() { - printf "%s - %s: %s\n" "$(timestamp)" "$1" "$2" >>${FILE_STDOUTLOG} - OUTPUTLINES=$((OUTPUTLINES+=1)) -} - -# $1 - Type of message (INFO, WARNING, ERROR, etc) -# $2 - Actual message -# $3 - Should include newline (assumed yes. If this equals "no" will not include it) -LogStdOut() { - if [ ! -z "$1" ]; then - printf "%s: %s" "$1" "$2" - else - printf "%s" "$2" - fi - if [ -z "$3" -o ! "$3" = "no" ]; then - printf "\n" - fi -} - -# $1 - Filename to add to log -# $2 - Type to log file contents as (INFO, ERROR, WARNING) -LogFileContents() { - while read line; - do - Log "$2" "$line" - done <"$1" -} - -# $1 - Message -# $2 - Print newline? defaults to yes -infoLog() { - Log "INFO" "$1" - if [ "${CRON}" = "no" ]; then - LogStdOut "" "$1" "$2" - fi -} - -infoLogNoNewline() { - infoLog "$1" "no" -} - -errorLog() { - Log "ERROR" "$1" >&2 - if [ "${CRON}" = "no" ]; then - LogStdOut "ERROR" "$1" "$2" >&2 - fi -} - -errorLogNoNewline() { - errorLog "$1" "no" -} - -warningLog() { - Log "WARNING" "$1" >&2 - if [ "${CRON}" = "no" ]; then - LogStdOut "WARNING" "$1" "$2" >&2 - fi -} - -warningLogNoNewline() { - warningLog "$1" "no" -} - -# Output log separator to make it easier to see each separate script execution -infoLog "##### Begin PlexUpdate #####" >/dev/null -# Output the arguments for this execution (NOTE: passwords entered on command line will get logged this way) -ALLARGS="$@" -infoLog "Args: ${ALLARGS}" >/dev/null -unset ALLARGS - ################################################################# # Don't change anything below this point, use a .plexupdate file # in your home directory to override this section. @@ -138,12 +55,11 @@ PLEXPORT=32400 # (aka "Advanced" settings, can be overriden with config file) FORCE=no FORCEALL=no -PUBLIC=no +PUBLIC=yes AUTOINSTALL=no AUTODELETE=no AUTOUPDATE=no AUTOSTART=no -CRON=no QUIET=no ARCH=$(uname -m) IGNOREAUTOUPDATE=no @@ -156,31 +72,33 @@ REDHAT_INSTALL="yum -y install" DEBIAN_INSTALL="dpkg -i" DISTRO_INSTALL="" +# Current pages we need - Do not change unless Plex.tv changes again +URL_LOGIN='https://plex.tv/users/sign_in.json' +URL_DOWNLOAD='https://plex.tv/api/downloads/1.json?channel=plexpass' +URL_DOWNLOAD_PUBLIC='https://plex.tv/api/downloads/1.json' + FILE_POSTDATA=$(mktemp /tmp/plexupdate.postdata.XXXX) -FILE_RAW=$(mktemp /tmp/plexupdate.raw.XXXX) -FILE_FAILCAUSE=$(mktemp /tmp/plexupdate.failcause.XXXX) +FILE_RAW=$(mktemp /tmp/plexupdate.failcause.XXXX) +FILE_FAILCAUSE=$(mktemp /tmp/plexupdate.raw.XXXX) FILE_KAKA=$(mktemp /tmp/plexupdate.kaka.XXXX) FILE_SHA=$(mktemp /tmp/plexupdate.sha.XXXX) FILE_WGETLOG=$(mktemp /tmp/plexupdate.wget.XXXX) -FILE_CMDLOG=$(mktemp /tmp/plexupdate.cmds.XXXX) -FILE_CMDERR=$(mktemp /tmp/plexupdate.errs.XXXX) FILE_LOCAL=$(mktemp /tmp/plexupdate.local.XXXX) FILE_REMOTE=$(mktemp /tmp/plexupdate.remote.XXXX) -# Current pages we need - Do not change unless Plex.tv changes again -URL_LOGIN=https://plex.tv/users/sign_in.json -URL_DOWNLOAD=https://plex.tv/api/downloads/1.json?channel=plexpass -URL_DOWNLOAD_PUBLIC=https://plex.tv/api/downloads/1.json - ###################################################################### # Functions for rest of script -cronexit() { - # Don't give anything but true error codes if in CRON mode - RAWEXIT=$1 - if [ "${CRON}" = "yes" -a $1 -gt 1 -a $1 -lt 255 ]; then - exit 0 - fi - exit $1 + +warn() { + echo "WARNING: $@" >&1 +} + +info() { + echo "$@" >&1 +} + +error() { + echo "ERROR: $@" >&2 } usage() { @@ -188,7 +106,6 @@ usage() { echo "" echo "" echo " -a Auto install if download was successful (requires root)" - echo " -c Cron mode, only fatal errors return non-zero cronexit code" echo " -d Auto delete after auto install" echo " -f Force download even if it's the same version or file" echo " already exists unless checksum passes" @@ -197,12 +114,11 @@ usage() { echo " -l List available builds and distros" echo " -p Public Plex Media Server version" echo " -P Show progressbar when downloading big files" - echo " -q Quiet mode. No stdout, only stderr and cronexit codes" + echo " -q Quiet mode. No stdout, only stderr and exit codes" echo " -r Print download URL and exit" echo " -s Auto start (needed for some distros)" echo " -u Auto update plexupdate.sh before running it (experimental)" echo " -U Do not autoupdate plexupdate.sh (experimental, default)" - echo " -v Show additional debug information (cannot be saved or set via config)" echo "" echo " Long Argument Options:" echo " --config Configuration file to use" @@ -213,7 +129,7 @@ usage() { echo " --port Port for Plex Server. Used with --server" echo " --saveconfig Save the configuration to config file" echo - cronexit 0 + exit 0 } running() { @@ -235,8 +151,8 @@ running() { return 1 else # We do not know what this means... - warningLog "Unknown response (${RET}) from server >>>" - warningLog "${DATA}" + warn "Unknown response (${RET}) from server >>>" + warn "${DATA}" return 0 fi } @@ -252,301 +168,6 @@ trimQuotes() { echo $__buffer } -if [ ! $# -eq 0 ]; then - HASCFG="${@: -1}" - if [ ! -z "${HASCFG}" -a ! "${HASCFG:0:1}" = "-" -a ! "${@:(-2):1}" = "--config" ]; then - if [ -f "${HASCFG}" ]; then - warningLog "Specifying config file as last argument is deprecated. Use --config instead." - CONFIGFILE=${HASCFG} - fi - fi -fi - -# Parse commandline -ALLARGS=( "$@" ) -optstring="acCdfFhlpPqrSsuUv -l config:,dldir:,email:,pass:,server:,port:,saveconfig" -getopt -T >/dev/null -if [ $? -eq 4 ]; then - optstring="-o $optstring" -fi -GETOPTRES=$(getopt $optstring -- "$@") -if [ $? -eq 1 ]; then - cronexit 1 -fi - -set -- ${GETOPTRES} -while true; -do - case "$1" in - (-h) usage;; - (-a) AUTOINSTALL_CL=yes;; - (-c) CRON_CL=yes;; - (-C) errorLog "CRON option has changed, please review README.md"; cronexit 255;; - (-d) AUTODELETE_CL=yes;; - (-f) FORCE_CL=yes;; - (-F) FORCEALL_CL=yes;; - (-l) LISTOPTS=yes;; - (-p) PUBLIC_CL=yes;; - (-P) SHOWPROGRESS=yes;; - (-q) QUIET_CL=yes;; - (-r) PRINT_URL=yes;; - (-s) AUTOSTART_CL=yes;; - (-S) errorLog "SILENT option has been removed, please use QUIET (-q) instead"; cronexit 255;; - (-u) AUTOUPDATE_CL=yes;; - (-U) IGNOREAUTOUPDATE=yes;; - (-v) VERBOSE_CL=yes;; - - (--config) shift; CONFIGFILE="$1"; CONFIGFILE=$(trimQuotes ${CONFIGFILE});; - (--dldir) shift; DOWNLOADDIR_CL="$1"; DOWNLOADDIR_CL=$(trimQuotes ${DOWNLOADDIR_CL});; - (--email) shift; EMAIL_CL="$1"; EMAIL_CL=$(trimQuotes ${EMAIL_CL});; - (--pass) shift; PASS_CL="$1"; PASS_CL=$(trimQuotes ${PASS_CL});; - (--server) shift; PLEXSERVER_CL="$1"; PLEXSERVER_CL=$(trimQuotes ${PLEXSERVER_CL});; - (--port) shift; PLEXPORT_CL="$1"; PLEXPORT_CL=$(trimQuotes ${PLEXPORT_CL});; - (--saveconfig) SAVECONFIG=yes;; - - (--) ;; - (-*) errorLog "Unrecognized option $1"; usage; cronexit 1;; - (*) break;; - esac - shift -done - -# Sanity, make sure wget is in our path... -if ! hash wget 2>/dev/null; then - errorLog "This script requires wget in the path. It could also signify that you don't have the tool installed." - exit 1 -fi - -# Allow manual control of configfile -if [ ! -z "${CONFIGFILE}" ]; then - if [ -f "${CONFIGFILE}" ]; then - infoLog "Using configuration: ${CONFIGFILE}" #>/dev/null - source "${CONFIGFILE}" - else - errorLog "Cannot load configuration ${CONFIGFILE}" - exit 1 - fi -else - # Load settings from config file if it exists - # Also, respect SUDO_USER and try that first - if [ ! -z "${SUDO_USER}" ]; then - # Make sure nothing bad comes from this (since we use eval) - ERROR=0 - if [[ $SUDO_USER == *";"* ]]; then ERROR=1 ; # Allows more commands - elif [[ $SUDO_USER == *" "* ]]; then ERROR=1 ; # Space is not a good thing - elif [[ $SUDO_USER == *"&"* ]]; then ERROR=1 ; # Spinning off the command is bad - elif [[ $SUDO_USER == *"<"* ]]; then ERROR=1 ; # No redirection - elif [[ $SUDO_USER == *">"* ]]; then ERROR=1 ; # No redirection - elif [[ $SUDO_USER == *"|"* ]]; then ERROR=1 ; # No pipes - elif [[ $SUDO_USER == *"~"* ]]; then ERROR=1 ; # No tilde - fi - if [ ${ERROR} -gt 0 ]; then - errorLog "SUDO_USER variable is COMPROMISED: \"${SUDO_USER}\"" - exit 255 - fi - - # Try using original user's config - CONFIGDIR="$( eval cd ~${SUDO_USER} 2>/dev/null && pwd )" - if [ -z "${CONFIGDIR}" ]; then - warningLog "SUDO_USER \"${SUDO_USER}\" does not have a valid home directory, ignoring." - fi - - if [ ! -z "${CONFIGDIR}" -a -f "${CONFIGDIR}/.plexupdate" ]; then - infoLog "Using \"${SUDO_USER}\" configuration: ${CONFIGDIR}/.plexupdate" #>/dev/null - CONFIGFILE="${CONFIGDIR}/.plexupdate" - source "${CONFIGDIR}/.plexupdate" - elif [ -f ~/.plexupdate ]; then - # Fallback for compatibility - infoLog "Using \"${SUDO_USER}\" configuration: ${HOME}/.plexupdate" #>/dev/null - CONFIGFILE="${HOME}/.plexupdate" # tilde expansion won't happen later. - source ~/.plexupdate - fi - elif [ -f ~/.plexupdate ]; then - # Fallback for compatibility - infoLog "Using configuration: ${HOME}/.plexupdate" #>/dev/null - CONFIGFILE="${HOME}/.plexupdate" - source ~/.plexupdate - fi -fi - -# DO NOT ALLOW VERBOSE FROM CONFIGURATION FILE! -if [ "${VERBOSE_CL}" = "yes" ]; then - VERBOSE=yes -else - VERBOSE=no -fi - -# The way I wrote this, it assumes that whatever we put on the command line is what we want and should override -# any values in the configuration file. As a result, we need to check if they've been set on the command line -# and overwrite the values that may have been loaded with the config file - -for VAR in AUTOINSTALL CRON AUTODELETE DOWNLOADDIR EMAIL PASS FORCE FORCEALL PUBLIC QUIET AUTOSTART AUTOUPDATE PLEXSERVER PLEXPORT -do - VAR2="$VAR""_CL" - if [ ! -z ${!VAR2} ]; then - eval $VAR='${!VAR2}' - fi -done - -# This will destroy and recreate the config file. Any settings that are set in the config file but are no longer -# valid will NOT be saved. -if [ "${SAVECONFIG}" = "yes" ]; then - if [ ! -d "$(eval cd ${DOWNLOADDIR// /\\ } 2>/dev/null && pwd)" ]; then - errorLog "Download directory does not exist or is not a directory (tried \"${DOWNLOADDIR}\")" - exit 1 - fi - echo "# Config file for plexupdate" >${CONFIGFILE:="${HOME}/.plexupdate"} - - for VAR in AUTOINSTALL CRON AUTODELETE DOWNLOADDIR EMAIL PASS FORCE FORCEALL PUBLIC QUIET AUTOSTART AUTOUPDATE PLEXSERVER PLEXPORT CHECKUPDATE - do - if [ ! -z ${!VAR} ]; then - - # The following keys have defaults set in this file. We don't want to include these values if they are the default. - if [ ${VAR} = "FORCE" \ - -o ${VAR} = "FORCEALL" \ - -o ${VAR} = "PUBLIC" \ - -o ${VAR} = "AUTOINSTALL" \ - -o ${VAR} = "AUTODELETE" \ - -o ${VAR} = "AUTOUPDATE" \ - -o ${VAR} = "AUTOSTART" \ - -o ${VAR} = "CRON" \ - -o ${VAR} = "QUIET" ]; then - - if [ ${!VAR} = "yes" ]; then - echo "${VAR}='${!VAR}'" >> ${CONFIGFILE} - fi - elif [ ${VAR} = "PLEXPORT" ]; then - if [ ! "${!VAR}" = "32400" ]; then - echo "${VAR}='${!VAR}'" >> ${CONFIGFILE} - fi - else - echo "${VAR}='${!VAR}'" >> ${CONFIGFILE} - fi - fi - done -fi - -if [ "${SHOWPROGRESS}" = "yes" ]; then - WGETOPTIONS="--show-progress" -fi - -if [ "${IGNOREAUTOUPDATE}" = "yes" ]; then - AUTOUPDATE=no -fi - -if [ "${KEEP}" = "yes" ]; then - errorLog "KEEP is deprecated and should be removed from .plexupdate" - cronexit 255 -fi - -if [ "${SILENT}" = "yes" ]; then - errorLog "SILENT option has been removed and should be removed from .plexupdate" - errorLog " Use QUIET or -q instead" - cronexit 255 -fi - -if [ ! -z "${RELEASE}" ]; then - errorLog "RELEASE keyword is deprecated and should be removed from .plexupdate" - errorLog " Use DISTRO and BUILD instead to manually select what to install (check README.md)" - cronexit 255 -fi - -if [ "${CRON}" = "yes" -a "${QUIET}" = "no" ]; then - exec 3>&1 >${FILE_CMDLOG} 2>${FILE_CMDERR} -elif [ "${QUIET}" = "yes" ]; then - # Redirect STDOUT to dev null. Use >&3 if you really, really, REALLY need to print to STDOUT - exec 3>&1 >/dev/null -fi - -if [ "${AUTOUPDATE}" = "yes" ]; then - if ! hash git 2>/dev/null; then - errorLog "You need to have git installed for this to work" - cronexit 1 - fi - pushd "$(dirname "$0")" >/dev/null - if [ ! -d .git ]; then - errorLog "This is not a git repository, auto update only works if you've done a git clone" - cronexit 1 - fi - git status | grep "git commit -a" >/dev/null 2>/dev/null - if [ $? -eq 0 ]; then - errorLog "You have made changes to the script, cannot auto update" - cronexit 1 - fi - infoLogNoNewline "Auto updating..." - git pull >/dev/null - if [ $? -ne 0 ]; then - errorLog 'Unable to update git, try running "git pull" manually to see what is wrong' - cronexit 1 - fi - infoLog "OK" - popd >/dev/null - - if ! type "$0" 2>/dev/null >/dev/null ; then - if [ -f "$0" ]; then - /bin/bash "$0" -U ${ALLARGS[@]} - else - errorLog "Unable to relaunch, couldn't find $0" - cronexit 1 - fi - else - "$0" -U ${ALLARGS[@]} - fi - cronexit $? -fi - -# Sanity check -if [ -z "${EMAIL}" -o -z "${PASS}" ] && [ "${PUBLIC}" = "no" ]; then - errorLog "Need email & password to download PlexPass version. Otherwise run with -p to download public version." - cronexit 1 -elif [ ! -z "${EMAIL}" ] && [[ "$EMAIL" == *"@"* ]] && [[ "$EMAIL" != *"@"*"."* ]]; then - errorLog "EMAIL field must contain a valid email address" - cronexit 1 -fi - - -if [ "${AUTOINSTALL}" = "yes" -o "${AUTOSTART}" = "yes" ]; then - id | grep -i 'uid=0(' 2>&1 >/dev/null - if [ $? -ne 0 ]; then - errorLog "You need to be root to use autoinstall/autostart option." - cronexit 1 - fi -fi - - -# Remove any ~ or other oddness in the path we're given -DOWNLOADDIR_PRE=${DOWNLOADDIR} -DOWNLOADDIR="$(eval cd ${DOWNLOADDIR// /\\ } 2>/dev/null && pwd)" -if [ ! -d "${DOWNLOADDIR}" ]; then - errorLog "Download directory does not exist or is not a directory (tried \"${DOWNLOADDIR_PRE}\")" - cronexit 1 -fi - -if [ -z "${DISTRO_INSTALL}" ]; then - if [ -z "${DISTRO}" -a -z "${BUILD}" ]; then - # Detect if we're running on redhat instead of ubuntu - if [ -f /etc/redhat-release ]; then - REDHAT=yes - BUILD="linux-ubuntu-${ARCH}" - DISTRO="redhat" - DISTRO_INSTALL="${REDHAT_INSTALL}" - else - REDHAT=no - BUILD="linux-ubuntu-${ARCH}" - DISTRO="ubuntu" - DISTRO_INSTALL="${DEBIAN_INSTALL}" - fi - elif [ -z "${DISTRO}" -o -z "${BUILD}" ]; then - errorLog "You must define both DISTRO and BUILD" - cronexit 255 - fi -else - if [ -z "${DISTRO}" -o -z "${BUILD}" ]; then - errorLog "Using custom DISTRO_INSTALL requires custom DISTRO and BUILD too" - cronexit 255 - fi -fi - # Useful functions rawurlencode() { local string="${1}" @@ -571,49 +192,298 @@ keypair() { echo "${key}=${val}" } -# Setup an cronexit handler so we cleanup -function cleanup { - if [ "${CRON}" = yes -a "${RAWEXIT}" -ne 5 -a -f "${FILE_CMDLOG}" ]; then - infoLog "Command Output:" >/dev/null - OUTPUTLINES=$((OUTPUTLINES+$(wc -l <${FILE_CMDLOG}))) - cat ${FILE_CMDLOG} >>${FILE_STDOUTLOG} - - errorLog "Command Errors/Warnings:" >/dev/null - OUTPUTLINES=$((OUTPUTLINES+$(wc -l <${FILE_CMDERR}))) - cat ${FILE_CMDERR} >>${FILE_STDOUTLOG} - - exec 1>&3 - tail -n ${OUTPUTLINES} "${FILE_STDOUTLOG}" - fi - rm "${FILE_POSTDATA}" 2>/dev/null >/dev/null - rm "${FILE_RAW}" 2>/dev/null >/dev/null - rm "${FILE_FAILCAUSE}" 2>/dev/null >/dev/null - rm "${FILE_KAKA}" 2>/dev/null >/dev/null - rm "${FILE_SHA}" 2>/dev/null >/dev/null - rm "${FILE_WGETLOG}" 2>/dev/null >/dev/null - rm "${FILE_CMDLOG}" 2>/dev/null >/dev/null - rm "${FILE_CMDERR}" 2>/dev/null >/dev/null - rm "${FILE_LOCAL}" 2>/dev/null >/dev/null - rm "${FILE_REMOTE}" 2>/dev/null >/dev/null +# Setup an exit handler so we cleanup +cleanup() { + for F in "${FILE_RAW}" "${FILE_FAILCAUSE}" "${FILE_POSTDATA}" "${FILE_KAKA}" "${FILE_SHA}" "${FILE_LOCAL}" "${FILE_REMOTE}"; do + rm "$F" 2>/dev/null >/dev/null + done } trap cleanup EXIT +if [ ! $# -eq 0 ]; then + HASCFG="${@: -1}" + if [ ! -z "${HASCFG}" -a ! "${HASCFG:0:1}" = "-" -a ! "${@:(-2):1}" = "--config" ]; then + if [ -f "${HASCFG}" ]; then + warn "Specifying config file as last argument is deprecated. Use --config instead." + CONFIGFILE=${HASCFG} + fi + fi +fi + +# Parse commandline +ALLARGS=( "$@" ) +optstring="acCdfFhlpPqrSsuU -l config:,dldir:,email:,pass:,server:,port:,saveconfig" +getopt -T >/dev/null +if [ $? -eq 4 ]; then + optstring="-o $optstring" +fi +GETOPTRES=$(getopt $optstring -- "$@") +if [ $? -eq 1 ]; then + exit 1 +fi + +set -- ${GETOPTRES} +while true; +do + case "$1" in + (-h) usage;; + (-a) AUTOINSTALL_CL=yes;; + (-c) error "CRON option is deprecated, please use cronwrapper (see README.md)"; exit 255;; + (-C) error "CRON option is deprecated, please use cronwrapper (see README.md)"; exit 255;; + (-d) AUTODELETE_CL=yes;; + (-f) FORCE_CL=yes;; + (-F) FORCEALL_CL=yes;; + (-l) LISTOPTS=yes;; + (-p) PUBLIC_CL=yes;; + (-P) SHOWPROGRESS=yes;; + (-q) QUIET_CL=yes;; + (-r) PRINT_URL=yes;; + (-s) AUTOSTART_CL=yes;; + (-u) AUTOUPDATE_CL=yes;; + (-U) IGNOREAUTOUPDATE=yes;; + + (--config) shift; CONFIGFILE="$1"; CONFIGFILE=$(trimQuotes ${CONFIGFILE});; + (--dldir) shift; DOWNLOADDIR_CL="$1"; DOWNLOADDIR_CL=$(trimQuotes ${DOWNLOADDIR_CL});; + (--email) shift; EMAIL_CL="$1"; EMAIL_CL=$(trimQuotes ${EMAIL_CL});; + (--pass) shift; PASS_CL="$1"; PASS_CL=$(trimQuotes ${PASS_CL});; + (--server) shift; PLEXSERVER_CL="$1"; PLEXSERVER_CL=$(trimQuotes ${PLEXSERVER_CL});; + (--port) shift; PLEXPORT_CL="$1"; PLEXPORT_CL=$(trimQuotes ${PLEXPORT_CL});; + (--saveconfig) SAVECONFIG=yes;; + + (--) ;; + (-*) error "Unrecognized option $1"; usage; exit 1;; + (*) break;; + esac + shift +done + +# Sanity, make sure wget is in our path... +if ! hash wget 2>/dev/null; then + error "This script requires wget in the path. It could also signify that you don't have the tool installed." + exit 1 +fi + +# Allow manual control of configfile +if [ ! -z "${CONFIGFILE}" ]; then + if [ -f "${CONFIGFILE}" ]; then + info "Using configuration: ${CONFIGFILE}" #>/dev/null + source "${CONFIGFILE}" + else + error "Cannot load configuration ${CONFIGFILE}" + exit 1 + fi +else + # Load settings from config file if it exists + # Also, respect SUDO_USER and try that first + if [ ! -z "${SUDO_USER}" ]; then + # Make sure nothing bad comes from this (since we use eval) + ERROR=0 + if [[ $SUDO_USER == *";"* ]]; then ERROR=1 ; # Allows more commands + elif [[ $SUDO_USER == *" "* ]]; then ERROR=1 ; # Space is not a good thing + elif [[ $SUDO_USER == *"&"* ]]; then ERROR=1 ; # Spinning off the command is bad + elif [[ $SUDO_USER == *"<"* ]]; then ERROR=1 ; # No redirection + elif [[ $SUDO_USER == *">"* ]]; then ERROR=1 ; # No redirection + elif [[ $SUDO_USER == *"|"* ]]; then ERROR=1 ; # No pipes + elif [[ $SUDO_USER == *"~"* ]]; then ERROR=1 ; # No tilde + fi + if [ ${ERROR} -gt 0 ]; then + error "SUDO_USER variable is COMPROMISED: \"${SUDO_USER}\"" + exit 255 + fi + + # Try using original user's config + CONFIGDIR="$( eval cd ~${SUDO_USER} 2>/dev/null && pwd )" + if [ -z "${CONFIGDIR}" ]; then + warn "SUDO_USER \"${SUDO_USER}\" does not have a valid home directory, ignoring." + fi + + if [ ! -z "${CONFIGDIR}" -a -f "${CONFIGDIR}/.plexupdate" ]; then + info "Using \"${SUDO_USER}\" configuration: ${CONFIGDIR}/.plexupdate" #>/dev/null + CONFIGFILE="${CONFIGDIR}/.plexupdate" + source "${CONFIGDIR}/.plexupdate" + elif [ -f ~/.plexupdate ]; then + # Fallback for compatibility + info "Using \"${SUDO_USER}\" configuration: ${HOME}/.plexupdate" #>/dev/null + CONFIGFILE="${HOME}/.plexupdate" # tilde expansion won't happen later. + source ~/.plexupdate + fi + elif [ -f ~/.plexupdate ]; then + # Fallback for compatibility + info "Using configuration: ${HOME}/.plexupdate" #>/dev/null + CONFIGFILE="${HOME}/.plexupdate" + source ~/.plexupdate + fi +fi + +# The way I wrote this, it assumes that whatever we put on the command line is what we want and should override +# any values in the configuration file. As a result, we need to check if they've been set on the command line +# and overwrite the values that may have been loaded with the config file + +for VAR in AUTOINSTALL AUTODELETE DOWNLOADDIR EMAIL PASS FORCE FORCEALL PUBLIC QUIET AUTOSTART AUTOUPDATE PLEXSERVER PLEXPORT +do + VAR2="$VAR""_CL" + if [ ! -z ${!VAR2} ]; then + eval $VAR='${!VAR2}' + fi +done + +# This will destroy and recreate the config file. Any settings that are set in the config file but are no longer +# valid will NOT be saved. +if [ "${SAVECONFIG}" = "yes" ]; then + echo "# Config file for plexupdate" >${CONFIGFILE:="${HOME}/.plexupdate"} + + for VAR in AUTOINSTALL AUTODELETE DOWNLOADDIR EMAIL PASS FORCE FORCEALL PUBLIC QUIET AUTOSTART AUTOUPDATE PLEXSERVER PLEXPORT CHECKUPDATE + do + if [ ! -z ${!VAR} ]; then + + # The following keys have defaults set in this file. We don't want to include these values if they are the default. + if [ ${VAR} = "FORCE" \ + -o ${VAR} = "FORCEALL" \ + -o ${VAR} = "PUBLIC" \ + -o ${VAR} = "AUTOINSTALL" \ + -o ${VAR} = "AUTODELETE" \ + -o ${VAR} = "AUTOUPDATE" \ + -o ${VAR} = "AUTOSTART" \ + -o ${VAR} = "QUIET" ]; then + + if [ ${!VAR} = "yes" ]; then + echo "${VAR}='${!VAR}'" >> ${CONFIGFILE} + fi + elif [ ${VAR} = "PLEXPORT" ]; then + if [ ! "${!VAR}" = "32400" ]; then + echo "${VAR}='${!VAR}'" >> ${CONFIGFILE} + fi + else + echo "${VAR}='${!VAR}'" >> ${CONFIGFILE} + fi + fi + done +fi + +if [ "${SHOWPROGRESS}" = "yes" ]; then + WGETOPTIONS="--show-progress" +fi + +if [ "${IGNOREAUTOUPDATE}" = "yes" ]; then + AUTOUPDATE=no +fi + +if [ "${CRON}" = "yes" ]; then + error "CRON has been deprecated, please use cronwrapper (see README.md)" + exit 255 +fi + +if [ "${KEEP}" = "yes" ]; then + error "KEEP is deprecated and should be removed from .plexupdate" + exit 255 +fi + +if [ ! -z "${RELEASE}" ]; then + error "RELEASE keyword is deprecated and should be removed from .plexupdate" + error "Use DISTRO and BUILD instead to manually select what to install (check README.md)" + exit 255 +fi + +if [ "${AUTOUPDATE}" = "yes" ]; then + if ! hash git 2>/dev/null; then + error "You need to have git installed for this to work" + exit 1 + fi + pushd "$(dirname "$0")" >/dev/null + if [ ! -d .git ]; then + error "This is not a git repository, auto update only works if you've done a git clone" + exit 1 + fi + git status | grep "git commit -a" >/dev/null 2>/dev/null + if [ $? -eq 0 ]; then + error "You have made changes to the script, cannot auto update" + exit 1 + fi + info "Auto updating" + git pull >/dev/null + if [ $? -ne 0 ]; then + error 'Unable to update git, try running "git pull" manually to see what is wrong' + exit 1 + fi + info "Update complete" + popd >/dev/null + + if ! type "$0" 2>/dev/null >/dev/null ; then + if [ -f "$0" ]; then + /bin/bash "$0" -U ${ALLARGS[@]} + else + error "Unable to relaunch, couldn't find $0" + exit 1 + fi + else + "$0" -U ${ALLARGS[@]} + fi + exit $? +fi + +# Sanity check +if [ -z "${EMAIL}" -o -z "${PASS}" ] && [ "${PUBLIC}" = "no" ] && [ ! -f "${FILE_KAKA}" ]; then + error "Need username & password to download PlexPass version. Otherwise run with -p to download public version." + exit 1 +fi + +if [ "${AUTOINSTALL}" = "yes" -o "${AUTOSTART}" = "yes" ]; then + id | grep -i 'uid=0(' 2>&1 >/dev/null + if [ $? -ne 0 ]; then + error "You need to be root to use autoinstall/autostart option." + exit 1 + fi +fi + + +# Remove any ~ or other oddness in the path we're given +DOWNLOADDIR_PRE=${DOWNLOADDIR} +DOWNLOADDIR="$(eval cd ${DOWNLOADDIR// /\\ } 2>/dev/null && pwd)" +if [ ! -d "${DOWNLOADDIR}" ]; then + error "Download directory does not exist or is not a directory (tried \"${DOWNLOADDIR_PRE}\")" + exit 1 +fi + +if [ -z "${DISTRO_INSTALL}" ]; then + if [ -z "${DISTRO}" -a -z "${BUILD}" ]; then + # Detect if we're running on redhat instead of ubuntu + if [ -f /etc/redhat-release ]; then + REDHAT=yes + BUILD="linux-ubuntu-${ARCH}" + DISTRO="redhat" + DISTRO_INSTALL="${REDHAT_INSTALL}" + else + REDHAT=no + BUILD="linux-ubuntu-${ARCH}" + DISTRO="ubuntu" + DISTRO_INSTALL="${DEBIAN_INSTALL}" + fi + elif [ -z "${DISTRO}" -o -z "${BUILD}" ]; then + error "You must define both DISTRO and BUILD" + exit 255 + fi +else + if [ -z "${DISTRO}" -o -z "${BUILD}" ]; then + error "Using custom DISTRO_INSTALL requires custom DISTRO and BUILD too" + exit 255 + fi +fi + if [ "${CHECKUPDATE}" = "yes" ]; then - ERR1=0 (wget -q https://raw.githubusercontent.com/mrworf/plexupdate/master/plexupdate.sh -O - 2>/dev/null || echo ERROR) | shasum >"${FILE_REMOTE}" 2>/dev/null - ERR2=0 + ERR1=$? (cat "$0" 2>/dev/null || echo ERROR) | shasum >"${FILE_LOCAL}" 2>/dev/null + ERR2=$? if [ $ERR1 -ne 0 -o $ERR2 -ne 0 ]; then - errorLog "CheckUpdate: Unable to confirm version of script" + error "When checking for version, was unable to confirm version of script" else # "709c7506b17090bce0d1e2464f39f4a434cf25f1" is the hash for "ERROR" :) if grep -sq "709c7506b17090bce0d1e2464f39f4a434cf25f1" "${FILE_LOCAL}" ; then - errorLog "CheckUpdate: Unable to validate local copy" + error "When checking for version, was unable to validate local copy" elif grep -sq "709c7506b17090bce0d1e2464f39f4a434cf25f1" "${FILE_REMOTE}" ; then - errorLog "CheckUpdate: Unable to validate remote copy" + error "When checking for version, was was unable to validate remote copy" elif ! diff "${FILE_LOCAL}" "${FILE_REMOTE}" >/dev/null 2>/dev/null ; then - infoLog "Newer version of this script is available at https://github.com/mrworf/plexupdate" - infoLog "(or you've made changes to this script yourself)" + info "Newer version of this script is available at https://github.com/mrworf/plexupdate" fi fi rm "${FILE_LOCAL}" 2>/dev/null >/dev/null @@ -633,7 +503,7 @@ fi # commit Sign in if [ "${PUBLIC}" = "no" ]; then - infoLogNoNewline "Authenticating..." + info "Authenticating with plex.tv" # Clean old session rm "${FILE_KAKA}" 2>/dev/null @@ -650,17 +520,15 @@ if [ "${PUBLIC}" = "no" ]; then # Provide some details to the end user RESULTCODE=$(head -n1 "${FILE_RAW}" | grep -oe '[1-5][0-9][0-9]') + info "Contents of ${FILE_RAW}" + cat "${FILE_RAW}" if [ $RESULTCODE -eq 401 ]; then - errorLog "email and/or password incorrect" - if [ "$VERBOSE" = "yes" ]; then - errorLog "Tried using \"${EMAIL}\" and \"${PASS}\" " - fi - cronexit 1 + error "Username and/or password incorrect" + exit 1 elif [ $RESULTCODE -ne 201 ]; then - errorLog "Failed to login, debug information:" - cat "${FILE_FAILCAUSE}" >&2 - LogFileContents "${FILE_FAILCAUSE}" "ERROR" - cronexit 1 + error "Failed to login, debug information:" + cat "${FILE_RAW}" >&2 + exit 1 fi # If the system got here, it means the login was successfull, so we set the TOKEN variable to the authToken from the response @@ -670,8 +538,6 @@ if [ "${PUBLIC}" = "no" ]; then # Remove this, since it contains more information than we should leave hanging around rm "${FILE_FAILCAUSE}" - infoLog "OK" - elif [ "$PUBLIC" != "no" ]; then # It's a public version, so change URL and make doubly sure that cookies are empty rm 2>/dev/null >/dev/null "${FILE_KAKA}" @@ -692,47 +558,38 @@ if [ "${LISTOPTS}" = "yes" ]; then elif [ -z "$BUILD" ]; then BUILD="$X" else - if [ "${QUIET}" = "yes" ]; then - printf "%-12s %-30s %s\n" "$DISTRO" "$BUILD" "$X" >&3 - else - printf "%-12s %-30s %s\n" "$DISTRO" "$BUILD" "$X" - fi + printf "%-12s %-30s %s\n" "$DISTRO" "$BUILD" "$X" BUILD= DISTRO= fi done - cronexit 0 + exit 0 fi # Extract the URL for our release -infoLogNoNewline "Finding download URL..." +info "Retriving list of available downloads" # Set "X-Plex-Token" to the auth token, if no token is specified or it is invalid, the list will return public downloads by default RELEASE=$(wget --header "X-Plex-Token:"${TOKEN}"" --load-cookies "${FILE_KAKA}" --save-cookies "${FILE_KAKA}" --keep-session-cookies "${URL_DOWNLOAD}" -O - 2>/dev/null | grep -ioe '"label"[^}]*' | grep -i "\"distro\":\"${DISTRO}\"" | grep -m1 -i "\"build\":\"${BUILD}\"") DOWNLOAD=$(echo ${RELEASE} | grep -m1 -ioe 'https://[^\"]*') CHECKSUM=$(echo ${RELEASE} | grep -ioe '\"checksum\"\:\"[^\"]*' | sed 's/\"checksum\"\:\"//') -infoLog "OK" if [ -z "${DOWNLOAD}" ]; then - errorLog "Unable to retrieve the URL needed for download (Query DISTRO: $DISTRO, BUILD: $BUILD)" - cronexit 3 + error "Unable to retrieve the URL needed for download (Query DISTRO: $DISTRO, BUILD: $BUILD)" + exit 3 fi FILENAME="$(basename 2>/dev/null ${DOWNLOAD})" if [ $? -ne 0 ]; then - errorLog "Failed to parse HTML, download cancelled." - cronexit 3 + error "Failed to parse HTML, download cancelled." + exit 3 fi echo "${CHECKSUM} ${DOWNLOADDIR}/${FILENAME}" >"${FILE_SHA}" if [ "${PRINT_URL}" = "yes" ]; then - if [ "${QUIET}" = "yes" ]; then - infoLog "${DOWNLOAD}" >&3 - else - infoLog "${DOWNLOAD}" - fi - cronexit 0 + info "${DOWNLOAD}" + exit 0 fi # By default, try downloading @@ -743,39 +600,39 @@ if [ "${REDHAT}" != "yes" ]; then INSTALLED_VERSION=$(dpkg-query -s plexmediaserver 2>/dev/null | grep -Po 'Version: \K.*') else if [ "${AUTOSTART}" = "no" ]; then - warningLog "Your distribution may require the use of the AUTOSTART [-s] option for the service to start after the upgrade completes." + warn "Your distribution may require the use of the AUTOSTART [-s] option for the service to start after the upgrade completes." fi INSTALLED_VERSION=$(rpm -qv plexmediaserver 2>/dev/null) fi if [[ $FILENAME == *$INSTALLED_VERSION* ]] && [ "${FORCE}" != "yes" -a "${FORCEALL}" != "yes" ] && [ ! -z "${INSTALLED_VERSION}" ]; then - infoLog "Your OS reports the latest version of Plex ($INSTALLED_VERSION) is already installed. Use -f to force download." - cronexit 5 + info "Your OS reports the latest version of Plex ($INSTALLED_VERSION) is already installed. Use -f to force download." + exit 5 fi if [ -f "${DOWNLOADDIR}/${FILENAME}" ]; then if [ "${FORCE}" != "yes" -a "${FORCEALL}" != "yes" ]; then sha1sum --status -c "${FILE_SHA}" if [ $? -eq 0 ]; then - infoLog "File already exists (${FILENAME}), won't download." + info "File already exists (${FILENAME}), won't download." if [ "${AUTOINSTALL}" != "yes" ]; then - cronexit 2 + exit 2 fi SKIP_DOWNLOAD="yes" else - infoLog "File exists but fails checksum. Redownloading." + info "File exists but fails checksum. Redownloading." SKIP_DOWNLOAD="no" fi elif [ "${FORCEALL}" == "yes" ]; then - infoLog "Note! File exists, but asked to overwrite with new copy" + info "Note! File exists, but asked to overwrite with new copy" else sha1sum --status -c "${FILE_SHA}" if [ $? -ne 0 ]; then - infoLog "Note! File exists but fails checksum. Redownloading." + info "File exists but fails checksum. Redownloading." else - infoLog "File exists and checksum passes, won't redownload." + info "File exists and checksum passes, won't redownload." if [ "${AUTOINSTALL}" != "yes" ]; then - cronexit 2 + exit 2 fi SKIP_DOWNLOAD="yes" fi @@ -783,35 +640,35 @@ if [ -f "${DOWNLOADDIR}/${FILENAME}" ]; then fi if [ "${SKIP_DOWNLOAD}" = "no" ]; then - infoLogNoNewline "Downloading release \"${FILENAME}\"..." + info "Downloading release \"${FILENAME}\"" wget ${WGETOPTIONS} -o "${FILE_WGETLOG}" --load-cookies "${FILE_KAKA}" --save-cookies "${FILE_KAKA}" --keep-session-cookies "${DOWNLOAD}" -O "${DOWNLOADDIR}/${FILENAME}" 2>&1 CODE=$? if [ ${CODE} -eq 2 ]; then - errorLog "!! Your wget is too old to support --show-progress" - infoLogNoNewline "Trying to download release \"${FILENAME}\" again..." + error "Your wget is too old to support --show-progress" + info "Trying to download release \"${FILENAME}\" again" wget -o "${FILE_WGETLOG}" --load-cookies "${FILE_KAKA}" --save-cookies "${FILE_KAKA}" --keep-session-cookies "${DOWNLOAD}" -O "${DOWNLOADDIR}/${FILENAME}" 2>&1 CODE=$? fi - ERROR=$(cat ${FILE_WGETLOG}) if [ ${CODE} -ne 0 ]; then - errorLog "!! Download failed with code ${CODE}, \"${ERROR}\"" - cronexit ${CODE} + error "Download failed with code ${CODE}:" + cat "${FILE_WGETLOG}" >&2 + exit ${CODE} fi - infoLog "OK" + info "File downloaded" fi sha1sum --status -c "${FILE_SHA}" if [ $? -ne 0 ]; then - errorLog "Downloaded file corrupt. Try again." - cronexit 4 + error "Downloaded file corrupt. Try again." + exit 4 fi if [ ! -z "${PLEXSERVER}" -a "${AUTOINSTALL}" = "yes" ]; then # Check if server is in-use before continuing (thanks @AltonV, @hakong and @sufr3ak)... if running ${PLEXSERVER} ${TOKEN} ${PLEXPORT}; then - errorLog "Server ${PLEXSERVER} is currently being used by one or more users, skipping installation. Please run again later" - cronexit 6 + error "Server ${PLEXSERVER} is currently being used by one or more users, skipping installation. Please run again later" + exit 6 fi fi @@ -827,15 +684,15 @@ fi if [ "${AUTODELETE}" = "yes" ]; then if [ "${AUTOINSTALL}" = "yes" ]; then rm -rf "${DOWNLOADDIR}/${FILENAME}" - infoLog "Deleted \"${FILENAME}\"" + info "Deleted \"${FILENAME}\"" else - infoLog "Will not auto delete without [-a] auto install" + info "Will not auto delete without [-a] auto install" fi fi if [ "${AUTOSTART}" = "yes" ]; then if [ "${REDHAT}" = "no" ]; then - warningLog "The AUTOSTART [-s] option may not be needed on your distribution." + warn "The AUTOSTART [-s] option may not be needed on your distribution." fi # Check for systemd if hash systemctl 2>/dev/null; then @@ -845,9 +702,9 @@ if [ "${AUTOSTART}" = "yes" ]; then elif [ -x /etc/init.d/plexmediaserver ]; then /etc/init.d/plexmediaserver start else - errorLog "AUTOSTART was specified but no startup scripts were found for 'plexmediaserver'." - cronexit 1 + error "AUTOSTART was specified but no startup scripts were found for 'plexmediaserver'." + exit 1 fi fi -cronexit 0 +exit 0