Traping signals on child processes

Discussion in 'Ruby' started by Mário Lopes, Jun 2, 2008.

  1. Hi,

    I currently have a Ruby process that spawns another process through a
    system() call in order to run an application from the command line. The
    child process should not be aborted by INT signals and therefore the
    parent process shall wait for it to finish before exiting.

    In order to do so I tried trapping the INT signal but since system runs
    in the foreground of the main process it seems that the application
    that's being called gets the INT signal nevertheless, despite being
    trapped in Ruby. The other approach I took was spawning a process in the
    background and then waiting for it to finish but this approach adds a
    lot of complexity due to shared code that can't be run twice.

    What's the best approach to accomplish this? For the sake of simplicity
    I'll leave a very simple example of what I want to do

    Start main process
    Start system/child process
    INT signals can't stop whatever the system call is doing
    End system/child process
    If INT signal detected, exit, otherwise, continue running
    End main process

    Any ideas or suggestions to accomplish such are most welcome.

    Thanks,

    Mário
    --
    Posted via http://www.ruby-forum.com/.
    Mário Lopes, Jun 2, 2008
    #1
    1. Advertising

  2. Mário Lopes

    ara.t.howard Guest

    On Jun 2, 2008, at 8:59 AM, M=E1rio Lopes wrote:

    > Hi,
    >
    > I currently have a Ruby process that spawns another process through a
    > system() call in order to run an application from the command line. =20=


    > The
    > child process should not be aborted by INT signals and therefore the
    > parent process shall wait for it to finish before exiting.
    >
    > In order to do so I tried trapping the INT signal but since system =20
    > runs
    > in the foreground of the main process it seems that the application
    > that's being called gets the INT signal nevertheless, despite being
    > trapped in Ruby. The other approach I took was spawning a process in =20=


    > the
    > background and then waiting for it to finish but this approach adds a
    > lot of complexity due to shared code that can't be run twice.
    >
    > What's the best approach to accomplish this? For the sake of =20
    > simplicity
    > I'll leave a very simple example of what I want to do
    >
    > Start main process
    > Start system/child process
    > INT signals can't stop whatever the system call is doing
    > End system/child process
    > If INT signal detected, exit, otherwise, continue running
    > End main process
    >
    > Any ideas or suggestions to accomplish such are most welcome.
    >
    > Thanks,
    >
    > M=E1rio



    trap( :INT ){ warn "can't do that" }

    fork { exec command }
    Process.wait

    if the child installs it's own signal handler you are hosed.

    a @ http://codeforpeople.com/
    --
    we can deny everything, except that we have the possibility of being =20
    better. simply reflect on that.
    h.h. the 14th dalai lama
    ara.t.howard, Jun 2, 2008
    #2
    1. Advertising

  3. ara.t.howard wrote:
    > On Jun 2, 2008, at 8:59 AM, M�rio Lopes wrote:
    >
    >> in the foreground of the main process it seems that the application
    >> Start main process
    >> M�rio

    > trap( :INT ){ warn "can't do that" }
    >
    > fork { exec command }
    > Process.wait
    >
    > if the child installs it's own signal handler you are hosed.


    Tried that with the following example just for testing..

    fork { exec "echo 'start'; sleep 20; echo 'end'" }

    And if I issue a SIGINT the warning gets printed but the 'end' never
    gets printed meaning that the process ended abruptly. That's what I'm
    trying to avoid :-/

    Mário
    --
    Posted via http://www.ruby-forum.com/.
    Mário Lopes, Jun 2, 2008
    #3
  4. Mário Lopes

    ara.t.howard Guest

    On Jun 2, 2008, at 10:17 AM, M=E1rio Lopes wrote:

    >
    > Tried that with the following example just for testing..
    >
    > fork { exec "echo 'start'; sleep 20; echo 'end'" }
    >
    > And if I issue a SIGINT the warning gets printed but the 'end' never
    > gets printed meaning that the process ended abruptly. That's what I'm
    > trying to avoid :-/
    >
    > M=E1rio


    you have to avoid races, programs which install their own handlers =20
    (like ruby), and confusing the issue by shelling out three child =20
    processes in instead of one:

    cfp:~ > cat a.rb
    ready =3D :ready

    pipe =3D IO.pipe

    trap('INT'){ STDERR.puts "#{ Process.pid } killed" }

    pid =3D fork{
    pipe.first.close
    pipe =3D pipe.last
    pipe.puts ready
    sleep
    }

    trap 'INT', 'DEFAULT'

    pipe.last.close
    pipe =3D pipe.first
    ready =3D pipe.gets

    STDERR.puts "parent: #{ Process.pid }"
    STDERR.puts "child: #{ pid }"

    Process.kill 'INT', pid

    Process.waitpid pid, Process::WUNTRACED


    cfp:~ > ruby a.rb
    parent: 25227
    child: 25228
    25228 killed



    basically ruby is, by default, going to relay the signal to the child, =20=

    you need to catch it and do something sensible with it.


    a @ http://codeforpeople.com/
    --
    we can deny everything, except that we have the possibility of being =20
    better. simply reflect on that.
    h.h. the 14th dalai lama
    ara.t.howard, Jun 2, 2008
    #4
  5. > basically ruby is, by default, going to relay the signal to the child,
    > you need to catch it and do something sensible with it.


    I wonder whether I didn't get your example thoroughly or I just didn't
    make myself clear.

    What I'm precisely trying to do is trapping the SIGINT signal from
    getting to the child so it doesn't abort. I've been unable to do this. I
    can get it on the main process though but can't prevent it from being
    relayed to the child process.

    Thanks.

    Mário
    --
    Posted via http://www.ruby-forum.com/.
    Mário Lopes, Jun 2, 2008
    #5
  6. Mário Lopes

    Gary Wright Guest

    =20
    On Monday, June 02, 2008, at 01:40PM, "M=E1rio Lopes" <=
    m> wrote:
    >
    >What I'm precisely trying to do is trapping the SIGINT signal from=20
    >getting to the child so it doesn't abort. I've been unable to do this. I=

    =20
    >can get it on the main process though but can't prevent it from being=20
    >relayed to the child process.


    Does this code demonstrate what you are looking for? If
    you send an interrupt it should be caught by the main ruby process
    and ignored by the child processes.

    The code below arranges for the child processes to ignore the INT signal.

    If you want to arrange for the child process to not even receive the
    signal it gets more complicated. You've got to arrange for the child
    process to be within its own process group and you have to make
    sure that process group is detached from the terminal (has no
    controlling terminal). These concerns typically arise when you are
    trying to instantiate a daemon process. Even when you do that
    someone can explicitly send the INT signal to your process (assuming
    they have permissions).

    Anyway, here is the code for the simple case of ignoring the INT signal:

    puts "main: #{$$}"
    trap('INT') { puts 'int caught by main' }
    fork {
    trap 'INT', 'IGNORE' # forked process ignores INT
    puts "child before sleep: #{$$}"
    exec 'echo "exec command"; sleep 10; echo "exec command after sleep" '
    puts "child after sleep: #{$$}"
    }

    puts 'main program after fork'
    Process.wait
    Gary Wright, Jun 2, 2008
    #6
  7. Mário Lopes

    ara.t.howard Guest

    On Jun 2, 2008, at 11:36 AM, M=E1rio Lopes wrote:

    > What I'm precisely trying to do is trapping the SIGINT signal from
    > getting to the child so it doesn't abort. I've been unable to do =20
    > this. I
    > can get it on the main process though but can't prevent it from being
    > relayed to the child process.


    you need interject another process:

    #
    # Start main process
    # Start system/child process
    # INT signals can't stop whatever the system call is doing
    # End system/child process
    # If INT signal detected, exit, otherwise, continue running
    # End main process
    #

    def system_critical command
    signaled =3D false
    int =3D trap('INT'){ puts "signaled #{ $$ }"; signaled =3D true }
    begin
    pid =3D fork{ system command }
    Process.kill 'INT', pid
    Process.waitpid pid
    ensure
    trap('INT', int)
    end
    exit 15 if signaled
    end


    # use thread just so we can run *and* send ourselves a signal

    thread =3D Thread.new do
    system_critical "ruby -e' sleep 1 and puts :done' "
    end

    Process.kill 'INT', Process.pid

    thread.join

    cfp:~ > ruby a.rb
    signaled 25656
    signaled 25657
    done


    cfp:~ > ruby a.rb
    signaled 25660
    signaled 25661
    done




    this is because you have to no wait to install signal handlers in the =20=

    code the system call runs - it can do whatever it wants. what you can =20=

    do is cause a child process which *does* ignore INT to run that system =20=

    command. the reason you need the second child is that ruby's system =20
    call is going to do it's own signal management of INT.



    a @ http://codeforpeople.com/
    --
    we can deny everything, except that we have the possibility of being =20
    better. simply reflect on that.
    h.h. the 14th dalai lama
    ara.t.howard, Jun 2, 2008
    #7
  8. ara.t.howard wrote:
    > On Jun 2, 2008, at 11:36 AM, M�rio Lopes wrote:
    >
    >> What I'm precisely trying to do is trapping the SIGINT signal from
    >> getting to the child so it doesn't abort. I've been unable to do
    >> this. I
    >> can get it on the main process though but can't prevent it from being
    >> relayed to the child process.

    >
    > you need interject another process:
    >
    > #
    > # Start main process
    > # Start system/child process
    > # INT signals can't stop whatever the system call is doing
    > # End system/child process
    > # If INT signal detected, exit, otherwise, continue running
    > # End main process
    > #
    >
    > def system_critical command
    > signaled = false
    > int = trap('INT'){ puts "signaled #{ $$ }"; signaled = true }
    > begin
    > pid = fork{ system command }
    > Process.kill 'INT', pid
    > Process.waitpid pid
    > ensure
    > trap('INT', int)
    > end
    > exit 15 if signaled
    > end
    >
    >
    > # use thread just so we can run *and* send ourselves a signal
    >
    > thread = Thread.new do
    > system_critical "ruby -e' sleep 1 and puts :done' "
    > end
    >
    > Process.kill 'INT', Process.pid
    >
    > thread.join
    >
    > cfp:~ > ruby a.rb
    > signaled 25656
    > signaled 25657
    > done
    >
    >
    > cfp:~ > ruby a.rb
    > signaled 25660
    > signaled 25661
    > done


    This seems to work fine with sleep 10 — it waits until the end of the
    execution without actually passing the INT signal! But I'm unable to
    make it work with an external application like ffmpeg that traps the
    SIGINT itself. Any idea on how could I override this behavior without
    having to actually recompile ffmpeg and disable myself the handle?

    Thanks again for your help.

    Mário
    --
    Posted via http://www.ruby-forum.com/.
    Mário Lopes, Jun 2, 2008
    #8
  9. Mário Lopes

    ara.t.howard Guest

    On Jun 2, 2008, at 3:20 PM, M=E1rio Lopes wrote:

    > This seems to work fine with sleep 10 =97 it waits until the end of =

    the
    > execution without actually passing the INT signal! But I'm unable to
    > make it work with an external application like ffmpeg that traps the
    > SIGINT itself. Any idea on how could I override this behavior without
    > having to actually recompile ffmpeg and disable myself the handle?


    i'm unsure what you mean - you mean when running from the console? if =20=

    so try running ffmpeg with a pty.

    i have to ask though - *why* do this? what problem are you solving by =20=

    trapping INT and handling it differently?

    there are good reasons - but i'm wondering ;-)


    >
    >
    > Thanks again for your help.


    a @ http://codeforpeople.com/
    --
    we can deny everything, except that we have the possibility of being =20
    better. simply reflect on that.
    h.h. the 14th dalai lama
    ara.t.howard, Jun 3, 2008
    #9
  10. ara.t.howard wrote:
    > On Jun 2, 2008, at 3:20 PM, M�rio Lopes wrote:
    >
    >> This seems to work fine with sleep 10 � it waits until the end of the
    >> execution without actually passing the INT signal! But I'm unable to
    >> make it work with an external application like ffmpeg that traps the
    >> SIGINT itself. Any idea on how could I override this behavior without
    >> having to actually recompile ffmpeg and disable myself the handle?

    >
    > i'm unsure what you mean - you mean when running from the console? if
    > so try running ffmpeg with a pty.


    Yes, when being invoked like ruby script.rb

    > i have to ask though - *why* do this? what problem are you solving by
    > trapping INT and handling it differently?
    >
    > there are good reasons - but i'm wondering ;-)


    Well, ffmpeg should be running and not interrupted until it ends its
    job. The exit should be graceful and not end abruptly, otherwise the
    conversion will be interrupted.

    Which could be the best approach to this issue? Let ffmpeg run in the
    background and trap the INT signals in the foreground?

    Mário
    --
    Posted via http://www.ruby-forum.com/.
    Mário Lopes, Jun 3, 2008
    #10
  11. Mário Lopes

    ara.t.howard Guest

    On Jun 3, 2008, at 4:07 AM, M=E1rio Lopes wrote:

    > Which could be the best approach to this issue? Let ffmpeg run in the
    > background and trap the INT signals in the foreground?


    i believe this is the case - you cannot inject your own signal =20
    handlers into ffmpeg (easily) so this seems like the easiest solution.

    still i wonder - ffmpeg dies on INT for a reason - it's worth =20
    considering: if a user is running from the console then they expect to =20=

    be able to kill a process, if they are not then your problem =20
    vanishes. by trapping INT you will be forcing users to use something =20=

    stronger, like -9, which may not do things like clean up temp files, =20
    etc. food for thought.

    regards.

    a @ http://codeforpeople.com/
    --
    we can deny everything, except that we have the possibility of being =20
    better. simply reflect on that.
    h.h. the 14th dalai lama
    ara.t.howard, Jun 3, 2008
    #11
  12. ara.t.howard wrote:
    > On Jun 3, 2008, at 4:07 AM, M�rio Lopes wrote:
    >
    >> Which could be the best approach to this issue? Let ffmpeg run in the
    >> background and trap the INT signals in the foreground?

    >
    > i believe this is the case - you cannot inject your own signal
    > handlers into ffmpeg (easily) so this seems like the easiest solution.
    >
    > still i wonder - ffmpeg dies on INT for a reason - it's worth
    > considering: if a user is running from the console then they expect to
    > be able to kill a process, if they are not then your problem
    > vanishes. by trapping INT you will be forcing users to use something
    > stronger, like -9, which may not do things like clean up temp files,
    > etc. food for thought.


    Quite indeed.

    But it won't be run by a normal user. We just want to make sure that the
    process doesn't get killed and it gracefully quits and finishes whatever
    it has to do.

    I'm currently trying to use the Daemons gem to detach the process and
    run it in the background. It seems to be working fine lest having to
    hang on a while cycle and sleep until it ends. No chance of running it
    concurrently though.
    --
    Posted via http://www.ruby-forum.com/.
    Mário Lopes, Jun 3, 2008
    #12
  13. Mário Lopes

    ara.t.howard Guest

    On Jun 3, 2008, at 9:51 AM, M=E1rio Lopes wrote:

    > I'm currently trying to use the Daemons gem to detach the process and
    > run it in the background. It seems to be working fine lest having to
    > hang on a while cycle and sleep until it ends. No chance of running it
    > concurrently though.



    it's unclear to me why you need a daemon process? isn't just being in =20=

    the backgorund enough?


    cfp:~ > cat a.rb
    def background
    fork do
    begin
    yield
    ensure
    exit 0 unless $!
    end
    end
    end


    pid =3D background{ system "ruby -e' sleep 2 and puts 42 '" }

    trap('INT'){ STDERR.puts 'INT' }

    2.times{ Process.kill 'INT', Process.pid }

    Process.waitpid pid

    cfp:~ > ruby a.rb
    INT
    INT
    42


    ??


    a @ http://codeforpeople.com/
    --
    we can deny everything, except that we have the possibility of being =20
    better. simply reflect on that.
    h.h. the 14th dalai lama
    ara.t.howard, Jun 3, 2008
    #13
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. =?Utf-8?B?Unlhbg==?=

    traping curly apostrophes

    =?Utf-8?B?Unlhbg==?=, Jun 14, 2006, in forum: ASP .Net
    Replies:
    4
    Views:
    443
    Steven Cheng[MSFT]
    Jun 16, 2006
  2. Jeff Rodriguez
    Replies:
    23
    Views:
    1,112
    David Schwartz
    Dec 9, 2003
  3. sunil

    problem traping event in child control

    sunil, Apr 10, 2006, in forum: ASP .Net Web Controls
    Replies:
    1
    Views:
    124
    Phillip Williams
    Apr 10, 2006
  4. traping signals

    , Apr 26, 2005, in forum: Perl Misc
    Replies:
    4
    Views:
    89
    Charles DeRykus
    May 5, 2005
  5. none
    Replies:
    9
    Views:
    317
Loading...

Share This Page