#!/usr/bin/ruby
# DO NOT SET THIS TO /usr/bin/env ..anything...
# BZ 901449: SELinux prevents using "/usr/bin/env ruby".
# BZ 912215: SELinux prevents usint "/usr/bin/env oo-ruby".

require 'optparse'
require 'etc'
require 'rubygems'
require 'open4'

# each value begins with the subsystem name and a dot (.)
# extract the subsystem name and check it against the list

# When combined with selinux, this script will allow users to read their own
# cgroups entries but not other users cgroups entries


def usage
  puts <<EOF
usage: oo-cgroup-read [-h|--help] <attrname>

attrname: the name of the cgroup attribute requested
          or "all" to list all attribute names.
EOF

end


# Check that the user didn't send any nasty strings at us.
def scrub_input(instring)
  unless instring =~ /\A[a-zA-Z0-9\.\-_]*\z/
    puts "#{instring} is an invalid attribute"
    exit 1
  end
end


#
# Alternate method which does not call cgget.  Waiting on an
# SELinux policy update for RHEL 6.4 which allows this to work.
# Ref: https://bugzilla.redhat.com/show_bug.cgi?id=912215
#
def get_cgroup_attribute_nocgget(attribute, username)
  begin
    value = nil
    File.open('/proc/mounts', File::RDONLY) do |mounts|
      mounts.each do |l|
        if l =~ /^\S+\s+(\S+)\s+cgroup\s+/
          begin
            File.open(File.join($1, "openshift", username, attribute), File::RDONLY) do |cgfile|
              value = cgfile.read.strip
            end
          rescue
          end
        end
      end
    end
  rescue
  end
  if value.nil?
    $stderr.puts "Could not find attribute #{attribute}"
    exit 2
  end
  value
end

def get_cgroup_attribute_names_nocgget(username)
  begin
    names=[]
    File.open('/proc/mounts', File::RDONLY) do |mounts|
      mounts.each do |l|
        if l =~ /^\S+\s+(\S+)\s+cgroup\s+/
          begin
            cgpath=File.join($1, "openshift", username)
            Dir.entries(cgpath).select { |f|
              not f.start_with?(".")
            }.each { |name|
              begin
                File.open(File.join(cgpath, name), File::RDONLY) { |f| f.read }
                names << name
              rescue Errno::EACCES
              end
            }
          rescue
          end
        end
      end
    end
  end
  names.sort.uniq
end


#
# Call cgget to retrieve the value for the user.
# cgget does not set a return flag on failure to find a value or error
# You have to check the stdout
#
def get_cgroup_attribute_cgget(attribute, username)
  cmd = "cgget -n -v -r #{attribute} /openshift/#{username}"
  # This would be simple, but we can't catch errors
  #value = %x[%{cmd}]
  pid, stdin, stdout, stderr = open4(cmd)

  # get the error string (if any).  Remove trailing newlines.
  error = stderr.read.strip

  if not error == "" then
    $stderr.puts "Could not find attribute #{attribute}"
    $stderr.puts error
    exit 2
  end

  # Get the output string. Remove trailing newlines (just one)
  value = stdout.read.strip
end

def get_cgroup_attribute_names_cgget(username)
  names=[]

  cmd = "cgget -n -a /openshift/#{username}"
  pid, stdin, stdout, stderr = open4(cmd)

  stdout.each do |l|
    if l=~/^(\w+)\:/
      names << $1
    end
  end

  names.sort.uniq
end

# Set to the proper implementation for this platform
def get_cgroup_attribute(*args)
  get_cgroup_attribute_nocgget(*args)
end

def get_cgroup_attribute_names(*args)
  get_cgroup_attribute_names_nocgget(*args)
end

#
# Check for a help request from the user
#
# Only check for help.
#
def parse_options
  options = {}

  optparse = OptionParser.new do |opts|
    opts.banner = "Usage: #{File.basename($0)} [-h|--help] <attrname>"

    # Help
    opts.on('-h', '--help', 'Display usage information') do
      usage
      exit
    end
  end

  optparse.parse!
end
  

# Check for help request
parse_options

# What user is making the request?
euid = Process::Sys.geteuid
username = Etc.getpwuid(euid).name

# What attribute are they asking for?
attrname = ARGV[0]

if attrname == nil then
  $stderr.puts "ERROR: no attribute name requested"
  usage
  exit 2
end

scrub_input attrname
if attrname=="all"
  names = get_cgroup_attribute_names username
  puts names.join("\n")
else
  value = get_cgroup_attribute attrname, username
  puts value
end
