Traping signals on child processes

M

Mário Lopes

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
 
A

ara.t.howard

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

Mário Lopes

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

ara.t.howard

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

Mário Lopes

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
 
G

Gary Wright

=20
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
 
A

ara.t.howard

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

Mário Lopes

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

ara.t.howard

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

Mário Lopes

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

ara.t.howard

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

Mário Lopes

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

ara.t.howard

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/
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,733
Messages
2,569,440
Members
44,831
Latest member
HealthSmartketoReviews

Latest Threads

Top