Traping signals on child processes

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/.

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/

···

On Jun 2, 2008, at 8:59 AM, Mário 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. 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

--
we can deny everything, except that we have the possibility of being better. simply reflect on that.
h.h. the 14th dalai lama

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/\.

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

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

pipe = IO.pipe

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

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

trap 'INT', 'DEFAULT'

pipe.last.close
pipe = pipe.first
ready = 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, you need to catch it and do something sensible with it.

a @ http://codeforpeople.com/

···

On Jun 2, 2008, at 10:17 AM, Mário 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ário

--
we can deny everything, except that we have the possibility of being better. simply reflect on that.
h.h. the 14th dalai lama

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/\.

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

···

On Monday, June 02, 2008, at 01:40PM, "Mário Lopes" <mario.lopes@gmail.com> 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:

···

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.

#
# 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 is because you have to no wait to install signal handlers in the code the system call runs - it can do whatever it wants. what you can do is cause a child process which *does* ignore INT to run that system command. the reason you need the second child is that ruby's system 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 better. simply reflect on that.
h.h. the 14th dalai lama

ara.t.howard 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

···

On Jun 2, 2008, at 11:36 AM, M�rio Lopes wrote:

--
Posted via http://www.ruby-forum.com/\.

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.

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 :wink:

Thanks again for your help.

a @ http://codeforpeople.com/

···

On Jun 2, 2008, at 3:20 PM, Mário Lopes wrote:
--
we can deny everything, except that we have the possibility of being better. simply reflect on that.
h.h. the 14th dalai lama

ara.t.howard 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 :wink:

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

···

On Jun 2, 2008, at 3:20 PM, M�rio Lopes wrote:

--
Posted via http://www.ruby-forum.com/\.

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.

regards.

a @ http://codeforpeople.com/

···

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?

--
we can deny everything, except that we have the possibility of being better. simply reflect on that.
h.h. the 14th dalai lama

ara.t.howard 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.

···

On Jun 3, 2008, at 4:07 AM, M�rio Lopes wrote:

--
Posted via http://www.ruby-forum.com/\.

it's unclear to me why you need a daemon process? isn't just being in the backgorund enough?

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

pid = 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
42

??

a @ http://codeforpeople.com/

···

On Jun 3, 2008, at 9:51 AM, Mário 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.

--
we can deny everything, except that we have the possibility of being better. simply reflect on that.
h.h. the 14th dalai lama