#! /usr/bin/env ruby

USAGE = " 
  NAME
    RAILS_ROOT/script/sesswatch

  SYNOPSIS
    sesswatch (--verbose|v --noop|n --help|h) max=100_000 on=atime

  DESCRIPTION
    sesswatch keeps a rails sessions dir limited to a certain number of files.
    rather than deleting files older than a certain time, it simply deletes
    the oldest sessions first until no more that a given max amount occupies
    the directory.  this is advantageous because it limits disk usage and
    directory size while allowing users to keep their sessions as long as
    possible.

  EXAMPLES

    . show what would be done by default, actually delete nothing

      ./script/sesswatch --noop --verbose

    . keep only 42 files in the sessions dir

      ./script/sesswatch 42

    . delete files based on mtime rather than atime, keep only 50000 session
      lying around

      ./script/sesswatch 50000 mtime

    . a sample crontab line might be

      */30 * * * * /full/path/to/RAILS_ROOT/script/sesswatch

  INSTALL
    cd ./RAILS_ROOT/scripts/
    curl --remote-name http://s3.amazonaws.com/drawohara.com.ruby/sesswatch

"


# ruby built-in
#
  require 'fileutils'

# bootstrap into the rails app 
#
  RAILS_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
  Dir.chdir RAILS_ROOT

# naive argument parsing
#
  verbose = ARGV.delete('--verbose') || ARGV.delete('-v')
  noop = ARGV.delete('--noop') || ARGV.delete('-n')
  help = ARGV.delete('--help') || ARGV.delete('-h')
  max = Integer(ARGV.shift || 42_000)
  on = String(ARGV.shift || 'atime')

# spew usage if asked
#
  abort USAGE if help

# load all sessions and their stats sorted oldest first
#
  sessions = []

  Dir.glob('tmp/sessions/*') do |entry|
    begin
      next unless entry =~ %r/ruby_sess/
      stat = File.stat entry
      next unless stat.file?
      sessions << [ File.expand_path(entry), stat ]
    rescue Exception
      next
    end
  end

  sessions.sort!{|a,b| a.last.send(on) <=> b.last.send(on)}

# nuke sessions until we're below threshold, killing oldest first, don't
# assume we can delete every file, just try every one until we've met our goal
# - the move give a consistent view to rails if sessions are living on NFS
#
  n = sessions.size

  rm_f =
    if noop
      lambda do |pathname|
        STDOUT.puts pathname if verbose
        n -= 1
      end
    else
      lambda do |pathname|
        STDOUT.puts pathname if verbose
        tmp = pathname + ".sesswatch"
        begin
          FileUtils.mv pathname, tmp
          FileUtils.rm_f tmp
          n -= 1
        rescue Object
        end
      end
    end

  n.times do |i|
    break if n <= max
    pathname, stat = sessions[i]
    rm_f[ pathname ]
  end

# die if we can't clean up (for instance due to permissions issues)
#
  abort "could not clean up session dir!" unless n <= max