request for added feature be commited: duende pid file

Yarin yarin at warpmail.net
Mon Dec 27 16:22:42 EST 2010


Mr. Trenholme,

I noticed that your duende daemon doesn't save itself a pid file, which makes handling it harder.
Specifically, the service scripts included in your latest release, have to do some really hackish things to shut down the daemon. :-(

To fix this, I've added a PID file feature to the duende. I figured, all good daemons have it, why doesn't yours? :-)
I've successfully compiled and tested the code, and am using it myself, so it's ready to go.

Basically, I just added a some chunks near the start of the main() routine, but modified it a hair to accommodate the changes.
Besides that, I replaced all of the "argv[1]" occurrences with "argv[exec_argv_offset]" beyond the first for() loop in main().
(Note: I also preserved your formatting style is the new code)

Below is the proposed new code for the main() function of the file ./tools/duende.c:


int main(int argc, char **argv) {
    int exit_status;
    pid_t pid, log_pid;
    int stream1[2]; /* Used for piping */
    int exec_argv_offset = 1; /* Also used to determine PID writing */
    if(argv[0] == NULL || argv[1] == NULL) {
        printf("Usage: duende (--pid=/path/to/file) [program] [arguments]\n");
        exit(1);
        }
    if(!strncasecmp(argv[1],"--pid=",6)) {
        if(argv[2] == NULL) {
            printf("Usage: duende (--pid=/path/to/file) [program] [arguments]\n");
            exit(1);
            }
        exec_argv_offset = 2;
        }

    /* Let children know that duende is running */
    if(setenv("DUENDE_IS_RUNNING","1",0) != 0) {
        printf("FATAL: Unable to set environment variable\n");
        exit(1);
        }

    /* The parent immediately exits */
    if(fork() != 0)
        exit(0);

    /* The child becomes a full-fledged daemon */
    setpgid(0,0); /* No longer visible in 'ps' without the 'auxw' argument */

    /* Write our PID to a file if the user so desires us to */
    if(exec_argv_offset == 2) {
        FILE *fp_pid = fopen(argv[1] + 6,"w");
        if(!fp_pid) {
            syslog(LOG_ALERT,"Fatal writing, to PID file, error\n");
            exit(1);
            }
        unsigned int local_pid = getpid();
        fprintf(fp_pid,"%u",local_pid);
        fclose(fp_pid);
        }

    /* Sysadmins expect HUP to reload, so we set that up */
    signal(SIGHUP,handle_hup);
    signal(SIGTERM,handle_term);
    signal(SIGINT,handle_term);

    pid = 0; log_pid = 0;

    for(;;) {
        if(pipe(stream1) != 0) {
            syslog(LOG_ALERT,"Fatal pipe error");
            exit(3);
            }
        pid = fork();
        if(pid == -1) {
            syslog(LOG_ALERT,"Fatal pid error");
            exit(1);
            }
        if(pid == 0) { /* Child; this one execs maradns */
            close(stream1[0]);
            /* Dup the standard output */
            if(dup2(stream1[1],1) != 1) {
                syslog(LOG_ALERT,"Fatal dup2 error 1");
                exit(4);
                }
            /* And the standard error */
            if(dup2(stream1[1],2) != 2) {
                syslog(LOG_ALERT,"Fatal dup2 error 2");
                exit(5);
                }
            argv[0] = argv[exec_argv_offset];
            execvp(argv[exec_argv_offset],argv + exec_argv_offset);
            /* OK, not found */
            printf("duende: %s: Command can't run, terminating\n",argv[exec_argv_offset]);
            syslog(LOG_ALERT,"Command can't run, terminating\n");
            exit(1);
            }

        /* Parent */
        close(stream1[1]);
        log_pid = fork();
        if(log_pid == 0) { /* Child to syslog all of MaraDNS' output */
            argv[0] = "duende-log-helper";
            log_helper(argv[exec_argv_offset],stream1[0]);
            syslog(LOG_ALERT,"log_helper finished, terminating\n");
            exit(1);
            }
        for(;;) {
            /* If we got a HUP signal, send it to the child */
            if(got_hup_signal == 1) {
                kill(pid,SIGHUP);
                got_hup_signal = 0;
                }
            /* If we got a TERM or INT signal, send it to the children
               then exit ourselves */
            else if(got_term_signal == 1) {
                /* XXX: make sure term really stops the children */
                kill(pid,SIGTERM);
                kill(log_pid,SIGTERM);
                syslog(LOG_ALERT,"got term signal, terminating\n");
                exit(0);
                }
            sleep(1);
            if(waitpid(pid,&exit_status,WNOHANG) == pid) { /* If child ended */
                handle_child_exited(exit_status,log_pid,pid);
                close(stream1[0]);
                break; /* Out of the inner loop; re-start Mara */
                }
            /* If logger terminated */
            if(waitpid(log_pid,&exit_status,WNOHANG) == log_pid) {
                handle_child_exited(exit_status,pid,log_pid);
                close(stream1[0]);
                break; /* Out of the inner loop; re-start Mara */
                }
            }
        }
    }


Thank you,
Yarin Licht



More information about the list mailing list