Killing a Haskell binary - unix

If I press Ctrl+C, this throws an exception (always in thread 0?). You can catch this if you want - or, more likely, run some cleanup and then rethrow it. But the usual result is to bring the program to a halt, one way or another.
Now suppose I use the Unix kill command. As I understand it, kill basically sends a (configurable) Unix signal to the specified process.
How does the Haskell RTS respond to this? Is it documented somewhere? I would imagine that sending SIGTERM would have the same effect as pressing Ctrl+C, but I don't know that for a fact...
(And, of course, you can use kill to send signals that have nothing to do with killing at all. Again, I would imagine that the RTS would ignore, say, SIGHUP or SIGPWR, but I don't know for sure.)

Googling "haskell catch sigterm" led me to System.Posix.Signals of the unix package, which has a rather nice looking system for catching and handling these signals. Just scroll down to the "Handling Signals" section.
EDIT: A trivial example:
import System.Posix.Signals
import Control.Concurrent (threadDelay)
import Control.Concurrent.MVar
termHandler :: MVar () -> Handler
termHandler v = CatchOnce $ do
putStrLn "Caught SIGTERM"
putMVar v ()
loop :: MVar () -> IO ()
loop v = do
putStrLn "Still running"
threadDelay 1000000
val <- tryTakeMVar v
case val of
Just _ -> putStrLn "Quitting" >> return ()
Nothing -> loop v
main = do
v <- newEmptyMVar
installHandler sigTERM (termHandler v) Nothing
loop v
Notice that I had to use an MVar to inform loop that it was time to quit. I tried using exitSuccess from System.Exit, but since termHandler executes in a thread that isn't the main one, it can't cause the program to exit. There might be an easier way to do it, but I've never used this module before so I don't know of one. I tested this on Ubuntu 12.10.

Searching for "signal" in the ghc source code on github revealed the installDefaultSignals function:
void
initDefaultHandlers(void)
{
struct sigaction action,oact;
// install the SIGINT handler
action.sa_handler = shutdown_handler;
sigemptyset(&action.sa_mask);
action.sa_flags = 0;
if (sigaction(SIGINT, &action, &oact) != 0) {
sysErrorBelch("warning: failed to install SIGINT handler");
}
#if defined(HAVE_SIGINTERRUPT)
siginterrupt(SIGINT, 1); // isn't this the default? --SDM
#endif
// install the SIGFPE handler
// In addition to handling SIGINT, also handle SIGFPE by ignoring it.
// Apparently IEEE requires floating-point exceptions to be ignored by
// default, but alpha-dec-osf3 doesn't seem to do so.
// Commented out by SDM 2/7/2002: this causes an infinite loop on
// some architectures when an integer division by zero occurs: we
// don't recover from the floating point exception, and the
// program just generates another one immediately.
#if 0
action.sa_handler = SIG_IGN;
sigemptyset(&action.sa_mask);
action.sa_flags = 0;
if (sigaction(SIGFPE, &action, &oact) != 0) {
sysErrorBelch("warning: failed to install SIGFPE handler");
}
#endif
#ifdef alpha_HOST_ARCH
ieee_set_fp_control(0);
#endif
// ignore SIGPIPE; see #1619
// actually, we use an empty signal handler rather than SIG_IGN,
// so that SIGPIPE gets reset to its default behaviour on exec.
action.sa_handler = empty_handler;
sigemptyset(&action.sa_mask);
action.sa_flags = 0;
if (sigaction(SIGPIPE, &action, &oact) != 0) {
sysErrorBelch("warning: failed to install SIGPIPE handler");
}
set_sigtstp_action(rtsTrue);
}
From that, you can see that GHC installs at least SIGINT and SIGPIPE handlers. I don't know if there are any other signal handlers hidden in the source code.

Related

How to capture linux signals in julia

I'm looking for a way to capture SIGINT in a julia script on a linux host, but I'm not able to understand how to manage signals.
In the REPL:
julia > try
sleep(1000)
catch e
#info "interrupt captured!"
end
Ctrl-C
[ Info: interrupt captured!
Instead, executing demo.jl:
try
sleep(1000)
catch e
#info "interrupt captured!"
end
gives:
terminal> julia demo.jl
Ctrl-C
signal (2): Interrupt
in expression starting at /tmp/demo.jl:3
epoll_pwait at /lib/x86_64-linux-gnu/libc.so.6 (unknown line)
uv__io_poll at /workspace/srcdir/libuv/src/unix/linux-core.c:270
uv_run at /workspace/srcdir/libuv/src/unix/core.c:359
jl_task_get_next at /buildworker/worker/package_linux64/build/src/partr.c:473
poptask at ./task.jl:704
wait at ./task.jl:712 [inlined]
...
How to manage the interrupt in this case?
As described here InterruptException is not thrown by Ctrl-C when you exec julia demo.jl. Use instead:
Base.exit_on_sigint(false)
try
while true
sleep(1)
#info "."
end
catch e
#info "interrupt captured!"
end
And I think sleep(1000) isn't helpful in this context.
I would not use exceptions if you want to just catch the SIGINT signal, i would set an handler. You should take a look at the atexit(f) function.
atexit() do
//Handle your exception here if necessary
end
EDIT: Try this way if the first doesn't works:
atexit(exitFunc()
//Code to be executed when the signal is received
end)

System signal interception in Julia

In a Julia program which run under Linux, I need to launch a dedicated action when a console window is resized. So how in Julia, can I intercept the system signal SIGWINCH (window resizing) and attach to it a function which performs the required action ?
In Ada it is rather straightforward to declare it :
protected Signalhandler is
procedure Handlewindowresizing;
pragma Attach_Handler (Handlewindowresizing, SIGWINCH);
end Signalhandler;
TENTATIVE SOLUTION BASED ON IDEA OF SCHEMER : I try to use a C Library which conducts the SIGWINCH interruption monitoring.
myLibrary.h
void Winresize (void Sig_Handler());
myLibrary.c
#include "myLibrary.h"
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
void Winresize(void sig_handler (void)) {
signal(SIGWINCH, sig_handler);
}
Compilation & Library preparation
gcc -c -Wall -fPIC myLibrary.c
gcc -shared -fPIC -o myLibrary.so myLibrary.o
Program in Julia which uses the C-Library :
function getc1()
ret = ccall(:jl_tty_set_mode, Int32, (Ptr{Cvoid},Int32), stdin.handle, true)
ret == 0 || error("unable to switch to raw mode")
c = read(stdin, UInt8)
ccall(:jl_tty_set_mode, Int32, (Ptr{Cvoid},Int32), stdin.handle, false)
c
end
function traitement() println(displaysize(stdout)); end
Mon_traitement_c = #cfunction(traitement, Cvoid, ())
ccall((:Winresize, "/home/Emile/programmation/Julia/myLibrary.so"), Cvoid, (Ptr{Cvoid},), Mon_traitement_c)
while true
println(getc1())
end
The Julia program run properly but when the terminal window is resized a Segmentation fault (core dumped) is issued and program is said exited with code: 139.
So the question is where does this segmentation fault come from ? From the compilation model ? Julia has not the right to control code execution in the memory part where C manages the signal monitoring ?
Removing println operation in Sig_handler suppress the segmentation fault :
curr_size = displaysize(stdout)
new_size = curr_size
function traitement() global new_size ; new_size = displaysize(stdout); return end
Mon_traitement_c = #cfunction(traitement, Cvoid, ())
ccall((:Winresize, "/home/Emile/programmation/Julia/myLibrary.so"), Cvoid, (Ptr{Cvoid},), Mon_traitement_c)
while true
global curr_size, new_size
if new_size != curr_size
curr_size = new_size
println(curr_size)
end
sleep(0.1)
end
Since no one has answered this question so far, one possible workaround could be asynchronously monitoring the size of the terminal in some time intervals.
function monitor_term(func)
#async begin
curr_size = displaysize(stdout)
while (true)
sleep(0.1)
new_size = displaysize(stdout)
if new_size != curr_size
curr_size = new_size
func()
end
end
end
end
And now sample usage:
julia> monitor_term(() -> print("BOO!"))
Task (runnable) #0x0000000013071710
As long as the terminal is alive, any change to its size will print BOO!.
Yes, it is indeed a fallback solution which is hardly what one expects from a new language full of promises ... but for lack of thrushes we can actually eat blackbirds (smile).
But if Julia hasn't planned to be able to take into account the system signals of the Unix/Linux world, it might be possible to do it using a C library like the one that signal.h accesses.
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
void sig_handler(int signum)
{
printf("Received signal %d\n", signum);
}
int main()
{
signal(SIGINT, sig_handler);
sleep(10); // This is your chance to press CTRL-C
return 0;
}
We would have to define a julia function doing what is expected when the system signal is received. Make it usable in C as Sig_handler and call from julia the C statement signal(SIGWINCH, Sig_handler);
I am not enough familiar with julia to write the exact code. But this is the idea...

Setting the TCP keepalive interval on the Hiredis async context

I'm writing a wrapper around hiredis to enable publish / subscribe functionality with reconnects should a redis node go down.
I'm using the asynchronous redis API.
So I have a test harness that sets up a publisher and subscriber. The harness then shuts down the slave VM from which the subscriber is reading.
However, the disconnect callback isn't called until much later (when I'm destructing the Subscription object that contains the corresponding redisAsyncContext.
I thought that the solution to this might be using tcp keepalive.
So I found that there's a suitable redis function in net.h:
int redisKeepAlive (redisContext* c, int interval);
However, the following appears to show that the redisKeepAlive function has been omitted from the library on purpose:
$ nm libhiredis.a --demangle | grep redisKeepAlive
0000000000000030 T redisKeepAlive
U redisKeepAlive
$ nm libhiredis.a -u --demangle | grep redisKeepAlive
U redisKeepAlive
Certainly when I try to use the call, the linker complains:
Subscription.cpp:167: undefined reference to `redisKeepAlive(redisContext*, int)'
collect2: error: ld returned 1 exit status
Am I out of luck - is there a way to set the TCP keepalive interval on the Hiredis async context?
Update
I've found this:
int redisEnableKeepAlive(redisContext *c);
But setting this on the asyncContext->c and adjusting REDIS_KEEPALIVE_INTERVAL seems to have no effect.
I found that the implementation of redisKeepAlive contains code that shows how to get direct access to the underlying socket descriptor:
int redisKeepAlive(redisContext *c, int interval) {
int val = 1;
int fd = c->fd;
if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)) == -1){
__redisSetError(c,REDIS_ERR_OTHER,strerror(errno));
return REDIS_ERR;
}
Maybe this'll help someone..

Send SIGINT to a process by sending ctrl-c to stdin

I'm looking for a way to mimick a terminal for some automated testing: i.e. start a process and then interact with it via sending data to stdin and reading from stdout. E.g. sending some lines of input to stdin including ctrl-c and ctrl-\ which should result in sending signals to the process.
Using std::process::Commannd I'm able to send input to e.g. cat and I'm also seeing its output on stdout, but sending ctrl-c (as I understand that is 3) does not cause SIGINT sent to the shell. E.g. this program should terminate:
use std::process::{Command, Stdio};
use std::io::Write;
fn main() {
let mut child = Command::new("sh")
.arg("-c").arg("-i").arg("cat")
.stdin(Stdio::piped())
.spawn().unwrap();
let mut stdin = child.stdin.take().unwrap();
stdin.write(&[3]).expect("cannot send ctrl-c");
child.wait();
}
I suspect the issue is that sending ctrl-c needs the some tty and via sh -i it's only in "interactive mode".
Do I need to go full fledged and use e.g. termion or ncurses?
Update: I confused shell and terminal in the original question. I cleared this up now. Also I mentioned ssh which should have been sh.
The simplest way is to directly send the SIGINT signal to the child process. This can be done easily using nix's signal::kill function:
// add `nix = "0.15.0"` to your Cargo.toml
use std::process::{Command, Stdio};
use std::io::Write;
fn main() {
// spawn child process
let mut child = Command::new("cat")
.stdin(Stdio::piped())
.spawn().unwrap();
// send "echo\n" to child's stdin
let mut stdin = child.stdin.take().unwrap();
writeln!(stdin, "echo");
// sleep a bit so that child can process the input
std::thread::sleep(std::time::Duration::from_millis(500));
// send SIGINT to the child
nix::sys::signal::kill(
nix::unistd::Pid::from_raw(child.id() as i32),
nix::sys::signal::Signal::SIGINT
).expect("cannot send ctrl-c");
// wait for child to terminate
child.wait().unwrap();
}
You should be able to send all kinds of signals using this method. For more advanced "interactivity" (e.g. child programs like vi that query terminal size) you'd need to create a pseudoterminal like #hansaplast did in his solution.
After a lot of research I figured out it's not too much work to do the pty fork myself. There's pty-rs, but it has bugs and seems unmaintained.
The following code needs pty module of nix which is not yet on crates.io, so Cargo.toml needs this for now:
[dependencies]
nix = {git = "https://github.com/nix-rust/nix.git"}
The following code runs cat in a tty and then writes/reads from it and sends Ctrl-C (3):
extern crate nix;
use std::path::Path;
use nix::pty::{posix_openpt, grantpt, unlockpt, ptsname};
use nix::fcntl::{O_RDWR, open};
use nix::sys::stat;
use nix::unistd::{fork, ForkResult, setsid, dup2};
use nix::libc::{STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO};
use std::os::unix::io::{AsRawFd, FromRawFd};
use std::io::prelude::*;
use std::io::{BufReader, LineWriter};
fn run() -> std::io::Result<()> {
// Open a new PTY master
let master_fd = posix_openpt(O_RDWR)?;
// Allow a slave to be generated for it
grantpt(&master_fd)?;
unlockpt(&master_fd)?;
// Get the name of the slave
let slave_name = ptsname(&master_fd)?;
match fork() {
Ok(ForkResult::Child) => {
setsid()?; // create new session with child as session leader
let slave_fd = open(Path::new(&slave_name), O_RDWR, stat::Mode::empty())?;
// assign stdin, stdout, stderr to the tty, just like a terminal does
dup2(slave_fd, STDIN_FILENO)?;
dup2(slave_fd, STDOUT_FILENO)?;
dup2(slave_fd, STDERR_FILENO)?;
std::process::Command::new("cat").status()?;
}
Ok(ForkResult::Parent { child: _ }) => {
let f = unsafe { std::fs::File::from_raw_fd(master_fd.as_raw_fd()) };
let mut reader = BufReader::new(&f);
let mut writer = LineWriter::new(&f);
writer.write_all(b"hello world\n")?;
let mut s = String::new();
reader.read_line(&mut s)?; // what we just wrote in
reader.read_line(&mut s)?; // what cat wrote out
writer.write(&[3])?; // send ^C
writer.flush()?;
let mut buf = [0; 2]; // needs bytewise read as ^C has no newline
reader.read(&mut buf)?;
s += &String::from_utf8_lossy(&buf).to_string();
println!("{}", s);
println!("cat exit code: {:?}", wait::wait()?); // make sure cat really exited
}
Err(_) => println!("error"),
}
Ok(())
}
fn main() {
run().expect("could not execute command");
}
Output:
hello world
hello world
^C
cat exit code: Signaled(2906, SIGINT, false)
Try adding -t option TWICE to force pseudo-tty allocation. I.e.
klar (16:14) ~>echo foo | ssh user#host.ssh.com tty
not a tty
klar (16:14) ~>echo foo | ssh -t -t user#host.ssh.com tty
/dev/pts/0
When you have a pseudo-tty, I think it should convert that to SIGINT as you wanted to do.
In your simple example, you could also just close stdin after the write, in which case the server should exit. For this particular case it would be more elegant and probably more reliable.
Solution without using a crate
Now that you are spawning a command in Rust, you might as well spawn another to send SIGINT to it. That command is kill.
So, you can do this:
use std::process::{Command, Stdio};
use std::io::{Result, Write};
fn main() -> Result<()> {
let mut child = Command::new("sh")
.arg("-c").arg("-i").arg("cat")
.stdin(Stdio::piped())
.spawn()?;
let mut stdin = child.stdin.take().unwrap();
let mut kill = Command::new("kill")
.arg(child.id().to_string())
.spawn()?;
kill.wait()
}

Signal being forwarded to children for the symfony process component

I'm trying to write a small script that will manage a series of background processes using the symfony component Process (http://symfony.com/doc/current/components/process.html).
For this to work correctly i would like to handle signals sent to the main process, mainly SIGINT (ctrl + c).
When the main process gets this signal, it should stop starting new processes, wait for all current processes to exit and then exit itself.
I successfully catch the signal in the main process but the problem is that the child-processes gets the signal too and exits immediately.
Is there any way of changing this behavior or interrupting this signal?
This is my example script to demonstrate the behavior.
#!/usr/bin/env php
<?php
require_once __DIR__ . "/vendor/autoload.php";
use Symfony\Component\Process\Process;
$process = new Process("sleep 10");
$process->start();
$exitHandler = function ($signo) use ($process) {
print "Got signal {$signo}\n";
while ($process->isRunning()) {
usleep(10000);
}
exit;
};
pcntl_signal(SIGINT, $exitHandler);
while (true) {
pcntl_signal_dispatch();
sleep(1);
}
Running this script, and sending the signal (pressing ctrl + c) will immediately stop the parent and child processes).
If i replace the while-loop with the isRunning call and the sleep with a call to the wait-method on the process i get an RuntimeException saying: The process has been signaled with signal "2".
If i take a more manual approach and execute the child process with phps build in exec, i get the behavior i want.
#!/usr/bin/env php
<?php
require_once __DIR__ . "/vendor/autoload.php";
exec(sprintf("%s > %s 2>&1 & echo $! >> %s", "sleep 10", "/dev/null", "/tmp/testscript.pid"));
$exitHandler = function ($signo) {
print "Got signal {$signo}\n";
$pid = file_get_contents("/tmp/testscript.pid");
while (isRunning($pid)) {
usleep(10000);
}
exit;
};
pcntl_signal(SIGINT, $exitHandler);
while (true) {
pcntl_signal_dispatch();
sleep(1);
}
function isRunning($pid){
try{
$result = shell_exec(sprintf("ps %d", $pid));
if( count(preg_split("/\n/", $result)) > 2){
return true;
}
}catch(Exception $e){}
return false;
}
In this case, when i send the signal, the main process waits for it's child to finish before exiting.
Is there any way to get the behavior in the symfony process component?
It's not the behavior of Symfony's Process, but behavior of ctrl+c in UNIX terminal. When you press ctrl+c in terminal signal is sent to process group (parent and all child processes).
Manual approach works because sleep isn't child process. When you want to use Symfony's component you can change child's process group with posix_setpgid:
$otherGroup = posix_getpgid(posix_getppid());
posix_setpgid($process->getPid(), $otherGroup);
Then signal won't be sent to $process. That's the only working solution I found when I recently tackled with similar problem.
Research
Sending signals to process group
Child process is created in Symfony example. You can check it in terminal.
# find pid of your script
ps -aux | grep "myscript.php"
# show process tree
pstree -a pid
# you will see that sleep is child process
php myscript.php
└─sh -c sleep 20
└─sleep 20
Signal sent to process group is nicely visible when you print information about process in $exitHandler:
$exitHandler = function ($signo) use ($process) {
print "Got signal {$signo}\n";
while ($process->isRunning()) {
usleep(10000);
}
$isSignaled = $process->hasBeenSignaled() ? 'YES' : 'NO';
echo "Signaled? {$isSignaled}\n";
echo "Exit code: {$process->getExitCode()}\n\n";
exit;
};
When you press ctrl+c or kill process group:
# kill process group (like in ctrl+c)
kill -SIGINT -pid
# $exitHandler's output
Got signal 2
Signaled? YES
Exit code: 130
When signal is send only to parent process then you'll get expected behavior:
# kill only main process
kill -SIGINT pid
# $exitHandler's output
Got signal 2
Signaled? NO
Exit code: 0
Now the solution is obvious. Don't create child process or change processs group, so signal is sent only to parent process.
Disadvantages of changing process group
Be aware of consequences when real child process isn't used. $process won't be terminated when parent process is killed with SIGKILL. If $process is long-running script then you could have multiple running instances after restarting parent process. It's good idea to check running processes before starting $process.

Resources