From 5bebbfad91f574603334fa4f98a35fd8e32ae588 Mon Sep 17 00:00:00 2001 From: David Coppit Date: Wed, 24 Jun 2015 00:15:38 -0400 Subject: [PATCH] Change the container to be a general inotify container. --- Dockerfile | 6 +- monitor.sh | 133 +++++++++++++++++++++++++++++++++++++++++++++ sagetv-rescan.conf | 16 ------ sample.conf | 28 ++++++++++ start.sh | 115 +++++++++++---------------------------- 5 files changed, 197 insertions(+), 101 deletions(-) create mode 100755 monitor.sh delete mode 100644 sagetv-rescan.conf create mode 100644 sample.conf diff --git a/Dockerfile b/Dockerfile index b266090..e62e801 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,10 +14,12 @@ RUN set -x \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* -VOLUME ["/media", "/config"] +VOLUME ["/config", \ + "/watch1", "/watch2", "/watch3", "/watch4", "/watch5", "/watch6", "/watch7", "/watch8", "/watch9", "/watch10", \ + "/watch11", "/watch12", "/watch13", "/watch14", "/watch15", "/watch16", "/watch17", "/watch18", "/watch19", "/watch20"] # Add default config file -ADD sagetv-rescan.conf /root/sagetv-rescan.conf +ADD sample.conf /root/sample.conf # Add scripts ADD start.sh /root/start.sh diff --git a/monitor.sh b/monitor.sh new file mode 100755 index 0000000..ccf4313 --- /dev/null +++ b/monitor.sh @@ -0,0 +1,133 @@ +#!/bin/bash + +CONFIG_FILE=$1 +NAME=$(basename $CONFIG_FILE .conf) + +function ts { + echo [`date '+%b %d %X' $NAME: `] +} + +echo "$(ts) Starting monitor for $CONFIG_FILE" + +tr -d '\r' < $CONFIG_FILE > /tmp/$NAME.conf + +. /tmp/$NAME.conf + +if [[ ! -d "$WATCH_DIR" ]]; then + echo "$(ts) WATCH_DIR specified in $CONFIG_FILE must be a directory." + exit 1 +fi + +if [[ ! "$SETTLE_DURATION" =~ ^([0-9]{1,2}:){0,2}[0-9]{1,2}$ ]]; then + echo "$(ts) SETTLE_DURATION must be defined in $CONFIG_FILE as HH:MM:SS or MM:SS or SS." + exit 1 +fi + +if [[ ! "$MAX_WAIT_TIME" =~ ^([0-9]{1,2}:){0,2}[0-9]{1,2}$ ]]; then + echo "$(ts) MAX_WAIT_TIME must be defined in $CONFIG_FILE as HH:MM:SS or MM:SS or SS." + exit 1 +fi + +if [[ ! "$MIN_PERIOD" =~ ^([0-9]{1,2}:){0,2}[0-9]{1,2}$ ]]; then + echo "$(ts) MIN_PERIOD must be defined in $CONFIG_FILE as HH:MM:SS or MM:SS or SS." + exit 1 +fi + +if [ -z "$COMMAND" ]; then + echo "$(ts) COMMAND must be defined in $CONFIG_FILE" + exit 1 +fi + +to_seconds () { + readarray elements < <(echo $1 | sed 's/:/\n/g' | tac) + + SECONDS=0 + POWER=1 + + for (( i=0 ; i<${#elements[@]}; i++ )) ; do + SECONDS=$(( 10#$SECONDS + 10#${elements[i]} * 10#$POWER )) + POWER=$(( 10#$POWER * 60 )) + done + + echo "$SECONDS" +} + +SETTLE_DURATION=$(to_seconds $SETTLE_DURATION) +MAX_WAIT_TIME=$(to_seconds $MAX_WAIT_TIME) +MIN_PERIOD=$(to_seconds $MIN_PERIOD) + +pipe=$(mktemp -u) +mkfifo $pipe + +echo "$(ts) Waiting for changes..." +inotifywait -m -q --format '%e %f' /media >$pipe & + +last_run_time=0 + +while true +do + if read RECORD + then + EVENT=$(echo "$RECORD" | cut -d' ' -f 1) + FILE=$(echo "$RECORD" | cut -d' ' -f 2-) + +# echo "$RECORD" +# echo " EVENT=$EVENT" +# echo " FILE=$FILE" + + if [ "$EVENT" == "CREATE,ISDIR" ] + then + echo "$(ts) Detected new directory: $FILE" + elif [ "$EVENT" == "CLOSE_WRITE,CLOSE" ] + then + echo "$(ts) Detected new file: $FILE" + elif [ "$EVENT" == "MOVED_TO" ] + then + echo "$(ts) Detected moved file: $FILE" + else + continue + fi + + # Monster up as many events as possible, until we hit the either the settle duration, or the max wait threshold. + start_time=$(date +"%s") + + while true + do + if read -t $SETTLE_DURATION RECORD + then + end_time=$(date +"%s") + + if [ $(($end_time-$start_time)) -gt $MAX_WAIT_TIME ] + then + echo "$(ts) Input directory didn't stabilize after $MAX_WAIT_TIME seconds. Triggering command anyway." + break + fi + else + echo "$(ts) Input directory stabilized for $SETTLE_DURATION seconds. Triggering command." + break + fi + done + + time_since_last_run=$(($(date +"%s")-$last_run_time)) + if [ $time_since_last_run -lt $MIN_PERIOD ] + then + remaining_time=$(($MIN_PERIOD-$time_since_last_run)) + + echo "$(ts) Waiting an additional $remaining_time seconds before running command" + fi + + # Process events while we wait for $MIN_PERIOD to expire + while [ $time_since_last_run -lt $MIN_PERIOD ] + do + remaining_time=$(($MIN_PERIOD-$time_since_last_run)) + + read -t $remaining_time RECORD + + time_since_last_run=$(($(date +"%s")-$last_run_time)) + done + + echo "$(ts) Running command" + $COMMAND + last_run_time=$(date +"%s") + fi +done <$pipe diff --git a/sagetv-rescan.conf b/sagetv-rescan.conf deleted file mode 100644 index 1104999..0000000 --- a/sagetv-rescan.conf +++ /dev/null @@ -1,16 +0,0 @@ -# After seeing the first event, we don't notify SageTV until we've processed any other events that might be happening. -# Otherwise we'll end up notifying SageTV 100 times for the case where someone moves 100 files into the media directory. - -# If we don't see any events for $SETTLE_DURATION time, assume that it's safe to notify SageTV. Format is HH:MM:SS, -# with HH and MM optional. -SETTLE_DURATION=5 - -# However, if we see continuous changes for longer than $MAX_WAIT_TIME with no break of $SETTLE_DURATION or more, then -# go ahead and notify SageTV. Otherwise we might be waiting forever for the directory to stop changing. Format is HH:MM:SS, -# with HH and MM optional. -MAX_WAIT_TIME=05:00 - -# This is the command to run to notify SageTV. Replace YOUR_SERVER with a proper IP address. This command assumes that -# you've installed the plugin called "sagex-services - SageTV Remote API Services". -NOTIFY_COMMAND="wget -nv -O /dev/null --auth-no-challenge \ - http://sage:frey@YOUR_SERVER:8080/sagex/api?c=RunLibraryImportScan&1=" diff --git a/sample.conf b/sample.conf new file mode 100644 index 0000000..4920d80 --- /dev/null +++ b/sample.conf @@ -0,0 +1,28 @@ +# This is a sample config file, for notifying a SageTV server running on 192.168.1.102 that changes have occurred to the +# media import folder. SageTV will then rescan the folder, adding new media to its library. + +# The directory to watch +WATCH_DIR=/dir1 + +# If we don't see any events for $SETTLE_DURATION time, assume that it's safe to run the command. Format is HH:MM:SS, +# with HH and MM optional. +SETTLE_DURATION=5 + +# However, if we see a stream of changes for longer than $MAX_WAIT_TIME with no break of $SETTLE_DURATION or more, then +# go ahead and run the command. Otherwise we might be waiting forever for the directory to stop changing. Format is +# HH:MM:SS, with HH and MM optional. +MAX_WAIT_TIME=05:00 + +# After running the command, wait at least this long before running it again, even if $SETTLE_DURATION time has passed +# after change. This controls the maximum frequency of the command. +MIN_PERIOD=10:00 + +# This is the command to run when a change is detected. If this command runs quickly and triggers some other +# long-running task, you want to be sure that rerunning this command while the long-running task is in progress won't +# cause any problems. You can also allow this command to wait until the work is done, which will cause it to naturally +# limit its run frequency. (But you might still want to set a longer MIN_PERIOD above in order to prevent back-to-back +# execution.) + +# In this example, we call a SageTV remote API to trigger a rescan of the imported media library. The command assumes +# that we've installed the plugin called "sagex-services - SageTV Remote API Services". +COMMAND="wget -nv -O /dev/null --auth-no-challenge http://sage:frey@192.168.1.102:8080/sagex/api?c=RunLibraryImportScan&1=" diff --git a/start.sh b/start.sh index 666e771..60505c9 100755 --- a/start.sh +++ b/start.sh @@ -1,108 +1,57 @@ #!/bin/bash function ts { - echo [`date '+%b %d %X'`] + echo [`date '+%b %d %X' MASTER:`] } -echo "$(ts) Starting SageTV-Rescan container" +echo "$(ts) Starting master controller" -# Search for custom config file, if it doesn't exist, copy the default one -if [ ! -f /config/sagetv-rescan.conf ]; then - echo "$(ts) Creating config file and exiting. Check the settings, then rerun the container." - cp /root/sagetv-rescan.conf /config/sagetv-rescan.conf - chmod a+w /config/sagetv-rescan.conf +if [ -f /config/sample.conf ]; then + echo "$(ts) /config/sample.conf exists. Rename it, check the settings, then rerun the container. Exiting." exit 1 fi -tr -d '\r' < /config/sagetv-rescan.conf > /tmp/sagetv-rescan.conf +readarray -t CONFIG_FILES < <(ls /config/*.conf) -. /tmp/sagetv-rescan.conf - -if [[ ! "$SETTLE_DURATION" =~ ^([0-9]{1,2}:){0,2}[0-9]{1,2}$ ]]; then - echo "$(ts) SETTLE_DURATION must be defined in sagetv-rescan.conf as HH:MM:SS or MM:SS or SS." +# If there is no config file copy the default one +if [[ "$CONFIG_FILES" == "" ]] +then + echo "$(ts) Creating sample config file. Rename it, check the settings, then rerun the container. Exiting." + cp /root/sample.conf /config/sample.conf + chmod a+w /config/sample.conf exit 1 fi -if [[ ! "$MAX_WAIT_TIME" =~ ^([0-9]{1,2}:){0,2}[0-9]{1,2}$ ]]; then - echo "$(ts) MAX_WAIT_TIME must be defined in sagetv-rescan.conf as HH:MM:SS or MM:SS or SS." - exit 1 -fi +PIDS=() -if [ -z "$NOTIFY_COMMAND" ]; then - echo "$(ts) NOTIFY_COMMAND must be defined in sagetv-rescan.conf" - exit 1 -elif [ "$NOTIFY_COMMAND" = "YOUR_SERVER" ]; then - echo "$(ts) Please replace \"YOUR_SERVER\" in sagetv-rescan.conf" - exit 1 -fi +for $CONFIG_FILE in "${CONFIG_FILES[@]}" +do + echo "$(ts) Launching monitor for $CONFIG_FILE" + /root/monitor.sh $CONFIG_FILE & + PIDS+=($!) +done -to_seconds () { - readarray elements < <(echo $1 | sed 's/:/\n/g' | tac) - - SECONDS=0 - POWER=1 - - for (( i=0 ; i<${#elements[@]}; i++ )) ; do - SECONDS=$(( 10#$SECONDS + 10#${elements[i]} * 10#$POWER )) - POWER=$(( 10#$POWER * 60 )) - done - - echo "$SECONDS" -} - -SETTLE_DURATION=$(to_seconds $SETTLE_DURATION) -MAX_WAIT_TIME=$(to_seconds $MAX_WAIT_TIME) - -pipe=$(mktemp -u) -mkfifo $pipe - -echo "$(ts) Waiting for changes..." -inotifywait -m -q --format '%e %f' /media >$pipe & +# Sleep for a second to allow the monitors to check their config files +sleep 1 while true do - if read RECORD - then - EVENT=$(echo "$RECORD" | cut -d' ' -f 1) - FILE=$(echo "$RECORD" | cut -d' ' -f 2-) - -# echo "$RECORD" -# echo " EVENT=$EVENT" -# echo " FILE=$FILE" - - if [ "$EVENT" == "CREATE,ISDIR" ] + for ((i = 0; i < ${#PIDS[@]}; i++)) + do + if ps -p ${PIDS[$i]} > /dev/null then - echo "$(ts) Detected new directory: $FILE" - elif [ "$EVENT" == "CLOSE_WRITE,CLOSE" ] - then - echo "$(ts) Detected new file: $FILE" - elif [ "$EVENT" == "MOVED_TO" ] - then - echo "$(ts) Detected moved file: $FILE" - else continue fi - # Monster up as many events as possible, until we hit the either the settle duration, or the max wait threshold. - start_time=$(date +"%s") + echo "$(ts) Monitor for ${CONFIG_FILES[$i]} has died (PID ${PIDS[$i]}). Killing other monitors and exiting." - while true - do - if read -t $SETTLE_DURATION RECORD - then - end_time=$(date +"%s") - - if [ $(($end_time-$start_time)) -gt $MAX_WAIT_TIME ] - then - echo "$(ts) Input directory didn't stabilize after $MAX_WAIT_TIME seconds. Notifying SageTV anyway." - break - fi - else - echo "$(ts) Input directory stabilized for $SETTLE_DURATION seconds. Notifying SageTV." - break - fi + for $PID in "${PIDS[@]}" + do + kill -9 $PID >/dev/null 2>&1 done - $NOTIFY_COMMAND - fi -done <$pipe + exit 2 + done + + sleep 60 +done