#!/bin/bash

# Allow live logs to grow this big in total...
LIVE_TOTAL_MB_TRIGGER=36 # MiBytes

# ...then use logrotate on the biggest ones to end up under this size:
LIVE_TOTAL_MB_TARGET=12 # MiBytes

# A hook so we can override the numbers above without having to edit this file.
# (The extra config file is also used by the delete_old_logs_by_space script.)
[ -r /etc/sysconfig/logrotate ] && . /etc/sysconfig/logrotate

# Regardless of size of individual live log files, run logrotate only if the
# total of them is over a threshold, LIVE_TOTAL_MB_TRIGGER.
# Therefore first we must find the current total size.

# Count files not dirs, not recursing, excluding files not subject to rotation.
# (We could specify the list of relevant log files rather than using find, but
# then we would have to maintain that list in future.)

find_live() {
  find /var/log /var/log/openvswitch /var/log/blktap /var/log/xen \
    -maxdepth 1 -type f ! -name '*.tmp' ! -name '*.gz' ! -name lastlog \
    ! -name faillog ! -name '*dmesg' ! -regex '.*\.[0-9]+'
}

live_megabytes=$(find_live \
    | xargs --delimiter='\n' du -m --total 2>/dev/null \
    | tail -1 | cut -f 1) # last line from du has total as first field

if [ $live_megabytes -ge $LIVE_TOTAL_MB_TRIGGER ]; then

    # Calculate a suitable size threshold for logrotate to use, so as to
    # rotate the smallest number of files needed to bring the total size
    # under a target LIVE_TOTAL_KB_TARGET. (Note that the logrotate size
    # directive will rotate a file of size greater than OR EQUAL TO the
    # specified size, even though this is not explicit in the man page.)

    # Alternatively we could use a fixed size threshold of LIVE_TOTAL_KB_TARGET
    # divided by the number of live log files (about twenty), but this would
    # tend to rotate files more often than needed, leading to larger numbers of
    # archived files to manage. In the worst case (with all log files the same
    # size as each other, growing at the same rate) the two approaches would
    # work out the same.

    LIVE_TOTAL_KB_TARGET=$((LIVE_TOTAL_MB_TARGET * 1024)) # kiBytes
    smallest_to_rotate=$(find_live |\
      xargs --delimiter='\n' du -k 2>/dev/null |\
      sort -g |\
      awk 'BEGIN {tot=0}
          {tot += $1}
          tot > '$LIVE_TOTAL_KB_TARGET' {print $2; exit}')

    # Find the size according to logrotate's definition of size, rather than
    # the (larger) amount of disc space used up. (Note that logrotate will
    # rotate a file that matches a size directive exactly, even though the
    # man-page says only if greater than.)
    set -- $(du --block-size=1 --apparent-size "$smallest_to_rotate" 2>/dev/null)
    threshold=$1 # bytes

    /usr/bin/logger -t logrotate \
      "${live_megabytes}MB >= ${LIVE_TOTAL_MB_TRIGGER}MB so running logrotate with size $((${threshold}/1024))k (${smallest_to_rotate})"

    # Regrettably, logrotate cannot accept configuration on the command line,
    # only from a file. Also it cannot take standard input as a configuration
    # file, so we have to create a real file. Since this script is for freeing
    # disc space, we want it to work even when the disc is full. Therefore put
    # the temporary file on /dev/shm which is backed by memory not disc.
    # (Yes, this is inelegant.)
    echo "size ${threshold}" > /dev/shm/logrotate_size.conf

    # Later configuration files override earlier ones but global directives do
    # not apply to logfile-specific sections in earlier configuration files.
    /usr/sbin/logrotate /dev/shm/logrotate_size.conf /etc/logrotate-xenserver.conf
    EXITVALUE=$?
    if [ $EXITVALUE != 0 ]; then
        /usr/bin/logger -t logrotate "ALERT exited abnormally with [$EXITVALUE]"
    fi

    rm -f /dev/shm/logrotate_size.conf

fi
