0

I'm writing daemon with ability to recover work process CentOS release 5.7 (Final).

Here is example of code:

#define CHILD_NEED_WORK                 1
#define CHILD_NEED_TERMINATE    2

int ReloadConfig()
{
    ....
    return 0;
}

void DestroyWorkThread()
{...}

int InitWorkThread()
{
     ...
    return 0;
}

int LoadConfig(char* FileName)
{
     ...
    return 0;
}

void SetPidFile(char* Filename)
{
    FILE* f;
    f = fopen(Filename, "w+");
    if (f)
    {
        fprintf(f, "%u\n", getpid());
        fclose(f);
    }
}

int SetFdLimit(int MaxFd)
{
    struct rlimit lim;
    int          status;

    lim.rlim_cur = MaxFd;
    lim.rlim_max = MaxFd;

    status = setrlimit(RLIMIT_NOFILE, &lim);

    return status;
}

//Monitor process
int MonitorProc()
{
    int      pid;
    int      status;
    int      need_start = 1;
    sigset_t sigset;
    siginfo_t siginfo;

    parent_pid = getpid();

    sigemptyset(&sigset);

    sigaddset(&sigset, SIGQUIT);

    sigaddset(&sigset, SIGINT);

    sigaddset(&sigset, SIGTERM);

    sigaddset(&sigset, SIGCHLD);

    sigaddset(&sigset, SIGUSR1);

    sigprocmask(SIG_BLOCK, &sigset, NULL);

    SetPidFile(PID_FILE);

    for (;;)
    {
        if (need_start)
        {
            pid = fork();
        }

        need_start = 1;

        if (pid == -1)
        {

        }
        else if (!pid) 
        {
            status = WorkProc();

            exit(status);
        }
        else
        {
            sigwaitinfo(&sigset, &siginfo);

            if (siginfo.si_signo == SIGCHLD)
            {
                wait(&status);

               status = WEXITSTATUS(status);

                 if (status == CHILD_NEED_TERMINATE)
                 {
                     Write("[MONITOR] Child stopped");
                     break;
                 }
                 else if (status == CHILD_NEED_WORK)
                 {
                     Write("[MONITOR] Child restart");
                 }
             }
             else if (siginfo.si_signo == SIGUSR1)
             {
                 kill(pid, SIGUSR1); 
                 need_start = 0;
             }
             else if (siginfo.si_signo == 0) 
             {
                need_start = 0;
                continue;
             }
             else
             {
                 Write("[MONITOR] Signal ", strsignal(siginfo.si_signo));
                 kill(pid, SIGTERM);
                 status = 0;
                 break;
             }
         }
     }

     Write("[MONITOR] Stop");

     unlink(PID_FILE);

     return status;
}

//Work process
int WorkProc()
{
    struct sigaction sigact;
    sigset_t         sigset;
    int             signo;
    int             status;

    sigact.sa_flags = SA_SIGINFO;

    sigact.sa_sigaction = signal_error_for_backtrace;

    sigemptyset(&sigact.sa_mask);


    sigaction(SIGFPE, &sigact, 0);
    sigaction(SIGILL, &sigact, 0);
    sigaction(SIGSEGV, &sigact, 0);
    sigaction(SIGBUS, &sigact, 0);

    sigemptyset(&sigset);

    sigaddset(&sigset, SIGQUIT);

    sigaddset(&sigset, SIGINT);

    sigaddset(&sigset, SIGTERM);

    sigaddset(&sigset, SIGUSR1);
    sigprocmask(SIG_BLOCK, &sigset, NULL);

    SetFdLimit(FD_LIMIT);


    status = InitWorkThread();


    if (!status)
    {
        for (;;)
        {
            sigwait(&sigset, &signo);

            if (signo == SIGUSR1)
            {
                status = ReloadConfig();
                if (status)
                {
                    Write("[DAEMON] Reload config failed");
                }
                else
                {
                    Write("[DAEMON] Reload config OK");
                }
            }
            else
            {
                break;
            }
        }

        DestroyWorkThread();
    }
    else
    {
        Write("[DAEMON] Create work thread failed");
    }

    Write("[DAEMON] Stopped");


    return CHILD_NEED_TERMINATE;
}


int main(int argc , char *argv[])
{

    if (argc != 2)
    {
        printf("Usage: ./test_daemon.conf failed!\n");
        return -1;
    }

    status = LoadConfig(argv[1]);
    if (status) 
    {
        printf("Error: Load config failed\n");
        return -1;
    }

    if (CheckForAnotherInstance())
    {
    printf("Daemon is already running!\n");
    return 1;
    }

    pid = fork();
    if (pid == -1)
    {
        printf("Error: Start Daemon failed (%s)\n", strerror(errno));
        return -1;
    }
    else if (!pid)
    {
        umask(0);
        setsid();

        close(STDIN_FILENO);
        close(STDOUT_FILENO);
        close(STDERR_FILENO);
        //Monitor process startup
        status = MonitorProc();
        return status;
    }
    else
    {
        return 0;
    }
    return 0;
}

I use two processes: work process, which produces main work and monitor process, which waits for signals from work process, and restarts them, if it receives required signal. When i try to send a signal to parent process - monitor process - with command kill -s SIGCHLD, it receives this signal.

When i try to terminate child process, parent process doesn't receive SIGCHLD signal- it contunies to wait for signals, and child process transforms to zombie.

But when i use utility strace with parent process, all works fine - child process terminates successfully, and parent process receives SIGCHLD signal.

I read about function waitpid(), which uses to receive SIGCHLD signal, but i want to receive another signals in parent process too.

Any ideas?

icodebuster
  • 8,780
  • 7
  • 59
  • 63
  • If you're trying to self-daemonize, see this post for how to do it properly: http://stackoverflow.com/questions/17954432/creating-a-daemon-in-linux – kfsone May 28 '15 at 01:16

2 Answers2

0

My guess , signal handler is not installed before first fork?

Anand Rathi
  • 746
  • 4
  • 11
0

You SIG_BLOCK the SIGCHLD so will no receive any signals. But this is ok as you go on to use sigwaitinfo() but you fail to use siginfo.si_pid when you do a wait(), you should use waitpid() for the PID you are cleaning up due to receiving the signal synchronously via sigwaitinfo().

You use WEXITSTATUS() without checking WIFEXITED(status) first. See wait() man page.

Your monitor and work process appear to use the same executable as you do a fork() with out exec() after. So be careful as you may been to restore the signal handler state in the child to get the code in the child to behave normally. For example the monitor process is the parent? So to get a child it does a fork() and then calls into WorkProc(). Inside WorkProc() it proceeds to block a bunch of signals (but not SIGCLD). However the execution is the sigprocmask(SIG_BLOCK, ...) from MonitorProc() will still be active inside WorkProc().

I am not sure what "if (siginfo.si_signo == 0) " is all about.

So to another your main query the reason why SIGCHLD is not being delivered from the process running WorkProc() function, is because you blocked that signal already inside MonitorProc(). So fix this issue use the 3rd argument to sigprocmask() to save the original block/unblock mask in MonitorProc() and when you fork() and before jumping into WorkProc() restore the block/unblock mask.

Darryl Miles
  • 4,456
  • 1
  • 19
  • 20