skip to Main Content

I have a strange problem. I want to write a simple C programm that outputs "Daytime" repeatedly unless I press Ctrl+C (a SIGINT signal) which makes it to switch to "Nighttime" and vice versa. This part of the programm works. I also want to implement the termination of the programm, which would work over the SIGUSR1 signal. Yet when I type kill -SIGUSR1 <pid> or kill -10 <pid> it doesn’t work. Why?

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>


volatile int termination;
volatile int state;

void handler(int num) {
    if (state == 0) {
        state = 1;
    } else {
        state = 0;
    }
}

void terminator(int num) {
    // kill -SIGUSR1 3289
    // kill -USR1 2040
    // kill -USR 2040
    // kill -10 3289
    printf("I work!n");
    termination = 0;
}

int main(void) {
    struct sigaction act_int, act_usr;

    act_int.sa_handler = handler;
    act_usr.sa_handler = terminator;

    sigaction(SIGUSR1, &act_usr, NULL);
    sigaction(SIGINT, &act_int, NULL);

    termination = 1; 
    state = 1;

    printf("My PID is: %dn", getpid());

    while (termination == 1) {
        sleep(5);
        if (state == 0) {
            printf("Daytimen");
        } else {
            printf("Nighttimen");
        }
    }
    printf("Thank you for using my function! :)n");
    return 0;
}

Kind regards

EDIT (My modified new code):

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

volatile int termination = 1;
volatile int state = 1;

void handler(int num) {
    if (state == 0) {
        state = 1;
    } else {
        state = 0;
    }
}

void terminator(int num) {
    // kill -SIGUSR1 20560
    // kill -10 20560
    write(1, "Terminatedn", 10);
    termination = 0;
}

int main(void) {
    struct sigaction act_int, act_usr;

    memset(&act_int, 0, sizeof act_int);
    memset(&act_usr, 0, sizeof act_usr);

    act_int.sa_handler = handler;
    act_usr.sa_handler = terminator;

    sigaction(SIGINT, &act_int, NULL);
    sigaction(SIGUSR1, &act_usr, NULL);

    printf("My PID is: %dn", getpid());

    while (termination == 1) {
        if (state == 0) {
            printf("Daytimen");
        } else {
            printf("Nighttimen");
        }
        sleep(5);
    }
    printf("Thank you for using my function! :)n");
    return 0;
}

EDIT: I ran my program with strace and redirected the output to a file, here are the contents:

execve("./program.x", ["./program.x"], 0x7ffe2fc5b530 /* 58 vars */) = 0
brk(NULL)                               = 0x55f3ca56b000
arch_prctl(0x3001 /* ARCH_??? */, 0x7ffd50daca00) = -1 EINVAL (Das Argument ist ungültig)
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f586663f000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (Datei oder Verzeichnis nicht gefunden)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
newfstatat(3, "", {st_mode=S_IFREG|0644, st_size=114855, ...}, AT_EMPTY_PATH) = 0
mmap(NULL, 114855, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f5866622000
close(3)                                = 0
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "177ELF21133>1P2372"..., 832) = 832
pread64(3, "64@@@"..., 784, 64) = 784
pread64(3, "4 5GNU230043"..., 48, 848) = 48
pread64(3, "4243GNUi8235HZ227223333350s360352,223340."..., 68, 896) = 68
newfstatat(3, "", {st_mode=S_IFREG|0644, st_size=2216304, ...}, AT_EMPTY_PATH) = 0
pread64(3, "64@@@"..., 784, 64) = 784
mmap(NULL, 2260560, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f58663fa000
mmap(0x7f5866422000, 1658880, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x28000) = 0x7f5866422000
mmap(0x7f58665b7000, 360448, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1bd000) = 0x7f58665b7000
mmap(0x7f586660f000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x214000) = 0x7f586660f000
mmap(0x7f5866615000, 52816, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f5866615000
close(3)                                = 0
mmap(NULL, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f58663f7000
arch_prctl(ARCH_SET_FS, 0x7f58663f7740) = 0
set_tid_address(0x7f58663f7a10)         = 20560
set_robust_list(0x7f58663f7a20, 24)     = 0
rseq(0x7f58663f80e0, 0x20, 0, 0x53053053) = 0
mprotect(0x7f586660f000, 16384, PROT_READ) = 0
mprotect(0x55f3c96a2000, 4096, PROT_READ) = 0
mprotect(0x7f5866679000, 8192, PROT_READ) = 0
prlimit64(0, RLIMIT_STACK, NULL, {rlim_cur=8192*1024, rlim_max=RLIM64_INFINITY}) = 0
munmap(0x7f5866622000, 114855)          = 0
rt_sigaction(SIGINT, {sa_handler=0x55f3c96a0229, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7f586643c520}, NULL, 8) = 0
rt_sigaction(SIGUSR1, {sa_handler=0x55f3c96a0257, sa_mask=[], sa_flags=SA_RESTORER, sa_restorer=0x7f586643c520}, NULL, 8) = 0
getpid()                                = 20560
newfstatat(1, "", {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0), ...}, AT_EMPTY_PATH) = 0
getrandom("x62x94x42xccxdfx17xbcx20", 8, GRND_NONBLOCK) = 8
brk(NULL)                               = 0x55f3ca56b000
brk(0x55f3ca58c000)                     = 0x55f3ca58c000
write(1, "My PID is: 20560n", 17)      = 17
write(1, "Nighttimen", 10)             = 10
clock_nanosleep(CLOCK_REALTIME, 0, {tv_sec=5, tv_nsec=0}, 0x7ffd50dac930) = 0
write(1, "Nighttimen", 10)             = 10
clock_nanosleep(CLOCK_REALTIME, 0, {tv_sec=5, tv_nsec=0}, 0x7ffd50dac930) = 0
write(1, "Nighttimen", 10)             = 10
clock_nanosleep(CLOCK_REALTIME, 0, {tv_sec=5, tv_nsec=0}, 0x7ffd50dac930) = 0
write(1, "Nighttimen", 10)             = 10
clock_nanosleep(CLOCK_REALTIME, 0, {tv_sec=5, tv_nsec=0}, {tv_sec=4, tv_nsec=592627880}) = ? ERESTART_RESTARTBLOCK (Interrupted by signal)
--- SIGINT {si_signo=SIGINT, si_code=SI_KERNEL} ---
rt_sigreturn({mask=[]})                 = -1 EINTR (Unterbrechung während des Betriebssystemaufrufs)
write(1, "Daytimen", 8)                = 8
clock_nanosleep(CLOCK_REALTIME, 0, {tv_sec=5, tv_nsec=0}, {tv_sec=3, tv_nsec=876879211}) = ? ERESTART_RESTARTBLOCK (Interrupted by signal)
--- SIGINT {si_signo=SIGINT, si_code=SI_KERNEL} ---
rt_sigreturn({mask=[]})                 = -1 EINTR (Unterbrechung während des Betriebssystemaufrufs)
write(1, "Nighttimen", 10)             = 10
clock_nanosleep(CLOCK_REALTIME, 0, {tv_sec=5, tv_nsec=0}, {tv_sec=4, tv_nsec=383598465}) = ? ERESTART_RESTARTBLOCK (Interrupted by signal)
--- SIGINT {si_signo=SIGINT, si_code=SI_KERNEL} ---
rt_sigreturn({mask=[]})                 = -1 EINTR (Unterbrechung während des Betriebssystemaufrufs)
write(1, "Daytimen", 8)                = 8
clock_nanosleep(CLOCK_REALTIME, 0, {tv_sec=5, tv_nsec=0}, {tv_sec=4, tv_nsec=611480158}) = ? ERESTART_RESTARTBLOCK (Interrupted by signal)
--- SIGINT {si_signo=SIGINT, si_code=SI_KERNEL} ---
rt_sigreturn({mask=[]})                 = -1 EINTR (Unterbrechung während des Betriebssystemaufrufs)
write(1, "Nighttimen", 10)             = 10
clock_nanosleep(CLOCK_REALTIME, 0, {tv_sec=5, tv_nsec=0}, 0x7ffd50dac930) = 0
write(1, "Nighttimen", 10)             = 10
clock_nanosleep(CLOCK_REALTIME, 0, {tv_sec=5, tv_nsec=0}, 0x7ffd50dac930) = 0
write(1, "Nighttimen", 10)             = 10
clock_nanosleep(CLOCK_REALTIME, 0, {tv_sec=5, tv_nsec=0}, 0x7ffd50dac930) = 0
write(1, "Nighttimen", 10)             = 10
clock_nanosleep(CLOCK_REALTIME, 0, {tv_sec=5, tv_nsec=0}, 0x7ffd50dac930) = 0
write(1, "Nighttimen", 10)             = 10
clock_nanosleep(CLOCK_REALTIME, 0, {tv_sec=5, tv_nsec=0}, {tv_sec=0, tv_nsec=558127083}) = ? ERESTART_RESTARTBLOCK (Interrupted by signal)
--- SIGHUP {si_signo=SIGHUP, si_code=SI_USER, si_pid=20542, si_uid=1000} ---
+++ killed by SIGHUP +++

My platform information (obtained with uname -a):

Linux Luxdragon 5.15.0-76-generic #83-Ubuntu SMP Thu Jun 15 19:16:32 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux

So basically Linux Mint 21.1. I tested the code in VSCodium, in the Linux terminal and on Onlinegdb.

EDIT: If I type kill -SIGUSR1 in a separate terminal, then my program terminates as it should. Why can’t I terminate it in the same terminal, where the program is run? Why does Ctrl+C then get processed correctly?

2

Answers


  1. Main problem

    As deduced from comments, the questioner was typing the kill -SIGUSR1 command into the same terminal / shell that was running their program. In that shell, input is fed into the running program, not into the shell. The ^-C were intercepted by the shell and converted into signals, but normal text typed was just going nowhere.

    Other problems

    struct sigaction contains more than just the sa_handler field. It contains other things too – e.g. on my Ubuntu 22.04 box, the manpage shows it as:

    struct sigaction {
        void     (*sa_handler)(int);
        void     (*sa_sigaction)(int, siginfo_t *, void *);
        sigset_t   sa_mask;
        int        sa_flags;
        void     (*sa_restorer)(void);
    };
    

    Your struct sigaction variables are local variables, and hence have undefined values unless they are somehow initialised, or have all their fields set. As you provide no initialisation, and only set one field in each of them, then you will invoke undefined behaviour when sigaction() accesses the uninitialised structure fields.

    The simplest solution might be to use memset() to set them to zero, before you start setting the fields you care about. e.g.

        struct sigaction act_int, act_usr;
    
        memset(&act_int, 0, sizeof act_int);
        memset(&act_usr, 0, sizeof act_usr);
    
        act_int.sa_handler = handler;
        act_usr.sa_handler = terminator;
    
    Login or Signup to reply.
  2. You have this local declaration in your main() routine:

        struct sigaction act_int, act_usr;
    

    then you initialize

        act_int.sa_handler = handler;
        act_usr.sa_handler = terminator;
    

    but don’t initialize the other fields (which should be zeroed) leading to Undefined Behaviour.

    Just initialize it in this alternate form:

        struct sigaction act_int = {
            .sa_handler = handler,
        }, act_usr = {
            .sa_handler = terminator,
        };
    

    This initialization will zero all the other fields, as you are initializing the whole structure in this way.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search