Writing a daemon using c/c++

Discussion in 'Mac Programming' started by Stylerxxx, Sep 20, 2018.

  1. Stylerxxx, Sep 20, 2018
    Last edited: Sep 20, 2018

    Stylerxxx macrumors newbie

    Stylerxxx

    Joined:
    Sep 20, 2018
    #1
    Hey guys,
    Has anyone had an experience with porting linux daemon code to macos? I'm getting "no path for address" error in system log when the second thread start running its function loop. The daemon does not crash and even terminates properly when I kill it, but the code in the thread does not go any further. Could you advise something?

    OS = Sierra 10.12.6
    gcc/g++ --version = Apple LLVM version 9.0.0 (clang-900.0.39.2)

    Here's the code I'm trying to make alive

    Code:
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <errno.h>
    #include <unistd.h>
    #include <signal.h>
    #include <syslog.h>
    #include <pthread.h>
    
    static bool g_active = false;
    
    void* daemon_main(void* /* ptr */) {
        unsigned int counter = 0;
    
        while (g_active) {
            /* !!! THE PROBLEM IS HERE !!!
             *
             * All function calls in this loop do not work,
             * but it still can quit from it, when g_active == false.
             * If I remove the loop from here, the thread will be terminated normally */
    
            syslog(LOG_INFO, "Counter = %d", counter++);
            sleep(2);
        }
        return nullptr;
    }
    
    void signal_handler(int signal) {
        syslog(LOG_NOTICE, "SIGTERM signal has been received. Start quitting...");
    
        // quit from the daemon main loop
        g_active = false;
    }
    
    int main(int argc, char** argv) {
        // our process_id and session_id
        pid_t pid, sid;
    
        // fork off the parent process
        pid = fork();
        if (pid < 0) {
            printf("Could not fork the PID\n");
            exit(EXIT_FAILURE);
        }
    
        // if we got a good pid, then we can exit the parent process
        if (pid > 0) {
            exit(EXIT_SUCCESS);
        }
    
        // change the file mode mask
        umask(0);
    
        // open any logs here
        openlog("foo", LOG_NOWAIT | LOG_PID, LOG_USER);
        syslog(LOG_NOTICE, "Logger started");
    
        // create a new sid for the child process
        sid = setsid();
        if (sid < 0) {
            syslog(LOG_ERR, "Could not generate session ID for child process");
            closelog();
            exit(EXIT_FAILURE);
        }
    
        // change the current working directory
        if ((chdir("/")) < 0) {
            syslog(LOG_ERR, "Could not change working directory to /");
            closelog();
            exit(EXIT_FAILURE);
        }
    
        // catch and handle signals
        if (signal(SIGTERM, signal_handler) == SIG_ERR) {
            syslog(LOG_ERR, "Could not register SIGTERM catcher");
            closelog();
            exit(EXIT_FAILURE);
        }
    
        // close out the standard file descriptors
        close(STDIN_FILENO);
        close(STDOUT_FILENO);
        close(STDERR_FILENO);
    
        // daemon-specific initialization goes here
    
        pthread_t secondThread;
        g_active = true;
    
        int ires = pthread_create(&secondThread, NULL, daemon_main, NULL);
        if (ires != 0) {
            syslog(LOG_ERR, "Could not create a second thread. Error <%d>", ires);
            closelog();
            exit(EXIT_FAILURE);
        }
    
        syslog(LOG_NOTICE, "Initialization completed");
    
        pthread_join(secondThread, NULL);
    
        // close system log for the child process
        syslog(LOG_NOTICE, "Successfully stopped");
        closelog();
    
        // terminate the child process when the daemon completes
        exit(EXIT_SUCCESS);
    }
    
     
  2. Mikael H macrumors 6502a

    Joined:
    Sep 3, 2014
    #2
    static bool g_active = false;
    while (g_active) {...}


    I'm not a developer, but isn't the symptom you describe very much in accordance with the code?
     
  3. Stylerxxx thread starter macrumors newbie

    Stylerxxx

    Joined:
    Sep 20, 2018
    #3
    nope, I set it back to true before running the second thread
     
  4. Mikael H macrumors 6502a

    Joined:
    Sep 3, 2014
    #4
    My bad, I didn't notice the code continues past the visible block.. *facepalm*
     
  5. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    Location:
    betwixt
    #5
    Try putting something other than a syslog() in the thread's main loop. If that works, then at least you know the problem lies in the syslog family of functions. You could see if there are alternatives for them.


    Given the fork() and the pthread_join() on the main thread, it doesn't seem like you're following the guidelines for Mac OS daemons, which is to make them compatible with launchd.
    https://developer.apple.com/library...stemStartup/Chapters/CreatingLaunchdJobs.html

    Many of the guidelines are under the heading "Behavior for Processes Managed by launchd". The use of fork() followed by exit() is not only not required, it actively subverts launchd. "Do not change working directory" and "Do not call setsid" are a couple of recommendations.

    Another one, though not listed, would be Occam's Thread Razor: "Threads should not be multiplied beyond necessity". If the only thing your main thread is doing is waiting for a non-main thread to terminate, then you can probably just do what's on the secondary thread in the main thread.

    If you really need multiple actions possibly running asynchronously, you should look at using dispatch queues:
    https://developer.apple.com/documentation/DISPATCH
     
  6. Stylerxxx thread starter macrumors newbie

    Stylerxxx

    Joined:
    Sep 20, 2018
    #6
    I managed to make it work. For some reason syslog(LOG_INFO) does not print out messages to the system log, but with LOG_NOTICE it works fine. Apparently "no path for address" appears because I don't have a description of the daemon or stuff like that, I just run it as a regular executable. Gonna read carefully the docs (thanks @chown33). Well, at least it works now as expected =).
     
  7. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    Location:
    betwixt
    #7
    Glad you got it working.

    The LOG_NOTICE and LOG_INFO are different levels of detail. NOTICE is level 5, INFO is level 6. Only the log entries below a particular level are shown in the log. You can change the level to see more or less detail. See the man page for syslog(1).
     
  8. okieiam macrumors member

    okieiam

    Joined:
    Dec 17, 2016
    #8
    I am not a novice programmer but clang c++ thread my not fully implemented as GNU c11++
     
  9. Stylerxxx thread starter macrumors newbie

    Stylerxxx

    Joined:
    Sep 20, 2018
    #9
    I tend to think that this is a feature of BSD, since I got the same result on FreeBSD 11. Threads by the way work correctly in my tests.
     

Share This Page

8 September 20, 2018