Skip to content

Instantly share code, notes, and snippets.

@cinsk
Last active August 29, 2015 14:02

Revisions

  1. cinsk revised this gist Jul 18, 2014. 1 changed file with 11 additions and 0 deletions.
    11 changes: 11 additions & 0 deletions chkprocs.rb
    100644 → 100755
    Original file line number Diff line number Diff line change
    @@ -93,6 +93,10 @@ def get()
    @result
    end

    def reset()
    @result = :OK
    end

    def set(result)
    idx_new = RESULT_TYPES.find_index(result)
    idx_old = RESULT_TYPES.find_index(@result)
    @@ -117,6 +121,8 @@ def help_and_exit
    -c, --classname NAME Set the name of the class to NAME.
    Uppercase of COMMAND_NAME is used by default.
    -l, --filelock FILE If FILE does not exist, do nothing but report ok.
    --help Show help messages and exit
    @@ -137,6 +143,7 @@ def help_and_exit
    result = Result.new
    class_name = nil
    num_expected = nil
    predicate_file = nil

    begin
    options =
    @@ -145,6 +152,7 @@ def help_and_exit
    [ '--nexpected', '-n', GetoptLong::REQUIRED_ARGUMENT ],
    [ '--fd', '-f', GetoptLong::REQUIRED_ARGUMENT ],
    [ '--mem', '-m', GetoptLong::REQUIRED_ARGUMENT ],
    [ '--filelock', '-l', GetoptLong::REQUIRED_ARGUMENT ],
    [ '--classname', '-c', GetoptLong::REQUIRED_ARGUMENT ])

    options.each do |opt, arg|
    @@ -158,6 +166,7 @@ def help_and_exit
    when '--fd' then fdopen_safe_percent = arg.to_f
    when '--mem' then mem_safe_percent = arg.to_f
    when '--classname' then class_name = arg
    when '--filelock' then predicate_file = arg
    when '--help' then help_and_exit
    end
    end
    @@ -202,6 +211,8 @@ def help_and_exit
    expected = num_expected
    expected = "??" unless num_expected

    result.reset if predicate_file and File.exists?(predicate_file)

    print "#{class_name} #{result} - #{procs.length}/#{expected} process(es); | "

    #puts " PID NLWP MEM ELAPSED FDS"
  2. cinsk revised this gist Jun 17, 2014. 1 changed file with 6 additions and 3 deletions.
    9 changes: 6 additions & 3 deletions chkprocs.rb
    Original file line number Diff line number Diff line change
    @@ -199,15 +199,18 @@ def help_and_exit
    result.set(:WARNING) if mem_safe_percent and info[:mem] >= mem_safe_percent
    end

    puts "#{class_name} #{result} |"
    expected = num_expected
    expected = "??" unless num_expected

    puts " PID NLWP MEM ELAPSED FDS"
    print "#{class_name} #{result} - #{procs.length}/#{expected} process(es); | "

    #puts " PID NLWP MEM ELAPSED FDS"
    procs.each_value do |info|
    fdinfo = if info[:fds][1] == Process::RLIM_INFINITY
    "#{info[:fds][0]}/unlimited"
    else
    "#{info[:fds][0]}/#{info[:fds][1]}"
    end

    puts "#{info[:pid].to_s.rjust(6)} #{info[:nlwp].to_s.rjust(6)} #{info[:mem].to_s.rjust(3)} #{info[:elapsed].rjust(12)} #{fdinfo}"
    puts "PID=#{info[:pid].to_s};NLWP=#{info[:nlwp].to_s};MEM=#{info[:mem].to_s};ELAPSED=#{info[:elapsed]};FDS=#{fdinfo};"
    end
  3. cinsk revised this gist Jun 13, 2014. 1 changed file with 2 additions and 1 deletion.
    3 changes: 2 additions & 1 deletion chkprocs.rb
    Original file line number Diff line number Diff line change
    @@ -49,7 +49,8 @@ def ps_by_command(cmdname)
    IO.popen("ps -C #{cmdname} -o pid=,etime=,nlwp=,%mem=") do |io|

    while line = io.gets
    toks = line.chomp.split(/ +/)
    toks = line.strip.chomp.split(/ +/)
    continue if toks.length != 4
    p = {}
    pid = toks[0].to_i
    p[:pid] = pid
  4. cinsk revised this gist Jun 13, 2014. 1 changed file with 6 additions and 4 deletions.
    10 changes: 6 additions & 4 deletions chkprocs.rb
    Original file line number Diff line number Diff line change
    @@ -16,8 +16,8 @@
    "Max address space" => Process::RLIMIT_AS,
    }

    fdopen_safe_percent = 0.9
    mem_safe_percent = 0.9
    fdopen_safe_percent = nil
    mem_safe_percent = nil

    def rlimits(pid = nil)
    pid = Process.pid unless pid
    @@ -192,8 +192,10 @@ def help_and_exit
    procs.each_value do |info|
    nopen = proc_fd_info info[:pid]
    info[:fds] = nopen
    result.set(:WARNING) if nopen[0].to_f / nopen[1] >= fdopen_safe_percent
    result.set(:WARNING) if info[:mem] >= mem_safe_percent


    result.set(:WARNING) if fdopen_safe_percent and nopen[0].to_f / nopen[1] >= fdopen_safe_percent
    result.set(:WARNING) if mem_safe_percent and info[:mem] >= mem_safe_percent
    end

    puts "#{class_name} #{result} |"
  5. cinsk created this gist Jun 13, 2014.
    210 changes: 210 additions & 0 deletions chkprocs.rb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,210 @@
    #!/usr/bin/env ruby

    require 'timeout'
    require 'getoptlong'

    RLIMIT_MAP = {
    "Max cpu time" => Process::RLIMIT_CPU,
    "Max file size" => Process::RLIMIT_FSIZE,
    "Max data size" => Process::RLIMIT_DATA,
    "Max stack size" => Process::RLIMIT_STACK,
    "Max core file size" => Process::RLIMIT_CORE,
    "Max resident set" => Process::RLIMIT_RSS,
    "Max processes" => Process::RLIMIT_NPROC,
    "Max open files" => Process::RLIMIT_NOFILE,
    "Max locked memory" => Process::RLIMIT_MEMLOCK,
    "Max address space" => Process::RLIMIT_AS,
    }

    fdopen_safe_percent = 0.9
    mem_safe_percent = 0.9

    def rlimits(pid = nil)
    pid = Process.pid unless pid

    lims = {}

    IO.foreach("/proc/#{pid}/limits") do |line|
    fields = line.chomp.split(/[[:space:]][[:space:]]+/)

    key = RLIMIT_MAP[fields[0]]
    if key
    soft = fields[1]
    hard = fields[2]

    soft = (soft == "unlimited") ? Process::RLIM_INFINITY : soft.to_i
    hard = (hard == "unlimited") ? Process::RLIM_INFINITY : hard.to_i

    lims[key] = [soft, hard]
    end
    end
    lims
    rescue
    nil
    end

    def ps_by_command(cmdname)
    ps = {}
    Timeout.timeout(1) do
    IO.popen("ps -C #{cmdname} -o pid=,etime=,nlwp=,%mem=") do |io|

    while line = io.gets
    toks = line.chomp.split(/ +/)
    p = {}
    pid = toks[0].to_i
    p[:pid] = pid
    p[:elapsed] = toks[1]
    p[:nlwp] = toks[2].to_i
    p[:mem] = toks[3].to_f
    ps[pid] = p
    end
    end
    end
    ps
    rescue Timeout::Error => ex
    puts "timeout: #{ex}"
    ps
    end

    def proc_fd_info(pid = nil)
    pid = Process.pid unless pid

    fds = 0
    Timeout.timeout(1) do
    IO.popen("ls /proc/#{pid}/fd | wc -l") do |io|
    fds = io.gets.to_i
    end
    end

    limits = rlimits(pid)

    [ fds, limits[Process::RLIMIT_NOFILE][0] ]
    end

    class Result
    RESULT_TYPES = [ :OK, :WARNING, :CRITICAL ]

    def initialize(result = :OK)
    @result = result
    end

    def get()
    @result
    end

    def set(result)
    idx_new = RESULT_TYPES.find_index(result)
    idx_old = RESULT_TYPES.find_index(@result)

    @result = result if idx_new > idx_old
    end

    def to_s
    return @result.to_s
    end
    end

    def help_and_exit
    msg = <<EOF
    Usage: #{File.basename($PROGRAM_NAME)} [OPTION...] COMMAND_NAME
    -n, --expected=N Set the expected number of processes to N
    -f, --fd=PERCENTAGE Set the state to WARNING if the open FDs percent
    is equal to or greater than PERCENTAGE
    -m, --mem=PERCENTAGE Set the state to WARNING if the memory usage
    is greater than PERCENTAGE
    -c, --classname NAME Set the name of the class to NAME.
    Uppercase of COMMAND_NAME is used by default.
    --help Show help messages and exit
    This program will examine the system for the process started with
    COMMAND_NAME. It reports in Nagios plugin format; OK iff the number
    of processes meets the condition provided by '-n' option. Otherwise
    it reports as either WARNING or CRITICAL.
    PERCENTAGE should be a floating point value between 0 and 1.
    Send a bug report to <cinsk dot shin at samsung dot com>
    EOF
    puts msg
    exit 0
    end

    result = Result.new
    class_name = nil
    num_expected = nil

    begin
    options =
    GetoptLong.new(
    [ '--help', GetoptLong::NO_ARGUMENT ],
    [ '--nexpected', '-n', GetoptLong::REQUIRED_ARGUMENT ],
    [ '--fd', '-f', GetoptLong::REQUIRED_ARGUMENT ],
    [ '--mem', '-m', GetoptLong::REQUIRED_ARGUMENT ],
    [ '--classname', '-c', GetoptLong::REQUIRED_ARGUMENT ])

    options.each do |opt, arg|
    case opt
    when '--nexpected'
    if arg.to_i < 1
    STDERR.write("#{File.basename($PROGRAM_NAME)}: invalid value for `-n'\n")
    exit 1
    end
    num_expected = arg.to_i
    when '--fd' then fdopen_safe_percent = arg.to_f
    when '--mem' then mem_safe_percent = arg.to_f
    when '--classname' then class_name = arg
    when '--help' then help_and_exit
    end
    end
    rescue => ex
    puts "ex: #{ex}"
    STDERR.write("#{File.basename($PROGRAM_NAME)}: Try `--help' for help\n")
    exit 1
    end

    # puts "argv: #{ARGV}"

    cmd_name = ARGV.shift

    unless cmd_name
    STDERR.write("#{File.basename($PROGRAM_NAME)}: argument required\n");
    exit 1
    end

    class_name = cmd_name.upcase unless class_name

    procs = {}

    while cmd_name
    procs.merge! ps_by_command(cmd_name)
    cmd_name = ARGV.shift
    end

    if num_expected
    result.set(:CRITICAL) if procs.length < num_expected
    result.set(:WARNING) if procs.length > num_expected
    end

    procs.each_value do |info|
    nopen = proc_fd_info info[:pid]
    info[:fds] = nopen
    result.set(:WARNING) if nopen[0].to_f / nopen[1] >= fdopen_safe_percent
    result.set(:WARNING) if info[:mem] >= mem_safe_percent
    end

    puts "#{class_name} #{result} |"

    puts " PID NLWP MEM ELAPSED FDS"
    procs.each_value do |info|
    fdinfo = if info[:fds][1] == Process::RLIM_INFINITY
    "#{info[:fds][0]}/unlimited"
    else
    "#{info[:fds][0]}/#{info[:fds][1]}"
    end

    puts "#{info[:pid].to_s.rjust(6)} #{info[:nlwp].to_s.rjust(6)} #{info[:mem].to_s.rjust(3)} #{info[:elapsed].rjust(12)} #{fdinfo}"
    end