macOS `sigaction()` handler with `SA_SIGINFO` does not include `si_pid` - c

I'm trying to write a signal handler which needs to know the pid of the process that sends the signal. I'm having no luck with getting anything useful from the siginfo_t passed into my handler on macOS 10.14 with Xcode 10.
I've reduced my code to the below minimal sample to demonstrate the issue. In this sample I spawn a child process to send the signal I want to test which is defaulted to SIGTERM, but no other signal I've tried works any better.
Assuming you want to build and test this on a mac, you probably want to tell lldb to not stop when receiving a signal. You can use this lldb command: pro hand -p true -s false SIGTERM.
I'm also compiling with C++, but I believe I have excised all of that and the sample code should be pure C now.
Note that it doesn't matter if the signal originates from a child, terminal, or another process the result is always that si_pid is always 0 (along with everything other than the si_signo and si_addr). It doesnt matter how many times I send the signal, so it seems to not be simply a race condition.
How can I get the pid of the process sending the signal on macOS 10.14? I don't recall having this issue on 10.12 which is what I was using before.
This is just a sample to demostrate the problem, so please ignore anything that isn't actually causing a problem.
If the code seems like it should work as I expect, then I would be interested in seeing comments about systems that it works on too.
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
volatile sig_atomic_t histogram[3] = {0,0,0};
volatile sig_atomic_t signaled = 0;
const int testsig = SIGTERM;
void sigaction_handler(int sig, siginfo_t* info, void* context)
{
switch (info->si_pid) {
case 0:
case 1:
histogram[info->si_pid]++;
break;
default:
histogram[2]++;
break;
}
signaled = 1;
}
int main(int argc, const char * argv[]) {
pid_t mainpid = getpid();
pid_t pid = fork();
if (pid == 0) {
while (kill(mainpid, 0) == 0) {
sleep(1);
kill(mainpid, testsig);
}
_exit(0);
}
struct sigaction sigAction;
memset( &sigAction, 0, sizeof( sigAction ) );
sigAction.sa_sigaction = sigaction_handler;
sigemptyset (&sigAction.sa_mask);
sigAction.sa_flags = SA_SIGINFO;
sigaction(testsig, &sigAction, NULL);
while (1) {
if (signaled) {
printf("pid 0: %d, pid 1: %d, others: %d\n", histogram[0], histogram[1], histogram[2]);
signaled = 0;
}
sleep(1);
}
}

I'm currently using macOS Mojave 10.14.1.
How can I get the pid of the process sending the signal on macOS
10.14? I don't recall having this issue on 10.12 which is what I was using before.
The following code meets your wish simply. If you send SIGTERM, you can see pid of sender process.
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
static void hdl (int sig, siginfo_t *siginfo, void *context)
{
printf ("Sending PID: %ld, UID: %ld\n",
(long)siginfo->si_pid, (long)siginfo->si_uid);
}
int main (int argc, char *argv[])
{
struct sigaction act;
fprintf(stderr, "%i pp %i\n",getpid(), getppid());
memset (&act, '\0', sizeof(act));
/* Use the sa_sigaction field because the handles has two additional parameters */
act.sa_sigaction = &hdl;
/* The SA_SIGINFO flag tells sigaction() to use the sa_sigaction field, not sa_handler. */
act.sa_flags = SA_SIGINFO;
if (sigaction(SIGTERM, &act, NULL) < 0) {
perror ("sigaction");
return 1;
}
while (1)
sleep (10);
return 0;
}
For your code,
Rule of thumb: Don't forget to carry burial procedures out even though you are sure that child process ends prior parent process. By invoking wait(...) you tell the operating system that I'm done my things for my child so now you can clean allocated fields etc.
I'd prefer initialize signal utilities prior forking what if the parent process doesn't have a chance to register signal action? Moreover, I don't understand why you handle 0 and 1 cases in switch. Intrinsically the cases aren't hit, so always omitted.
In addition, you didn't use break in your if condition within main(). It doesn't go in if after a while yet the following circumstance which is not anticipated and desirable is that the program stays forever in while() loop. I'd prefer to put signaled into condition of while() loop.
At last but not least, due to sleep() call in child process until signaled is turned out 0, SIGTERM is caught several times successfully. When signaled is 0, the loop stops.
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include <memory.h>
#include <sys/wait.h>
volatile sig_atomic_t histogram[3] = {0,0,0};
volatile sig_atomic_t signaled = 0;
const int testsig = SIGTERM;
void sigaction_handler(int sig, siginfo_t* info, void* context)
{
switch (info->si_pid) {
case 0:
case 1:
histogram[info->si_pid]++;
break;
default:
fprintf(stderr, "sender pid -> %i\n", info->si_pid);
histogram[2]++;
break;
}
signaled = 1;
}
int main(int argc, const char * argv[]) {
struct sigaction sigAction;
memset( &sigAction, 0, sizeof( sigAction ) );
sigAction.sa_sigaction = sigaction_handler;
sigemptyset (&sigAction.sa_mask);
sigAction.sa_flags = SA_SIGINFO;
sigaction(testsig, &sigAction, NULL);
pid_t mainpid = getpid();
pid_t pid = fork();
if (pid == 0) {
fprintf(stderr, "my pid -> %i parent's pid-> %i\n", getpid(), getppid());
if (kill(mainpid, 0) == 0) { // signals are not queued not need loop
sleep(1);
kill(mainpid, testsig);
}
_exit(0);
} else {
wait(NULL); // play with this line to see what the difference is
while ( signaled ) {
printf("pid 0: %d, pid 1: %d, others: %d\n", histogram[0], histogram[1], histogram[2]);
signaled = 0;
sleep(1);
}
// wait(NULL); // play with this line to see what the difference is
}
}

It turns out that debugging via Xcode LLDB is the culprit. If I build and run the program normally it works fine. If I find out why I will update this answer.
I already have the "PASS" set for SIGTERM in lldb as noted in the question, so it seems like somehow there is a bug in the version of lldb shipped with Xcode 10.0 and it is "passing" the signal by creating a new struct and setting the signal number rather then the structure that would have normally been received. As I stated before this did used to work fine in whatever version of lldb shipped with macos 10.12
If somebody has a better explaination, please post an answer and I will accept and award bounty.

Related

Why the signal handler doesn't process the signal

I'm trying to make a program that simulates the command nohup. The program gets as a first parameter, the name of a command that is gonna be executed.
The program executed by my program must not be notified when the terminal is closed, it will have to ignore the SIGHUP.
If I test my program with with the following command:
./mynohup sleep 120 &
And then I try to send a SIGHUP from another terminal, sleep terminates when it should be immune to it.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include <signal.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include "utils.h"
#define NOHUP_OUT_FILE "nohup.out"
static void handle_signal(int signum)
{
if(signum == SIGHUP)
{
printf("This is ignored\n");
}
else
{
printf("Not ignored\n");
}
fflush(stdout);
}
/* configure handlers */
static void set_signals(void)
{
struct sigaction sa;
int rc;
/* TODO - ignore SIGHUP */
memset(&sa, 0, sizeof(struct sigaction));
sa.sa_handler = handle_signal;
rc = sigaction(SIGHUP, &sa, NULL);
DIE(rc == -1, "sigaction");
}
/* execute a new program */
static void exec_func(int argc, char **argv)
{
int rc;
int i;
char **exec_args;
int fd;
set_signals(); /* ignore SIGHUP */
if(isatty(STDOUT_FILENO))
{
fd = open(NOHUP_OUT_FILE, O_WRONLY | O_CREAT | O_TRUNC, 0644);
DIE(fd < 0, "open");
dup2(fd, STDOUT_FILENO);
close(fd);
}
/* exec a new process */
exec_args = malloc(argc * sizeof(*exec_args));
DIE(exec_args == NULL, "malloc");
for (i = 0; i < argc-1; i++)
exec_args[i] = argv[i+1];
exec_args[argc-1] = NULL;
execvp(exec_args[0], exec_args);
DIE(1, "execvp");
}
int main(int argc, char **argv)
{
if (argc <= 1) {
fprintf(stderr, "Usage: %s command_and_arguments\n", argv[0]);
exit(EXIT_FAILURE);
}
exec_func(argc, argv);
return 0;
}
I tried to skip creating a new process and the signal handler works great.
If the signal handler is in the following form the program works
static void set_signals(void)
{
struct sigaction sa;
int rc;
/* ignore SIGHUP */
memset(&sa, 0, sizeof(sa));
sa.sa_handler = SIG_IGN;
rc = sigaction(SIGHUP, &sa, NULL);
DIE(rc == -1, "sigaction");
}
I don't understand why when I create the first version of the signal handler the program doesn't works and with the second one it works.
Thanks in advance!
All exec functions reset the dispositions of caught signals to their default dispositions.
When you exec, your process image is destroyed and replaced by the process image of the new program. In it, the pointer to the handle_function you passed to sigaction no longer has meaning, or the old meaning at least. The only sensible thing the OS can do with handled signals upon execve is to reset them.
The meaning of SIG_IGN is universal and independent of the current program and that's why SIG_IGN can be, and is, inherited.
execvp() is a front end for the execve() syscall.
From its linux manpage:
All process attributes are preserved during an execve(), except the following:
* The dispositions of any signals that are being caught are reset to
the default (signal(7)).
So the signal handler you installed is reset.
CORRECTION: (see history of changes for original text)
The nohup(1) program just shifts the progran name (nohup) and the options to it, from the argc/argv parameters to main, redirects stdout/stderr to a file (nohup.out) in case one or both are directed to a tty device, and then just ignores SIGHUP and execvp(*argv, argv); for the original program to execute. It even does no fork(2) at all.
The source code of FreeBSD nohup is available here.

No trace of the child process

I forked a child and I am trying to synchronize them so they print
child 0
parent 0
child 1
parent 1
I have to use sigsuspend though, this is my code for the moment and all I get is parent suspend. There is no trace of the child.
int c=0, receivedP=0, receivedC=0;
sigset_t setParent, setChild;
void handler(int s){
if(s==SIGUSR1){
receivedC=1;
printf("parent --sig1--> child\n");
c++;
}
else{
receivedP=1;
printf("child --sig2--> parent\n");
}
}
void child(){
sigfillset(&setChild);
sigdelset(&setChild,SIGUSR1);
sigdelset(&setChild,SIGINT); //this makes me able to terminate the program at any time
while(1){
if(receivedC==0){
printf("child suspend\n");
sigsuspend(&setChild);
}
receivedC=0;
printf("child %d\n",c);
kill(getppid(),SIGUSR2);
}
}
void parent(pid_t pf){
sigfillset(&setParent);
sigdelset(&setParent,SIGUSR2);
sigdelset(&setParent,SIGINT); //this makes me able to terminate the program at any time
kill(pf,SIGUSR1);
while(1){
if(receivedP==0){
printf("parent suspend\n");
sigsuspend(&setParent);
}
receivedP=0;
printf("parent %d\n",c);
kill(pf,SIGUSR1);
}
}
int main(){
signal(SIGUSR1,handler);
signal(SIGUSR2,handler);
pid_t p;
p= fork();
if(!p)child();
else parent(p);
return 0;
}
Anybody knows what's causing this?
I think you are running foul of one of the classic problems with signals.
while(1){
if(receivedP==0){
printf("parent suspend\n");
sigsuspend(&setParent);
}
receivedP=0;
printf("parent %d\n",c);
kill(pf,SIGUSR1);
}
Imagine what happens if the signal from the child arrives in between the instructions for if(receivedP==0) and sigsuspend(&setParent). The handler will execute, and will set receivedP to one, but the main loop won't check it again; it will go into sigsuspend and never come out.
In order to use sigsuspend safely, you need to have the signals you care about be blocked at all times when the program is not calling sigsuspend. You do that with sigprocmask. It's also necessary to ensure that the signals are blocked during the execution of the handler, which requires you to use sigaction instead of signal (but you should do that anyway, as signal is severely underspecified and system-to-system variations will bite you in the ass).
Once you ensure that the signal can only be delivered during a sigsuspend, you no longer need the receivedP and receivedC variables; you know that the signal happened, or sigsuspend would not have returned. (This would not be true if your program was waiting for more than a single signal in each process, but at that point things get much more complicated; don't worry about it till it comes up.)
In fact, once you ensure that, you don't need to do anything in the signal handler. Your counter variable can be local to parent and child. It's always best to do as little in a signal handler as possible; the letter of the C standard allows you to do almost nothing without risking undefined behavior, and POSIX only opens it up a little bit more. (Exercise for you: change this program to use sigwaitinfo so that it doesn't need handler functions at all.)
This modification of your program works reliably for me. I also corrected a number of other style problems and minor errors: note the loops in parent and child doing things in different orders, the error checking in main, and that I am only blocking SIGUSR1 and SIGUSR2, because there are several other signals that should be allowed to terminate the process (SIGTERM, SIGHUP, SIGQUIT, SIGSEGV, …) and you don't want to have to maintain a list. It is sufficient to block the signals that the program has installed handlers for.
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
static void handler(int unused)
{
}
static void child(sigset_t *ss)
{
unsigned int c = 0;
pid_t parent_pid = getppid();
sigdelset(ss, SIGUSR1);
for (;;) {
sigsuspend(ss);
printf("child %u\n", c++);
kill(parent_pid, SIGUSR2);
}
}
static void parent(sigset_t *ss, pid_t child_pid)
{
unsigned int c = 0;
sigdelset(ss, SIGUSR2);
for (;;) {
printf("parent %u\n", c++);
kill(child_pid, SIGUSR1);
sigsuspend(ss);
}
}
int main(void)
{
// Ensure line-buffered stdout.
if (setvbuf(stdout, 0, _IOLBF, 0)) {
perror("setvbuf");
return 1;
}
// This signal mask is in effect at all times _except_ when sleeping
// in sigsuspend(). Note that _only_ the signals used for IPC are
// blocked. After forking, each process will modify it appropriately
// for its own use of sigsuspend(); this does not affect the kernel-side
// copy made by sigprocmask().
sigset_t ss;
sigemptyset(&ss);
sigaddset(&ss, SIGUSR1);
sigaddset(&ss, SIGUSR2);
if (sigprocmask(SIG_BLOCK, &ss, 0)) {
perror("sigprocmask");
return 1;
}
// Always use sigaction(), not signal(); signal() is underspecified.
// The mask here is the signal mask to use _while the handler is
// executing_; it should also block both IPC signals.
struct sigaction sa;
sa.sa_handler = handler;
sa.sa_mask = ss;
sa.sa_flags = SA_RESTART;
if (sigaction(SIGUSR1, &sa, 0) || sigaction(SIGUSR2, &sa, 0)) {
perror("sigaction");
return 1;
}
pid_t child_pid = fork();
if (child_pid < 0) {
perror("fork");
return 1;
}
if (child_pid == 0)
child(&ss);
else
parent(&ss, child_pid);
// we never get here but the compiler might not know that
return 0;
}
I recommend you read the GNU C Library Manual's section on signal handling all the way through; it contains several other bits of helpful advice on using signals safely.

sigaction's signal handler not called in child process

I've a program, which installs a signal handler for SIGSEGV. In signal handler ( I try to catch crash ) I restart my application.
But when my application is resurrected it doesn't handle SIGSEGV anymore.
Here's an example:
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
const char * app = 0;
void sig_handler(int signo)
{
puts("sig_handler");
const pid_t p = fork();
if (p == 0)
{
printf("Running app %s\n", app);
execl(app, 0);
}
exit(1);
}
int main(int argc, char** argv)
{
app = argv[0];
struct sigaction act;
sigemptyset(&act.sa_mask);
act.sa_handler = sig_handler;
act.sa_flags = 0;
const int status = sigaction(SIGSEGV, &act, 0) == 0;
printf("signaction = %d\n", status);
sleep(5);
int* a = 0;
int b = *a;
return 0;
}
what I get in output is:
./signals
signaction = 1
sig_handler
Running app ./signals
signaction = 1
So I can see sighandler was set in right way, but resurrected app simply crashed silently.
What am I missing?
What you're missing is that, by default, when you handle a signal, any additional delivery of that signal is blocked until the handling function returns. Since you never return from your signal handler (you call execl() instead) then your second SIGSEGV isn't being delivered. It's waiting until your signal handler function returns, which it never will.
To get the results you seem to want, you have to change this default behavior. The easiest way to do that is to set the appropriate flag when you register the signal handler:
act.sa_flags = SA_NODEFER;
and you'll get the recursive behavior you seem to be looking for. Your other option is to unblock it with sigprocmask() before your execl() call.
Couple of other ancillary points:
puts(), printf(), execl() and exit() are not async-safe, and shouldn't be called from a signal handler. execle() and _exit() would be OK.
You're not calling execl() properly. The first argument should be the application name, so execl(app, app, (char *)0); would be correct. The cast to char *, which you omit, is required.

Correct way for waiting for all children in real application

I'm writing a multi-threading application(school project, modified river crossing problem).
I'm using POSIX semaphores, shared memory and fork function. Main process creates 2 processes. Each of them then creates N processes. Each process represent a single person. I'd like to know what is the professional technique/the best approach for parent to wait for all of his children till they finish and then obtain their exit code. I don't/can't use constructions like:
while (wait(NULL) > 0)
;
// parent code
Neither constructions using shared variable like
while (1)
if (num_of_processes == num_of_finished_processes)
break;
// parent_code
NOTE: num_of_processes is argument passed to program
Can I somehow use semaphore to tell parent: "now wake up and execute your code".
In one sentence. I don't want to use active/cyclic waiting.
Thank you for any suggestion. I'm just beginner in this field.
The definition of "professional" can be elastic.
There are basically two reasons to reap your children: avoid zombies taking up room in the process table and to interrogate the children return codes and (presumably) base some action on them. Both of these may point to, all things being equal, reaping them as quickly as possible.
So your options are:
have the parent block waiting for them with wait or waitpid.
periodically poll for them with waitpid and the WNOHANG option
have the SIGCHLD signal delivered and handle them as they arise
have the SIGCHLD signal delivered and use signalfd (on linux) but this still requires polling or the use of select/poll/epoll
Since you have rejected 1 & 2 (and implicitly 4) that leaves dealing with the signal and using a handler. For mental health reasons, if not necessarily professionalism, most people avoid signals as much as they can and don't go looking for ways to deal with them if they don't have to do so because:
signal handlers come with their own constraints, primarily that you want to be in and out of them as quickly as possible and there are limited number async-safe functions you should employ within them. So that usually means recording whatever you can inside the handler and dealing with the information when you can in the main program or a thread dedicated to it.
Since you have specifically invited a signal (SIGCHLD) into your life by choosing to handle it, you have bear the consequences of you system calls being interrupted. Since you are using POSIX semaphores then sem_wait would be of particular concern. You can get around most of this by just turning on the SA_RESTART flag via your sigaction call when establishing your handler but even with the flag there are a number of calls which are not automatically restarted.
Multi-threading and signals come with their own set of headaches.
Below is a crude but illustrative example of some of the above issues:
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
typedef struct childInfo
{
pid_t childPid;
int childstatus;
} childInfo;
static volatile sig_atomic_t numberOfChildren = 0;
static volatile sig_atomic_t childrenReaped = 0;
childInfo *childrenTable;
void saveStatus(pid_t pid, int status)
{
for (int i = 0; i < numberOfChildren; ++i)
{
if (pid == childrenTable[i].childPid)
childrenTable[i].childstatus = status;
}
}
void printChildrenStatus()
{
for (int i = 0; i < numberOfChildren; ++i)
{
if (childrenTable[i].childPid != 0)
{
if (WIFEXITED(childrenTable[i].childstatus))
printf("PID %d exited normally. "
"Exit status: %d\n",
childrenTable[i].childPid, WEXITSTATUS(childrenTable[i].childstatus));
else
if (WIFSTOPPED(childrenTable[i].childstatus))
printf("PID %d was stopped by %d\n",
childrenTable[i].childPid,
WSTOPSIG(childrenTable[i].childstatus));
else
if (WIFSIGNALED(childrenTable[i].childstatus))
printf("PID %d exited due to signal %d\n.",
childrenTable[i].childPid,
WTERMSIG(childrenTable[i].childstatus));
childrenTable[i].childPid = 0;
childrenReaped++;
}
}
}
void childHandler(int signum)
{
int childstatus;
pid_t childpid;
while ((childpid = waitpid( -1, &childstatus, WNOHANG)) > 0)
saveStatus(childpid, childstatus);
}
int main(int argc, char *argv[])
{
if (argc > 1)
numberOfChildren = atoi(argv[1]);
else
{
printf("must enter num of children to create...");
exit(1);
}
childrenTable = calloc(numberOfChildren, sizeof(childInfo));
struct sigaction sa;
sa.sa_handler = childHandler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART; // Restart functions, particularly your parent's
// sem_wait if interrupted by the handler
if (sigaction(SIGINT, &sa, NULL) == -1)
{
perror("sigaction");
exit(1);
}
for (int i = 0; i < numberOfChildren; ++i)
{
pid_t pid = fork();
if (pid)
childrenTable[i].childPid = pid;
else
{
sleep(i);
exit(0);
}
}
while(numberOfChildren - childrenReaped)
{
pause();
printChildrenStatus();
}
return(0);
}
while ( wait(NULL) > 0 )
is an infine loop, because the return value of wait(2) is the pid of the terminated - child process, which is != 0, and won't change.
If you want the parent - process to wait for N processes to exit you can simply use a loop like this:
for (i = 0; i < N; ++i)
wait(NULL);

Sending and handling a signal on a cloned thread

UPDATE: This appears to be a timing issue. Adding a call to sleep before the call to kill makes everything work as expected.
I have been playing with clone(2) and trying to get a handle on how it works. I am currently having trouble sending signals to a cloned process. I have the following code:
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <sched.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <pthread.h>
volatile int keep_going = 1;
typedef void (*sighandler_t)(int);
void handler(int sig) {
printf("Signal Received\n");
keep_going = 0;
}
int thread_main(void* arg) {
struct sigaction usr_action;
sigset_t block_mask;
sigfillset(&block_mask);
usr_action.sa_handler = &handler;
usr_action.sa_mask = block_mask;
usr_action.sa_flags = 0;
sigaction(SIGUSR1, &usr_action, NULL);
printf("Hello from cloned thread\n");
while(keep_going);
}
int main(int argc, char **argv) {
void* stack = malloc(4096);
int flags = SIGCHLD;
int child_tid = clone(&thread_main, stack + 4096, flags, NULL);
if (child_tid < 0) {
perror("clone");
exit(EXIT_FAILURE);
}
printf("My pid: %d, child_tid: %d\n", (int) getpid(), (int) child_tid);
int kill_ret = kill(child_tid, SIGUSR1);
if (kill_ret < 0) {
perror("kill");
exit(EXIT_FAILURE);
}
int status = 0;
pid_t returned_pid = waitpid(child_tid, &status, 0);
if (returned_pid < 0) {
perror("waitpid");
exit(EXIT_FAILURE);
}
if (WIFEXITED(status)) {
printf("exited, status=%d\n", WEXITSTATUS(status));
} else if (WIFSIGNALED(status)) {
printf("killed by signal %d\n", WTERMSIG(status));
} else if (WIFSTOPPED(status)) {
printf("stopped by signal %d\n", WSTOPSIG(status));
} else if (WIFCONTINUED(status)) {
printf("continued\n");
}
exit(EXIT_SUCCESS);
}
Which yields the following output:
My pid: 14101, child_tid: 14102
killed by signal 10
The child was obviously killed as a result of the signal, why did the signal handler not get called?
To avoid the race condition, catch the signal on the parent, before the clone() call. The child inherits a copy of the parent's signal handlers. You can reset it later on the parent to SIG_DFL if you want. (Also, getpid() is async-signal-safe, if you want to emulate SIG_DFL behaviour on the parent).
The child is not receiving the signal because before the child has reached to the call to sigaction the parent is sending the signal and thats why it is getting killed. You should avoid setting the signal handler this way. Still if you want to do this way only then make sure is parent is waiting until the child sets up the signal handler. With this scenario you should see the expected result.
First what is strange is you didn't get this message :
"Hello from cloned thread\n"
therefore your child tread gets terminated before it manages to setup the signal handler.
EDIT:
I just saw your comment about sleep. Try to add another variable, which is set when the sigaction gets executed. The main thread should be blocked until this variable is not set.

Resources