Search code examples
gosignalscgo

handle signal with cgo, go cannot handle signal


Here is my main.go

package main

/*
#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>

// Global variable to store old action
struct sigaction old_action;

// Signal handler function
void handler(int signum, siginfo_t *info, void *context) {
    printf("Received signal: %d\n", signum);
    printf("Sent by PID: %d\n", info->si_pid);
    printf("Signal code: %d\n", info->si_code);
    printf("User ID: %d\n", info->si_uid);
    if (info->si_pid != 0) {
        char proc_status_path[256];
        snprintf(proc_status_path, sizeof(proc_status_path), "/rootfs/proc/%d/status", info->si_pid);
        FILE *fp = fopen(proc_status_path, "r");
        if (fp == NULL) {
            perror("fopen");
            return;
        }
        char buffer[256];
        while (fgets(buffer, sizeof(buffer), fp)) {
            printf("%s", buffer);
        }
        fclose(fp);
    }
}


// Function to set up signal handling
void setup_signal_handler() {
    struct sigaction action;
    // Initialize the action structure
    memset(&action, 0, sizeof(action));
    sigfillset(&action.sa_mask); // Block all signals while the handler is executing
    action.sa_sigaction = handler; // Set the signal handler function
    action.sa_flags = SA_SIGINFO | SA_ONSTACK; // Set flags

    // Set up the signal handler for SIGTERM
    if (sigaction(SIGTERM, &action, &old_action) < 0) {
        perror("sigaction");
        exit(EXIT_FAILURE);
    }
}
*/
import "C"
import "fmt"

func main() {
    C.setup_signal_handler()
    for {
        fmt.Println("Sleeping..., waiting for signal")
        C.sleep(10)
    }
}

I am trying to use C language to help me to handler signal and print out the pid of signal sender. The original Go packages(syscall, os,... etc) does not provide such information. However, when I run my code above, there are two process starts up. One is go run . and the other one is signal, which is the name of my go package. When I send signal to go run ., nothing happens while to signal, informations pop up.

What I understand is that the implicity C package fork another process to run those C code. I cannot apply the signal handler in C to my go process.

My goal is to print out informations of any kind of signal sent to my go process. Related informations should include at least pid, ppid. Maybe there is another better solution for my situation.

Is there any document about cgo? I couldn't find the document that describe the process control machanism of cgo package.


[UPDATE]

Thank @kostix and @eik for answering! I tried go build and run the binary later.

Before the handler return, I replace the handler of SIGTERM with the old action and raise SIGTERM again. It works and make the Go signal.Notify succeed to push os.Signal into channel. However, When I replace the handler back to the original one, it turns out to be a recursive signal.

        // ...
        fclose(fp);
    }
    fflush(stdout);
    sigaction(SIGTERM, &old_action, &action);
    raise(SIGTERM);   
}

However, implementation above could solve my problem. I only need to handle some signal once and then pass it to Go handler.


Solution

  • The process go run . is your Go compiler. It compiles your program (including the C code) into an executable, and then runs the resulting (build) program with buildRunProgram, which calls RunStdin which starts a new progress with Cmd.Run.

    So wahr you are seeing is normal and signal is your program. When you don't like this, use go build and run it separately with ./signal.

    Edit: With cgo, the C code is in the same binary as the Go code. Since they run in the same process, the signal is delivered to only once, so only one handler receives a signal. This has nothing to do with Go or a program being written in multiple languages, try writing a C program with two signal handlers.