I'm writing a program that scans the process memory and creates a memchunk structures to represent the accessibility of a block of memory. A side effect of this process is to learn how to handle signals, as there should be many segfaults over the course of this scan. So, I'm trying to cause many segfaults to learn about signals while also learning a bit about the virtual memory. This is compiled as a 32bit program on a linux platform.
I make use of setjmp and longjmp in order to re-enter my program.
My problem is that my segfault handler is only called once, and then the default segfault handler -- the core dumping one -- is called on the next segfault.
Here is my handler:
static void hdl (int sig, siginfo_t *siginfo, void *unused){
/* handler is called properly and can read curr_access */
printf("-------------------\n");
printf("handling segfault\n");
printf("curr_access = %d\n", curr_access);
printf("-----------------\n");
switch(curr_access){
case -1:
longjmp(buf, 1);
break;
case 0:
longjmp(buf, 2);
break;
default:
printf("error in hdl\n");
}
}
Here is where I register it
void set_hndlr(){
printf("------------------\n");
printf("setting handler\n");
printf("-----------------\n");
/* setting up signal handler */
struct sigaction sa;
sa.sa_flags = SA_SIGINFO;
sigemptyset(&sa.sa_mask);
sa.sa_sigaction = hdl;
if (sigaction(SIGSEGV, &sa, NULL) == -1)
printf("error setting segfault hanld");
}
here is where I "test" a memory location:
switch(jmp_code){
/* we haven't gone through testing yet */
case 0:
test_boolean = 1;
break;
/* we've gone through testing and we segfaulted at read */
case 1:
test_boolean = 0;
curr_access = -1;
break;
/* we've gone through testing and we segfaulted at write */
case 2:
test_boolean = 0;
curr_access = 0;
break;
/* if we reach here then there is an error in program logic */
default:
printf("error in programming logic regaurding jmp\n");
exit(-2);
}
if(1 == test_boolean){
/* not accessible */
curr_access = -1;
printf("testing read\n");
/*test read */
curr_cont = *curr_addr;
curr_access = 0;
printf("readable\n");
/*test write */
*curr_addr = curr_cont;
curr_access = 1;
printf("read/write\n");
}
Here is some example output:
setting handler
curr_addr = (nil)
jmp_code = 0
testing read
handling segfault
curr_access = -1
jmp_code = 1
base[0].RW = -1
base[0].length = 4096
base[0].start = (nil)
curr_addr = 0x1000
jmp_code = 0
testing read
Segmentation fault (core dumped)
It's strange that I can handle one segfault just fine, but then it wont handle again. I would really appreciate any help.
In some of the systems, it does not support the re-assignig of singal handler. we have to re assign the handle after the calling of that function_handler.
Unreliable Signals
One problem with these early versions is that the action for a signal was reset to its default each time the signal occurred.
int sig_int();
...
signal(SIGINT, sig_int);
...
sig_int()
{
signal(SIGINT, sig_int);
...
}
Related
I'm having trouble terminating my server in my multithreaded program (one server, multiple clients).
When the variable global_var, which counts the number of currently connected clients, gets set to 0, the server should terminate, but it doesn't.
What I think is happening is since accept() is blocking , the code never reaches the break condition in main loop.
It's breaking correctly out of thread_func but then it blocks inside the while loop, just before the accept() call and after printing "Exiting thread_func".
volatile int finished = 0; // Gets set to 1 by catching SIGINT/SIGSTOP
int global_var = 0; // When it gets to 0, server should terminate
int server_fd;
void * thread_func(void* arg)
{
do_some_pre_stuff();
while(1)
{
if(!global_var)
{
close(server_fd);
finished = 1;
break;
}
if(recv(...) > 0)
{
do_more_stuff()
}
else
{
disconnect_client();
global_var--;
break;
}
}
free_more_ressources();
return NULL;
}
int main()
{
do_initial_stuff();
init_socket();
listen();
while (!finished)
{
if( (fd = accept(server_fd,...)) == -1)
exit(-1);
global_var++;
/* Some intermediate code */
if(!global_var)
break;
// Thread for the newly connected player
if(pthread_create(&thread_id[...], NULL, thread_func, (void*)some_arg)
exit(-1);
}
free_resources();
puts("Exiting thread_func");
}
I tried the advice listed here without success (except the pipe answer, not trying to mess with pipes).
I'm new to socket programming but what I tried so far looked correct but none of the solutions worked (including semaphores, pthread_cancel,etc)
PS: synchronization has been implemented, just omitted here for readability
I'm fairly new to signals. It's an AF_INET socket, handling SIGIO. The first connection and packet are received and handled (displayed) properly, as far as I can tell.
Subsequent sends are not, however. A breakpoint on the signal handler in GDB (as well as the atomic state flag) shows that the signal handler is never raised again when packets are sent.
Relevant portions of my code (sections missing):
char buffer[1024];
volatile sig_atomic_t data_waiting = 0;
struct sigaction saio;
// [...] Socket setup and accept
saio.sa_handler = signal_handler_IO;
saio.sa_flags = 0;
saio.sa_restorer = NULL;
sigemptyset(&saio.sa_mask);
sigaddset(&saio.sa_mask, SIGINT);
sigaction(SIGIO, &saio, NULL);
fcntl(connected_sockfd, F_SETOWN, getpid());
fcntl(connected_sockfd, F_SETFL, FASYNC);
while(run_main)
{
usleep(500);
if(data_waiting == 1)
{
data_waiting = 0;
bytes = read(connected_sockfd, buffer, 1023);
if(bytes > 0)
{
buffer[bytes] = 0; // null termination
printf("Message: %s\n", buffer);
buffer[0] = 0; // "reset" string
}
}
}
and my handler:
void signal_handler_IO(int status)
{
data_waiting = 1;
}
I have nearly the same code handling SIGIO on a serial port file descriptor with zero issues, so I'm really stumped. I assumed the handler would be installed identically, but I might be wrong there.
I'm trying to run QEMU's user mode emulator as a thread in a larger program that I'm writing. I've modified the linux-user/main.c file so that the standard int main(int argc, char **argv, char **envp function is now called void *qemu_user_mode_func(void *arg). I've also added pthread_exit(NULL) to the end of that function, as is standard practice for pthreads (or so I've been told).
However, when I try to run a second thread that contains my own test function (shown below in void *test_func(void *arg)), the process exits before the second thread completes, even with a call to pthread_join(tid), which I've read blocks the calling thread until thread tid returns. Does QEMU's user mode emulation exit in such a way that would prevent pthread_join from exiting, or am I just using threads wrong?
Here's my code (not including the bulk of qemu_user_mode_func):
void *qemu_user_mode_func(void *arg)
{
thread_data_t *thread_data;
int argc;
char **argv;
char **envp;
/** QEMU's normal code **/
//return 0;
pthread_exit(NULL);
}
void *test_func(void *arg) {
struct timespec time;
time.tv_sec = 7;
time.tv_nsec = 0;
nanosleep(&time, NULL);
printf("hello, world - from a thread\n");
pthread_exit(NULL);
}
int main(int argc, char**argv, char **envp) {
//Initialize variables to create thread
int rc;
pthread_t threads[2];
thread_data_t main_args;
main_args.tid = 1;
main_args.argc = argc;
main_args.argv = argv;
main_args.envp = envp;
//Create thread
if ((rc = pthread_create(&(threads[0]), NULL, test_func, NULL))) {
fprintf(stderr, "error: pthread_create, rc: %d\n", rc);
return EXIT_FAILURE;
}
if ((rc = pthread_create(&(threads[1]), NULL, qemu_user_mode_func, (void *)&main_args))) {
fprintf(stderr, "error: pthread_create, rc: %d\n", rc);
return EXIT_FAILURE;
}
//Wait for thread to finish, then terminate process
for (rc = 0; rc < 2; rc++) {
pthread_join(threads[rc], NULL);
}
return 0;
}
EDIT: I've discovered in the void cpu_loop(CPUX86State *env) function that when the emulated program reaches its conclusion, QEMU calls syscall 231, which is sys_exit_group (as per 1). So I'm guessing this syscall is terminating the entire process that I'm running. I'd appreciate any tips on how to get around that!
If you turn a complicated preexisting application into thread there are going to be issues. One is that the application can call exit or its variants which will terminate your entire program. There are numerous other issues that could be causing a problem. I would suggest using gdb to determine what is making your program exit.
Problem was solved by editing the following section in void cpu_loop(CPUX86State *env). I capture either sys_exit_group and sys_exit system calls before they are executed, and just return from the function instead.
Original:
void cpu_loop(CPUX86State *env)
{
CPUState *cs = CPU(x86_env_get_cpu(env));
int trapnr;
abi_ulong pc;
target_siginfo_t info;
for(;;) {
cpu_exec_start(cs);
trapnr = cpu_x86_exec(env);
cpu_exec_end(cs);
switch(trapnr) {
case 0x80:
/* linux syscall from int $0x80 */
env->regs[R_EAX] = do_syscall(env,
env->regs[R_EAX],
env->regs[R_EBX],
env->regs[R_ECX],
env->regs[R_EDX],
env->regs[R_ESI],
env->regs[R_EDI],
env->regs[R_EBP],
0, 0);
break;
#ifndef TARGET_ABI32
case EXCP_SYSCALL:
/* linux syscall from syscall instruction */
env->regs[R_EAX] = do_syscall(env,
env->regs[R_EAX],
env->regs[R_EDI],
env->regs[R_ESI],
env->regs[R_EDX],
env->regs[10],
env->regs[8],
env->regs[9],
0, 0);
break;
#endif
Modified:
void cpu_loop(CPUX86State *env)
{
CPUState *cs = CPU(x86_env_get_cpu(env));
int trapnr;
abi_ulong pc;
target_siginfo_t info;
for(;;) {
cpu_exec_start(cs);
trapnr = cpu_x86_exec(env);
cpu_exec_end(cs);
switch(trapnr) {
case 0x80:
/* linux syscall from int $0x80 */
env->regs[R_EAX] = do_syscall(env,
env->regs[R_EAX],
env->regs[R_EBX],
env->regs[R_ECX],
env->regs[R_EDX],
env->regs[R_ESI],
env->regs[R_EDI],
env->regs[R_EBP],
0, 0);
break;
#ifndef TARGET_ABI32
case EXCP_SYSCALL:
/* linux syscall from syscall instruction */
----> if ((env->regs[R_EAX] == __NR_exit_group) || (env->regs[R_EAX] == __NR_exit)) {
return;
}
env->regs[R_EAX] = do_syscall(env,
env->regs[R_EAX],
env->regs[R_EDI],
env->regs[R_ESI],
env->regs[R_EDX],
env->regs[10],
env->regs[8],
env->regs[9],
0, 0);
break;
#endif
I am working on some example code in a C text called "Headfirst C". I wrote a practice application that demonstrates signal handling and after finishing the chapter decided to play around with it a bit. I'm an engineer used to working in LabVIEW (high degree of concurrency and intuitive event handling functionality) so I am interested in using alarms and signal handlers to generate periodic events. My question is this:
In the following sample code, what is the proper method or best practices for discarding a variable amount of user input that sits in stdin? I have written this little app as a demo and a 3 second alarm triggers an annoying "howdy!" message to interrupt an fgets call. The thing I have noticed, however, is that if the user is interrupted in the middle of typing, when he eventually does press enter any text entered (having been interrupted or not) will be echoed back. I want to discard anything that was interrupted before the user pressed enter.
//Sample Program - Signal Handling & Alarms
//Header Includes
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
//Function Declarations
//Handler Functions
void diediedie(int sig);
void howdy(int sig);
//Handler Register Function
int catchSignal(int signum, void(*handler)(int));
//Variable declarations
//Declare interrupted flags
static int interrupted = 0;
//Program entrypoint
int main() {
//Register interrupt hander, catch errors
if(catchSignal(SIGINT, diediedie) == -1) {
fprintf(stderr, "Could not register interrupt handler");
exit(2);
}
//Register alarm handler, catch errors
if(catchSignal(SIGALRM, howdy) == -1) {
fprintf(stderr, "Could not register alarm handler");
exit(2);
}
//Create initial alarm trigger
alarm(3);
//Do something stupid while waiting for signals
char name[30];
printf("Enter your name: ");
//Keep waiting for user input even if interrupted by alarm signal
while(1) {
fgets(name, 30, stdin);
if(interrupted) {
// reset interrupted flag
interrupted = 0;
// ***** ADD CODE TO DISCARD INTERRUPTED INPUT HERE ******
}
else {
//echo user input and break out
printf("Hello %s\n", name);
break;
}
}
//end program
return 0;
}
//interrupt handler definition
void diediedie(int sig) {
//write some stuff, exit program
puts("Goodbye world!");
exit(1);
}
//alarm handler definition
void howdy(int sig) {
//set interrupted flag
interrupted = 1;
//write some annoying message
puts("howdy!");
//set another alarm trigger
alarm(3);
//**** COULD STDIN BE FLUSHED HERE? ****
}
//signal handler registration function definition
int catchSignal(int signum, void(*handler)(int)) {
//register handler
struct sigaction action;
action.sa_handler = handler;
sigemptyset(&action.sa_mask);
action.sa_flags = 0;
return sigaction(signum, &action, NULL);
}
Is the proper place to do this purge within the alarm handler? Note the comments indicating my thoughts for proper code location.
I have considered the following:
while(getchar() != EOF) {}
I am also wondering, what happens while fgets is waiting for user input and SIGALRM is raised? Is that function terminated? I observe that if I don't include the while loop to check the interrupted flag and respond appropriately, the program will finish the fgets, dump some garbage on the screen (I assume the current state of stdin?) and end the program.
Thanks for any advice!
In Unix, a signal handler occurs out of band with your code. If the signal occurs in the middle of a blocking system call, the system call will exit with errno set to EINTR. But I believe that fgets() is handling this interruption for you and continuing on without returning control to you.
If you are using a Unix based OS and typing in input from the command line, what's really going on here is that you are reading data from your terminal in cooked mode. Your program doesn't get any data from the TTY until return is pressed. You need to set the terminal into 'raw' mode. Here is an example of how to do that integrated with your code:
//Sample Program - Signal Handling & Alarms
//Header Includes
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <termios.h>
//Function Declarations
//Handler Functions
void diediedie(int sig);
void howdy(int sig);
//Handler Register Function
int catchSignal(int signum, void(*handler)(int));
//Variable declarations
//Declare interrupted sa_flags
static int interrupted = 0;
static struct termios save_termios;
//Program entrypoint
int main() {
struct termios buf;
int fd = 1;
// This is derived from from Stevens, "Advanced Programming in the UNIX Environment"
if (tcgetattr(fd, &save_termios) < 0) /* get the original state */
return -1;
buf = save_termios;
buf.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
/* echo off, canonical mode off, extended input
processing off, signal chars off */
buf.c_iflag |= BRKINT | ICRNL;
/* SIGINT on BREAK, CR-toNL on */
buf.c_cflag &= ~(CSIZE | PARENB);
/* clear size bits, parity checking off */
buf.c_cflag |= CS8;
/* set 8 bits/char */
buf.c_oflag &= ~(OPOST);
/* output processing off */
buf.c_cc[VMIN] = 1; /* 1 byte at a time */
buf.c_cc[VTIME] = 0; /* no timer on input */
if (tcsetattr(fd, TCSAFLUSH, &buf) < 0)
return -1;
//Register interrupt hander, catch errors
if(catchSignal(SIGINT, diediedie) == -1) {
fprintf(stderr, "Could not register interrupt handler");
exit(2);
}
//Register alarm handler, catch errors
if(catchSignal(SIGALRM, howdy) == -1) {
fprintf(stderr, "Could not register alarm handler");
exit(2);
}
//Create initial alarm trigger
alarm(3);
//Do something stupid while waiting for signals
char name[30];
printf("Enter your name: ");
//Keep waiting for user input even if interrupted by alarm signal
char nextchar = 0;
char *p;
p = name;
while(nextchar != '\n') {
nextchar = fgetc(stdin);
if (interrupted) {
// reset interrupted flag
interrupted = 0;
//Discard interrupted input by reseting 'p' to the start of the buffer
p = name;
*p = 0;
continue;
}
if (nextchar == '\n') {
*p = 0;
fputc('\r', stdout);
fputc('\n', stdout);
break;
}
// You'll have to handle some characters manually to emulate what the
// terminal does, or you could filter them out using a function like isprint()
//
if (nextchar == 127) {
// *** handle backspace
if (p > name) {
p--;
}
// TODO: To handle this right you'll have to backup the cursor on the screen
} else {
*p = nextchar;
p++;
}
fputc(nextchar, stdout);
// Handle buffer overflow
if (p-name == sizeof(name) - 1) {
*p = 0;
break;
}
}
// echo user input
printf("Input is: %s\r\n", name);
tcsetattr(1, TCSAFLUSH, &save_termios);
}
//interrupt handler definition
void diediedie(int sig) {
//write some stuff, exit program
puts("Goodbye world!");
tcsetattr(1, TCSAFLUSH, &save_termios);
exit(1);
}
//alarm handler definition
void howdy(int sig) {
//set interrupted flag
interrupted = 1;
//write some annoying message
puts("howdy!");
//set another alarm trigger
alarm(3);
}
// signal handler registration function definition
int catchSignal(int signum, void(*handler)(int)) {
//register handler
struct sigaction action;
action.sa_handler = handler;
sigemptyset(&action.sa_mask);
action.sa_flags = 0;
return sigaction(signum, &action, NULL);
}
Note that you need to save the original terminal settings and restore them before the program exits! If you get into trouble, you might break the terminal settings. Use reset or stty sane from the command line to restore sane terminal settings. For more information on the termios datastructure, see the man pages.
You can also use libraries like ncurses to handle raw input.
To do this right, you'd need to put the terminal into "raw" mode, in which every keystroke is returned immediately to the application, instead of being accumulated with interpretation of line-editing characters ("cooked" mode, which is the default).
Of course, if you don't let the kernel handle line-editing (backspace, for example), then you need to do it yourself, which is quite a lot of work. Interpreting backspace and whatever other editing commands you're interested in isn't that hard, but maintaining the correct appearance on the terminal is a pain.
See man termios for a lot more information.
I'm trying to find out how to fix these memory leaks I'm getting while running this program with Valgrind. The leaks occur with the two allocations in nShell_client_main. But I'm not
sure how to properly free them.
I've tried freeing them at nShell_Connect, but it's causing libUV to abort the program. I've tried freeing them at the end of nShell_client_main, but then I get read/write errors when closing the loop. Does anyone know how I'm supposed to close these handles? I've read this, which got me started. But, it seams out-dated because uv_ip4_addr has a different prototype in the latest version.
(nShell_main is the "entry" point)
#include "nPort.h"
#include "nShell-main.h"
void nShell_Close(
uv_handle_t * term_handle
){
}
void nShell_Connect(uv_connect_t * term_handle, int status){
uv_close((uv_handle_t *) term_handle, 0);
}
nError * nShell_client_main(nShell * n_shell, uv_loop_t * n_shell_loop){
int uv_error = 0;
nError * n_error = 0;
uv_tcp_t * n_shell_socket = 0;
uv_connect_t * n_shell_connect = 0;
struct sockaddr_in dest_addr;
n_shell_socket = malloc(sizeof(uv_tcp_t));
if (!n_shell_socket){
// handle error
}
uv_error = uv_tcp_init(n_shell_loop, n_shell_socket);
if (uv_error){
// handle error
}
uv_error = uv_ip4_addr("127.0.0.1", NPORT, &dest_addr);
if (uv_error){
// handle error
}
n_shell_connect = malloc(sizeof(uv_connect_t));
if (!n_shell_connect){
// handle error
}
uv_error = uv_tcp_connect(n_shell_connect, n_shell_socket, (struct sockaddr *) &dest_addr, nShell_Connect);
if (uv_error){
// handle error
}
uv_error = uv_run(n_shell_loop, UV_RUN_DEFAULT);
if (uv_error){
// handle error
}
return 0;
}
nError * nShell_loop_main(nShell * n_shell){
int uv_error = 0;
nError * n_error = 0;
uv_loop_t * n_shell_loop = 0;
n_shell_loop = malloc(sizeof(uv_loop_t));
if (!n_shell_loop){
// handle error
}
uv_error = uv_loop_init(n_shell_loop);
if (uv_error){
// handle error
}
n_error = nShell_client_main(n_shell, n_shell_loop);
if (n_error){
// handle error
}
uv_loop_close(n_shell_loop);
free(n_shell_loop);
return 0;
}
The assertion is happening at the end of the switch statement in this excerpt of code (taken from Joyent's libUV page on Github):
void uv_close(uv_handle_t* handle, uv_close_cb close_cb) {
assert(!(handle->flags & (UV_CLOSING | UV_CLOSED)));
handle->flags |= UV_CLOSING;
handle->close_cb = close_cb;
switch (handle->type) {
case UV_NAMED_PIPE:
uv__pipe_close((uv_pipe_t*)handle);
break;
case UV_TTY:
uv__stream_close((uv_stream_t*)handle);
break;
case UV_TCP:
uv__tcp_close((uv_tcp_t*)handle);
break;
case UV_UDP:
uv__udp_close((uv_udp_t*)handle);
break;
case UV_PREPARE:
uv__prepare_close((uv_prepare_t*)handle);
break;
case UV_CHECK:
uv__check_close((uv_check_t*)handle);
break;
case UV_IDLE:
uv__idle_close((uv_idle_t*)handle);
break;
case UV_ASYNC:
uv__async_close((uv_async_t*)handle);
break;
case UV_TIMER:
uv__timer_close((uv_timer_t*)handle);
break;
case UV_PROCESS:
uv__process_close((uv_process_t*)handle);
break;
case UV_FS_EVENT:
uv__fs_event_close((uv_fs_event_t*)handle);
break;
case UV_POLL:
uv__poll_close((uv_poll_t*)handle);
break;
case UV_FS_POLL:
uv__fs_poll_close((uv_fs_poll_t*)handle);
break;
case UV_SIGNAL:
uv__signal_close((uv_signal_t*) handle);
/* Signal handles may not be closed immediately. The signal code will */
/* itself close uv__make_close_pending whenever appropriate. */
return;
default:
assert(0); // assertion is happening here
}
uv__make_close_pending(handle);
}
I could call uv__tcp_close manually, but it's not in the public headers (and probably not the right solution anyway).
libuv is not done with a handle until it's close callback is called. That is the exact moment when you can free the handle.
I see you call uv_loop_close, but you don't check for the return value. If there are still pending handles, it will return UV_EBUSY, so you should check for that.
If you want to close a loop and close all handles, you need to do the following:
Use uv_stop to stop the loop
Use uv_walk and call uv_close on all handles which are not closing
Run the loop again with uv_run so all close callbacks are called and you can free the memory in the callbacks
Call uv_loop_close, it should return 0 now
I finally figured out how to stop a loop and clean up all handles.
I created a bunch of handles and SIGINT signal handle:
uv_signal_t *sigint = new uv_signal_t;
uv_signal_init(uv_default_loop(), sigint);
uv_signal_start(sigint, on_sigint_received, SIGINT);
When SIGINT is received (Ctrl+C in console is pressed) the on_sigint_received callback is called.
The on_sigint_received looks like:
void on_sigint_received(uv_signal_t *handle, int signum)
{
int result = uv_loop_close(handle->loop);
if (result == UV_EBUSY)
{
uv_walk(handle->loop, on_uv_walk, NULL);
}
}
It triggers a call back function on_uv_walk:
void on_uv_walk(uv_handle_t* handle, void* arg)
{
uv_close(handle, on_uv_close);
}
It tries to close each opened libuv handle.
Note: that I do not call uv_stop before uv_walk, as mentioned saghul.
After on_sigint_received function is called libuv loop continuous the execution and on the next iteration calls on_uv_close for each opened handle. If you call the uv_stop function, then the on_uv_close callback will not be called.
void on_uv_close(uv_handle_t* handle)
{
if (handle != NULL)
{
delete handle;
}
}
After that libuv do not have opened handles and finishes the loop (exits from uv_run):
uv_run(uv_default_loop(), UV_RUN_DEFAULT);
int result = uv_loop_close(uv_default_loop());
if (result)
{
cerr << "failed to close libuv loop: " << uv_err_name(result) << endl;
}
else
{
cout << "libuv loop is closed successfully!\n";
}
I like the solution given by Konstantin Gindemit
I did run into a couple of problems however. His on_uv_close() function ends with a core dump. Also the uv_signal_t variable was causing valgrind to report a "definitely lost" memory leak.
I am using his code with fixes for these 2 situations.
void on_uv_walk(uv_handle_t* handle, void* arg) {
uv_close(handle, NULL);
}
void on_sigint_received(uv_signal_t *handle, int signum) {
int result = uv_loop_close(handle->loop);
if(result == UV_EBUSY) {
uv_walk(handle->loop, on_uv_walk, NULL);
}
}
int main(int argc, char *argv[]) {
uv_signal_t *sigint = new uv_signal_t;
uv_signal_init(uv_default_loop(), sigint);
uv_signal_start(sigint, on_sigint_received, SIGINT);
uv_loop_t* main_loop = uv_default_loop();
...
uv_run(main_loop, UV_RUN_DEFAULT));
uv_loop_close(uv_default_loop());
delete sigint;
return 0;
}