wishi wishi - 5 months ago 47
Linux Question

inotifywait with a fifo queue in Bash

I wrote a small Bash script which uses inotify-tools and the inotify interface. My problem is, that one of the commands in this function can block the execution until it's finished. This way the function gets stuck.

To solve this I would like to queue up detected files (by the close event), and read the queue from another function. Does anybody have a clue how to do this in Bash?

The variables in the following are simple strings to find directories or to assign file names.

inotifywait -mrq -e close --format %w%f /some/dir/ | while read FILE
do
NAME=$(echo $CAP)_"`date +"%F-%H-%M-%S"`.pcap"
logger -i "$FILE was just closed"
# cp "$FILE" "$DATA/$CAP/$ENV/$NAME"
rsync -avz --stats --log-file=/root/rsync.log "$FILE" "$DATA/$CAP/$ENV/$NAME" >> /root/rsync_stats.log
RESULT=$?
if [ $RESULT -eq 0 ] ; then
logger -i "Success: $FILE copied to SAN $DATA/$CAP/$ENV/$NAME, code $RESULT"
else
logger -i "Fail: $FILE copy failed to SAN for $DATA/$CAP/$ENV/$NAME, code $RESULT"
fi

rm "$FILE"
RESULT=$?
if [ $RESULT -eq 0 ] ; then
logger -i "Success: deletion successfull for $FILE, code $RESULT"
else
logger -i "Fail: deletion failed for $FILE on SSD, code $RESULT"
fi

do_something()
logger -i "$NAME was handled"
# for stdout
echo "`date`: Moved file"
done


I am copying the files to a SAN volume which sometimes has answering time variations. That is the reason why this function can get stuck for a while. I replaced cp with Rsync because I need the throughput stats. Cp (from coreutils) apparently doesn't do this.

Answer

A couple of ideas:

1) You could use a named pipe as a limited-size queue:

mkfifo pipe

your_message_source | while read MSG
do
  #collect files in a pipe 
  echo "$MSG" >> pipe
done &

while read MSG 
do
 #Do your blocking work here
done < pipe

This would block on echo "$MSG" >> pipe when the pipe's buffer gets filled (you can get the size of that buffer with ulimit -p (multiply by 512). This might be sufficient for some cases.

2) You could use a file as a message queue and file lock it on each operation:

 #Feeder part
    your_message_source | while read MSG     
       do
            (
            flock 9
            echo "$MSG" >> file_based_queue 
            ) 9> file_based_queue 
       done &

   # Worker part
   while :
   do 
    #Lock shared queue and cut'n'paste it's content to the worker's private queue
    (
      flock 9
      cp file_based_queue workers_queue
      truncate -s0 file_based_queue   
    ) 9> file_based_queue

    #process private queue
    while read MSG 
    do
     #Do your blocking work here   
    done < workers_queue 
   done

You're only blocking inotifywait if you're in the worker loop in the (flock ... ) 9>file_based_queue subshell and after the flock command at the same time. You could have the queues in a RAMdisk (/dev/shm) to minimize the time you spend there so that you don't miss out on FS events.

3) Or you could use some bash interface to (or execute scripts in languages that have an interface to) database-backed message-queues or to the SysV message queue.

Comments