/*
    デーモン作法スケルトン
    by J.r0ck
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>

#define WAIT_TIME    10
#define LOGFILENAME  "/tmp/test.log"

/*
    デーモン初期化
*/
int init_deamon(int debug_mode)
{

    /* デバッグの場合は、デーモン化しない */
    if(debug_mode) {
        fprintf(stdout, "-- Debug mode --\n");
        return 0;
    }

    switch(fork()) {
        case -1:
            /* fork失敗 */
            perror("fork");
            return errno;
        case 0:
            /* ★作法１．子プロセスをターミナルから切り離す */
            setsid();

            /* System V系セッションリーダーでなければ制御端末を再取得
               できないのでこれを保証するためには更にforkが必要 */
            //if(fork() == -1) {
            //    fork失敗
            //    perror("fork");
            //    return errno;
            //}
            //こんな感じ？

            break;
        default:
            /* 親プロセスを終了する */
            exit(0);
    }

    /* ★作法２．ファイルシステムがumountできるような配慮 */
    /* システム管理者と要打ち合わせ */
    chdir("/");

    /* ★作法３．STDIN、STDOUT、STDERRを閉じておく */
    /* 標準出力の取り合いにならないための作法 */
    close(0);
    close(1);
    close(2);

    /* ★作法４．すべてのファイルディスクリプタを/dev/nullへ*/
    /* 標準出力の取り合いにならないための作法 */
    open("/dev/null", O_RDWR);
    dup2(0, 1);
    dup2(0, 2);

    /* ★作法５．不要なシグナルへの対処 */
    signal(SIGALRM, SIG_IGN);
    signal(SIGCHLD, SIG_IGN);    //ゾンビ禁止 BSD系では更に処理が必要
    signal(SIGHUP, SIG_IGN);
    signal(SIGPIPE, SIG_IGN);
    signal(SIGTERM, SIG_IGN);

    /* ★作法６．親のmaskを引き継がない */
    umask(0);

    return 0;
}

/*
    イベント待ち
*/
int wait_event(int debug_mode)
{
    FILE    *fp = NULL;
    char    buf[256];
    int     ret_code = 0;

    while(1) {
        /* 待ち */
        sleep(WAIT_TIME);

        /* 処理：ここではファイルに1行出力 */
        if((fp = fopen(LOGFILENAME, "a")) == NULL) {
            ret_code = errno;
            perror("fopen");
            break;
        }

        sprintf(buf, "%d - processed\n", time(NULL));
        fputs(buf, fp);
        if(fclose(fp) == EOF) {
            ret_code = errno;
            perror("fclose");
            break;
        }
        if(debug_mode) {
            printf(buf);
        }
        /* --->ここまで処理 */
    }
    return ret_code;
}

/*
    メイン
*/
int main(int argc, char *argv[])
{
    int debug_mode = 0;
    int ret_code = 0;

    /* デバッグモードオプションチェック */
    if(argc >= 2) {
        if(strcmp(argv[1], "-d") == 0) {
            debug_mode = 1;
        }
        else {
            puts("usage: testd [-d]");
            puts("      -d ... debug_mode");
            return 1;
        }
    }

    /* デーモン初期化 */
    if((ret_code = init_deamon(debug_mode)) != 0) {
        fprintf(stderr, "init deamon failed code = %d\n", ret_code);
        return 2;
    }

    /* イベント待ち */
    if((ret_code = wait_event(debug_mode)) != 0) {
        fprintf(stderr, "wait event failed code = %d\n", ret_code);
        return 3;
    }

    return 0;
}
