Limiting the CPU time of program started via SYSTEM()

Let's say I have a ruby script which calls system() to run some
arbitrary program. Since that program might spiral off into some evil
infinite-loop, is there some way for me to say "execute this, but for
no longer than 2 seconds of CPU time"? In the C-world I might use
something like setrlimit, but I'm not sure how easy that would be to
do from within a ruby script.

Note that I don't want to limit the CPU time of the script itself. I
just want to limit the CPU time for specific applications that I
execute via system().

···

--
Garance Alistair Drosehn = drosihn@gmail.com
Senior Systems Programmer or gad@FreeBSD.org
Rensselaer Polytechnic Institute; Troy, NY; USA

maybe something like

   harp:~ > cat a.rb
   require 'timeout'
   def spawn cmd, sec
     pid = fork{ system cmd }
     begin
       Timeout::timeout(sec){ Process::waitpid2(pid).last }
     rescue Timeout::Error => e
       Process::kill 'TERM', pid rescue nil
       Process::kill 'KILL', pid rescue nil
       Process::waitpid2(pid).last
     end
   end

   p(spawn('sleep 2', 4))

   p(spawn('sleep 4', 2))

   harp:~ > ruby a.rb
   #<Process::Status: pid=6872,exited(0)>
   #<Process::Status: pid=6874,signaled(SIGTERM=15)>

hth.

-a

···

On Wed, 15 Jun 2005, Garance A Drosehn wrote:

Let's say I have a ruby script which calls system() to run some
arbitrary program. Since that program might spiral off into some evil
infinite-loop, is there some way for me to say "execute this, but for
no longer than 2 seconds of CPU time"? In the C-world I might use
something like setrlimit, but I'm not sure how easy that would be to
do from within a ruby script.

Note that I don't want to limit the CPU time of the script itself. I
just want to limit the CPU time for specific applications that I
execute via system().

--

email :: ara [dot] t [dot] howard [at] noaa [dot] gov
phone :: 303.497.6469
My religion is very simple. My religion is kindness.
--Tenzin Gyatso

===============================================================================

I'm going to give a general Unix programming answer and then some
comments about doing it in Ruby.

The basic technique is to fork a child process, have the child
process use setrlimit() to establish the resource limitations and
then have the child fork *again*. The "grandchild" process then
does the actual work subject to the limitations set by the child
process. The child process can exit once the grandchild has started.
This works because resource limits are inherited across a fork.

A quick and dirty way to accomplish all this in Ruby:

system(%q{
     echo $$; # show the current pid
     ulimit -a; # show the current limits
     ulimit -t 1; # change the CPU time limit to 1 sec
     sh -c 'echo $$; ulimit -a' # start a new process and show limits
})

Note, you can't use this technique to change the limits of the
current process because system does its thing in a child process, not
in the process that called system.

If you wanted to do all this within Ruby (i.e. without using system)
you are stuck because the standard Process class doesn't support
setrlimit (and the related getrlimit). Google has a link to an
RAA project that no longer exists at RAA called 'resource' that
looks like it implemented these functions. Maybe someone else is
aware of a similar project. If you had setrlimit you could do
something like:

puts "main: #{Process.pid}"
unless child_pid = fork
   puts "child: #{Process.pid}"
   # setrlimit(....)
   unless grand_child = fork
     puts "grandchild: #{Process.pid}"
     puts "grandchild: doing the work"
     exit(0)
   end
   puts "grandchild: exited: #{Process.wait}"
   exit(0)
end
puts "child: exited: #{Process.wait}"

···

On Jun 14, 2005, at 10:19 PM, Garance A Drosehn wrote:

Let's say I have a ruby script which calls system() to run some
arbitrary program. Since that program might spiral off into some evil
infinite-loop, is there some way for me to say "execute this, but for
no longer than 2 seconds of CPU time"? In the C-world I might use
something like setrlimit, but I'm not sure how easy that would be to
do from within a ruby script.

In article <97880ae0506141918e56d386@mail.gmail.com>,
  Garance A Drosehn <drosihn@gmail.com> writes:

Let's say I have a ruby script which calls system() to run some
arbitrary program. Since that program might spiral off into some evil
infinite-loop, is there some way for me to say "execute this, but for
no longer than 2 seconds of CPU time"? In the C-world I might use
something like setrlimit, but I'm not sure how easy that would be to
do from within a ruby script.

Note that I don't want to limit the CPU time of the script itself. I
just want to limit the CPU time for specific applications that I
execute via system().

Ruby 1.9 has Process.setrlimit (and Process.getrlimit).

% ruby -e 'Process.wait fork { Process.setrlimit Process::RLIMIT_CPU, 2, 2; exec "yes > /dev/null" }'

···

--
Tanaka Akira

Hi,

At Wed, 15 Jun 2005 12:40:47 +0900,
Ara.T.Howard wrote in [ruby-talk:145444]:

> Let's say I have a ruby script which calls system() to run some
> arbitrary program. Since that program might spiral off into some evil
> infinite-loop, is there some way for me to say "execute this, but for
> no longer than 2 seconds of CPU time"? In the C-world I might use
> something like setrlimit, but I'm not sure how easy that would be to
> do from within a ruby script.

Resouce module is in "rough".
<http://www.ruby-lang.org/cgi-bin/cvsweb.cgi/rough/ext/resource/&gt;

maybe something like

   harp:~ > cat a.rb
   require 'timeout'
   def spawn cmd, sec
     pid = fork{ system cmd }
     begin
       Timeout::timeout(sec){ Process::waitpid2(pid).last }
     rescue Timeout::Error => e
       Process::kill 'TERM', pid rescue nil

You should give a chance to terminate gently to the child process, I
guess.

···

       Process::kill 'KILL', pid rescue nil

--
Nobu Nakada

never used it, but i did notice "rlimit" port in RPAbase with
#getrlimit

Tanaka Akira wrote:

···

In article <97880ae0506141918e56d386@mail.gmail.com>,
  Garance A Drosehn <drosihn@gmail.com> writes:

> Let's say I have a ruby script which calls system() to run some
> arbitrary program. Since that program might spiral off into some evil
> infinite-loop, is there some way for me to say "execute this, but for
> no longer than 2 seconds of CPU time"? In the C-world I might use
> something like setrlimit, but I'm not sure how easy that would be to
> do from within a ruby script.
>
> Note that I don't want to limit the CPU time of the script itself. I
> just want to limit the CPU time for specific applications that I
> execute via system().

Ruby 1.9 has Process.setrlimit (and Process.getrlimit).

% ruby -e 'Process.wait fork { Process.setrlimit Process::RLIMIT_CPU, 2, 2; exec "yes > /dev/null" }'
--
Tanaka Akira

yes, very true. in 'real-life' the code from my lib is

   module Util

···

On Wed, 15 Jun 2005, nobuyoshi nakada wrote:

You should give a chance to terminate gently to the child process, I
guess.

       Process::kill 'KILL', pid rescue nil

     #
     # returns true if pid is running, false otherwise
     #
       def alive pid
#--{{

         pid = Integer("#{ pid }")
         begin
           Process::kill 0, pid
           true
         rescue Errno::ESRCH
           false
         end
#--}}}
       end
       alias alive? alive
     #
     # brutally shut down a process. opts can contain the keys 'signals' which
     # should be a list of signals used to send to the process and the key
     # 'suspend' which is the amount of time to wait after firing each signal
     # before seeing if the process is dead. the defaults are %w(TERM QUIT KILL)
     # and 4 respectively
     #
       def maim(pid, opts = {})
#--{{{
         sigs = Util::getopt 'signals', opts, %w(SIGTERM SIGQUIT SIGKILL)
         suspend = Util::getopt 'suspend', opts, 4
         pid = Integer("#{ pid }")
         existed = false
         sigs.each do |sig|
           begin
             Process::kill(sig, pid)
             existed = true
           rescue Errno::ESRCH
             unless existed
               return nil
             else
               return true
             end
           end
           sleep 0.1
           return true unless Util::alive?(pid)
           sleep suspend
           return true unless Util::alive?(pid)
         end
         return(not alive?(pid))
#--}}}
       end
     #
     # look up key in hash as key, then string of key, then intern of key -
     # returning the value or, if key is not found, nil or default
     #
       def getopt opt, hash, default = nil
#--{{{
         key = opt
         return hash[key] if hash.has_key? key
         key = "#{ key }"
         return hash[key] if hash.has_key? key
         key = key.intern
         return hash[key] if hash.has_key? key
         return default
#--}}}
       end
     end

but that's not so nice to post :wink:

-a
--

email :: ara [dot] t [dot] howard [at] noaa [dot] gov
phone :: 303.497.6469
My religion is very simple. My religion is kindness.
--Tenzin Gyatso

===============================================================================