Thread: Proper termination of a daemon process

  1. #1
    Registered User
    Join Date
    Mar 2017
    Posts
    6

    Proper termination of a daemon process

    I've written a program to run as a daemon, it runs fine and does what I intended it to do.
    To terminate the program, I send it a TERM signal.
    The signal handler closes the files before terminating the program.
    Code:
    /* Signal handler: close files then exit */
    void closeandexit()
    {
        close(mouse);
        fclose(fptr);
        return EXIT_SUCCESS;
    }
    
    signal(SIGINT, closeandexit);
    signal(SIGTERM, closeandexit);
    I normally return from functions, but this function terminates the program.
    Should I exit or return? Does it really matter?

    Thanks, Tom

  2. #2
    Registered User
    Join Date
    Jun 2015
    Posts
    1,640
    Technically you shouldn't be closing the files in a signal handler (you should do very little in a signal handler). The best thing to do is to change the value of a global sig_atomic_t variable and then respond to that change during the resumed (normal) program execution.
    Code:
    #include <stdio.h>
    #include <signal.h>
    
    sig_atomic_t g_continue = 1;
    
    void termination_handler(int sig) {
        g_continue = 0;
    }
    
    int main() {
        int i = 0;
    
        signal(SIGINT, termination_handler); // Testing with ctrl-C
    
        while (g_continue) {
            printf("%d ", i++);
            fflush(stdout);
        }
    
        // close files here
        printf("\nTerminated\n");
    
        return 0;
    }

  3. #3
    Registered User
    Join Date
    Mar 2017
    Posts
    6
    I tried your suggestion, it doesn't work in my situation.
    The program reads the click of a mouse button and acts on it.
    Code snippit:
    Code:
    while(g_continue) {
    // Read the mouse buttons
    bytes = read(mouse, data,sizeof(data));
    
    // Do a whole bunch of stuff
    
    }
    
    // Fall through to close files and exit
    
    exit(0);
    The program blocks waiting for a button press, so only breaks out after the read(mouse returns, that could be days later.

    Any ideas?
    Last edited by metryc; 03-08-2017 at 01:06 PM.

  4. #4
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,662
    > bytes = read(mouse, data,sizeof(data));
    You mean you don't get a return result of -1, and errno set to EINTR as per the manual page?
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  5. #5
    Registered User
    Join Date
    Mar 2017
    Posts
    6
    Sorry... I just assumed the "mouse" argument to the read function describes the file as a device driver.
    Ya I know the definition of assume.... my bad.

    The read is of a mouse device driver, not a file, so it's blocked until the driver gets the button press.
    Otherwise the system would run-away doing reads that fail, or don't I understand the device driver function.

    Anyway, the original question still needs an answer aside from my technically bad coding.
    Last edited by metryc; 03-08-2017 at 04:20 PM.

  6. #6
    Registered User
    Join Date
    Jun 2015
    Posts
    1,640
    This executes the code after the loop on receiving SIGTERM. It doesn't even need the global flag.
    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <fcntl.h>
    #include <linux/input.h>
    #include <unistd.h>
    #include <signal.h>
    
    #define MOUSE "/dev/input/mouse1"
    
    void termination_handler(int sig) {
        (void)sig;
    }
    
    int main() {
        int fd;
        struct input_event ie;
    
        if ((fd = open(MOUSE, O_RDONLY)) == -1) {
            printf("Cannot open mouse device\n");
            exit(EXIT_FAILURE);
        }
    
        signal(SIGTERM, termination_handler);
    
        while (read(fd, &ie, sizeof(struct input_event)) > 0) {
            char *p = (char*)&ie;
            printf("Left: %d, Middle: %d, Right: %d, x: %d  y: %d\n",
                   *p & 0x1, !!(*p & 0x4), !!(*p & 0x2), p[1], p[2]);
        }
    
        printf("\nTerminated\n");
        close(fd);
    
        return 0;
    }

  7. #7
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,662
    I don't have /dev/mouse, so I'm experimenting with stdin instead.
    Code:
    #include <stdio.h>
    #include <signal.h>
    #include <errno.h>
    #include <string.h>
    #include <unistd.h>
    #include <fcntl.h>
    
    sig_atomic_t g_continue = 1;
    
    void termination_handler(int sig)
    {
      // what you can call here is listed under the section "Async-signal-safe functions"
      // in http://man7.org/linux/man-pages/man7/signal.7.html
      write(1, "INT!!!\n", 7);
      g_continue = 0;
    }
    
    int main()
    {
      signal(SIGINT, termination_handler);  // Testing with ctrl-C
      fcntl(0, F_SETFL, O_NONBLOCK);  // so we don't get stuck in read
    
      while (g_continue) {
        char buff[100];
        int status = read(0, buff, sizeof(buff));
        if (status > 0) {
          printf("Read %d bytes=%.*s\n", status, status, buff);
        } else if (status == -1) {
          if (errno != EAGAIN) {
            printf("Read errno=%d, msg=%s\n", errno, strerror(errno));
          } else {
            int r = usleep(1000);
            if (r != 0) {
              printf("usleep errno=%d, msg=%s\n", errno, strerror(errno));
            }
          }
        } else {
          printf("status=%d\n", status);
        }
      }
    
      printf("\nTerminated\n");
    
      return 0;
    }
    I typed in "hello" to begin with, then pressed ctrl-c trying to type in "world"
    Code:
    $ ./a.out 
    hello
    Read 6 bytes=hello
    
    wor  ^CINT!!!
    usleep errno=4, msg=Interrupted system call
    
    Terminated
    That's a bit messy, so here's another version with different signal handling semantics.
    In this scheme, the system call is always interrupted (and you get to see it), rather than being silently restarted.
    Code:
    int main()
    {
      struct sigaction new = {
        .sa_handler = termination_handler,
        .sa_mask = 0,
        .sa_flags = 0 // specifically, we don't have SA_RESTART
      };
      struct sigaction old = {
        0,
      };
      sigaction(SIGINT, &new, &old);
             
      while (g_continue) {
        char buff[100];
        int status = read(0, buff, sizeof(buff));
        if (status > 0) {
          printf("Read %d bytes=%.*s\n", status, status, buff);
        } else if (status == -1) {
          printf("Read errno=%d, msg=%s\n", errno, strerror(errno));
        } else {
          printf("status=%d\n", status);
        }
      }
    
      printf("\nTerminated\n");
    
      return 0;
    }
    
    // test run
    $ ./a.out 
    hello
    Read 6 bytes=hello
    
    wor  ^CINT!!!
    Read errno=4, msg=Interrupted system call
    
    Terminated
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  8. #8
    Registered User
    Join Date
    Mar 2017
    Posts
    6
    This program above, from algorizm, compiles and runs on my machine, but doesn't terminate with a "kill -TERM ?????".
    It does terminate with a ^C, which is strange, but I need to send the kill -TERM.

    If I add exit(0); to the termination_handler, the program terminates.
    So... the interrupt is being caught, but just returns back to the read() command without the exit.
    Last edited by metryc; 03-09-2017 at 09:27 AM.

  9. #9
    Registered User
    Join Date
    Jun 2015
    Posts
    1,640
    Quote Originally Posted by metryc View Post
    This program above, from algorizm, compiles and runs on my machine, but doesn't terminate with a "kill -TERM ?????".
    It does terminate with a ^C, which is strange, but I need to send the kill -TERM.

    If I add exit(0); to the termination_handler, the program terminates.
    So... the interrupt is being caught, but just returns back to the read() command without the exit.
    What system are you using?

    Try both of Salem's examples. First try them as is, then convert them to work with the mouse. The first example uses non-blocking input, which should hopefully work. The second uses sigaction, which is a replacement for signal. Note that they are written to use SIGINT (ctrl-C) instead of SIGTERM, so you'll have to change that too.

    If neither of those work, another possibility is to use poll.

  10. #10
    Registered User
    Join Date
    Mar 2017
    Posts
    6
    Hopefully this will end this very exciting and informative discussion.

    With Salem's first program, it compiles and runs exactly as he shows it.
    When I substitute the mouse device, it sees and prints the mouse clicks, but when I send it a "kill -INT ????", it prints "INT!!!" but doesn't terminate until it sees another mouse click, printing one mouse click and then terminating.

    Proving device blocking is in effect.

    The second program, again compiles and runs exactly as he shows it.
    When I substitute the mouse device, it sees and prints the mouse clicks.
    This time when I send it a "kill -INT ????", it prints the following
    Code:
    INT!!!
    Read errno=4, msg=Interrupted system call
    
    Terminated
    So the first is blocked, by both the stdin and the mouse, but the second isn't.
    Proving the second example is the better/only way to go with a blocking device driver.

    Unless of course, (tongue in cheek!, wink, wink) you use my original signal handler and either exit or return, which still hasn't been answered.
    Last edited by metryc; 03-09-2017 at 11:36 AM.

  11. #11
    Registered User
    Join Date
    Jun 2015
    Posts
    1,640
    Now that I look at your original function again, it has a mistake in it since it's saying "return 0" in a void function. Presumably you meant "exit(0)". Anyway, you could do the following. Note that you cannot call exit, but you can call _exit. You also can't call fclose, but you can call close. (Search for "Async-signal-safe functions" in the link Salem posted: signal(7) - Linux manual page . Also read the next seciond "Interruption of system calls and library functions by signal handlers".)
    Code:
    void closeandexit() {
        close(mouse);
        close(fptr_fd); // determine fptr_fd beforehand
        _exit(0);
    }
    Still, this is obviously a messy way to end a program if you have the option to do it in a way that follows the normal program flow.

    It does seem somewhat strange that Salem's first example (using signal) is still blocked for you whereas the second (using sigaction) isn't, but I suppose that's what sigaction is all about. It provides a more portable version of signal processing and apparently here it is making all the difference. We should probably always use it in preference to signal.
    Last edited by algorism; 03-09-2017 at 12:04 PM.

  12. #12
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,662
    > When I substitute the mouse device, it sees and prints the mouse clicks, but when I send it a "kill -INT ????",
    > it prints "INT!!!" but doesn't terminate until it sees another mouse click, printing one mouse click and then terminating.
    There is absolutely nothing strange about that.

    On your system (and mine as it happens), the simple signal() interface sets SA_RESTART flag, which causes the read() to resume and deliver just one more result before you get around to testing the global state flag g_continue.

    You can see this if you strace the process to find out how things work at the OS interface.
    Here, the -DTHE_OLD_WAY compiles the version of main which uses signal() in my post #7
    Code:
    $ gcc -DTHE_OLD_WAY foo.c
    $ strace -e trace=file,signal ./a.out 
    execve("./a.out", ["./a.out"], [/* 65 vars */]) = 0
    access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
    access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
    open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
    access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
    open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
    rt_sigaction(SIGINT, {0x4007e6, [INT], SA_RESTORER|SA_RESTART, 0x7f209613b4b0}, {SIG_DFL, [], 0}, 8) = 0
    hello
    Read 6 bytes=hello
    
    wor  --- SIGINT {si_signo=SIGINT, si_code=SI_USER, si_pid=4097, si_uid=1000} ---
    INT!!!
    rt_sigreturn({mask=[]})                 = -1 EINTR (Interrupted system call)
    usleep errno=4, msg=Interrupted system call
    
    Terminated
    Because I've set up the descriptor to be non-blocking, 99% of the time is spent in usleep(), and it's that function which gets interrupted by the signal handler (usleep errno=4), which is then followed by testing the flag and the inevitable exit of the loop.

    It's also worth noting at this stage that read() is one of those functions which is restarted if the signal disposition is SA_RESTART, whereas usleep() isn't.


    If I comment out the fcntl call to make the descriptor blocking once more, I observe
    Code:
    $ strace -e trace=file,signal ./a.out 
    execve("./a.out", ["./a.out"], [/* 65 vars */]) = 0
    access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
    access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
    open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
    access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
    open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
    rt_sigaction(SIGINT, {0x400796, [INT], SA_RESTORER|SA_RESTART, 0x7fab6da334b0}, {SIG_DFL, [], 0}, 8) = 0
    hello
    Read 6 bytes=hello
    
    wor  --- SIGINT {si_signo=SIGINT, si_code=SI_USER, si_pid=4036, si_uid=1000} ---
    INT!!!
    rt_sigreturn({mask=[]})                 = 0
    
    Read 6 bytes=wor  
    
    
    Terminated
    Because there is SA_RESTART, the read() call is NOT abandoned and errno set to 4, so the program still hangs around for the read() to complete it's transaction, and return a meaningful buffer to main(). But since the signal hander happened, the global flag is set and the loop exits as per normal.


    With the sigaction code, there is no SA_RESTART flag!
    Code:
    $ gcc foo.c
    $ strace -e trace=file,signal ./a.out 
    execve("./a.out", ["./a.out"], [/* 65 vars */]) = 0
    access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
    access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
    open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
    access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
    open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
    rt_sigaction(SIGINT, {0x400756, [], SA_RESTORER, 0x7fa5660f74b0}, {SIG_DFL, [], 0}, 8) = 0
    hello
    Read 6 bytes=hello
    
    wor  --- SIGINT {si_signo=SIGINT, si_code=SI_USER, si_pid=4081, si_uid=1000} ---
    INT!!!
    rt_sigreturn({mask=[]})                 = -1 EINTR (Interrupted system call)
    Read errno=4, msg=Interrupted system call
    
    Terminated
    In this case, it's the read() call itself which returns immediately with Read errno=4.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  13. #13
    Registered User
    Join Date
    Mar 2017
    Posts
    6
    I will use the sigaction code to terminate.
    My program counts the fllps and flops of a rain gauge, generated by a magnetic switch, to determine rainfall amounts.
    Each flip and flop triggers the left button of a cannibalized USB mouse.

    Since its the dry season here in southern Florida, I may not get a flip or flop for many days.
    If I need to terminate the program for a reboot or something, I can't wait for .3 millimeters of rain to fall before the program ends gracefully.
    Thanks all for the great advice and even greater examples.
    Last edited by metryc; 03-09-2017 at 03:09 PM.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Daemon Process
    By DeanWinchester in forum C Programming
    Replies: 2
    Last Post: 05-29-2012, 09:40 AM
  2. Daemon Process
    By DeanWinchester in forum C Programming
    Replies: 1
    Last Post: 05-25-2012, 03:54 AM
  3. Daemon process debugger
    By karthigayan in forum C Programming
    Replies: 2
    Last Post: 03-31-2009, 12:28 AM
  4. Replies: 3
    Last Post: 02-20-2009, 12:46 AM
  5. process termination
    By George2 in forum Windows Programming
    Replies: 1
    Last Post: 07-16-2008, 12:48 AM

Tags for this Thread