/*
 * $Header: /san01/cvs/ashpool/csrc/continuity/cont/continuity.c,v 1.42 2004/05/11 02:53:17 aleigh Exp $
 */

/*
 * Copyright (c) 2001, 2004 Alex Leigh, All Rights Reserved
 *
 * THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF ALEX LEIGH The copyright
 * notice above does not evidence any actual or intended publication of such
 * source code.
 *
 * Use, disclosure, copying, distribution, or transmission of this code without
 * express permission is prohibited.
 */

#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <errno.h>
#include <signal.h>
#include <uuid/uuid.h>

#include "continuity.h"

/* Continuity's birthday is 12.05.2001 */

/*
 * When the startup is finished, this conditional will receive a broadcast.
 */
static pthread_cond_t startup_conditional = PTHREAD_COND_INITIALIZER;
static int started = 0;

static xmlTconfig *global_config = NULL;
static const xmlTtag *base_config_tag = NULL;
static hshTvoid_list *socket_stash;

static void print_version(void);

static int test_build(void);

static void print_usage(void);

static void conFdaemonize(void) {
    fclose(stdout);
    fclose(stdin);
    fclose(stderr);

    if (fork() != 0) {
        exit(0);
    }

    setsid();

    if (fork() != 0) {
        exit(0);
    }
}

const xmlTtag *conFget_config_basetag(void) {
    assert(base_config_tag != NULL);
    return base_config_tag;
}

/*
 * Do not go off the reservation. If you go off the reservation, I will not
 * come after you.
 */
void intr_handler(int sigraised) {
    logFmsg(3, "intr_handler called: rcv'd sigraised %d", sigraised);
    exit(0);
}

const char *conFget_build(void) {
    return BUILD_STRING;
}

static void print_usage(void) {
    printf(BUILD_STRING);
    printf("\n");

    printf("Copyright (c) 2001, 2004 Alex Leigh.\n");
    printf("Contains contributions by Eric Lindvall.\n\n");

    printf("Usage: \tcontinuity -c module.cfg [-o logfile [-r n]] [-u username]\n");
    printf("\tcontinuity -v\n\n");
    printf("\t-c\tPath to the module configuration file.\n");
    printf("\t-o\tOutput messages to a file. Server will run in the background.\n");
    printf("\t-r\tRotate message files and do not use more than n MB total disk.\n");
    printf("\t-u\tUser to run as.\n");
    printf("\t-v\tPrint version information.\n");
}

int conFsocketstash_update(int sd, int idx) {
    hshFvoid_int_update(socket_stash, idx, (void *)(uintptr_t)sd);
    return 0;
}

int conFsocketstash_find(int idx) {
    int s;

    s = (int) hshFvoid_int_find(socket_stash, idx);
    if (s == 0) return -1;
    return s;
}

int main(int argc, char **argv) {
    extern char *optarg;
    int ret;
    int c = 0, a = 0, v = 0, o = 0, r = 0, err = 0;
    char *c_arg = NULL, *a_arg = NULL, *o_arg = NULL, *r_arg = NULL;

    signal(SIGINT, intr_handler);
    signal(SIGPIPE, SIG_IGN);

    socket_stash = hshFvoid_int_init(NULL);

    logFinit();

    while ((ret = getopt(argc, argv, "tvc:u:a:o:r:")) != EOF) {
        switch (ret) {
            case 'c':
                if (c == 1) {
                    err++;
                } else {
                    c++;
                }
                c_arg = optarg;
                break;

            case 'a':
                if (a == 1) {
                    err++;
                } else {
                    a++;
                }
                a_arg = optarg;
                break;

            case 'v':
                if (v == 1) {
                    err++;
                } else {
                    v++;
                }
                break;

            case 'o':
                if (o == 1) {
                    err++;
                } else {
                    o++;
                    o_arg = optarg;
                }
                break;
            case 'r':
                if (r == 1) {
                    err++;
                } else {
                    r++;
                    r_arg = optarg;
                }
                break;

            default:
                err++;
        }
    }

    if (v == 1) {
        print_version();
    }

    /* no -c */
    if (c == 0) {
        err++;
    }

    /* -r without -o */
    if (r == 1 && o == 0) {
        err++;
    }

    if (err != 0) {
        print_usage();
        exit(5);
    }

    if (r_arg != NULL) {
        logFset_rotatesize(atoi(r_arg) * 1048576);
    }

    if (o_arg != NULL) {
        if (getenv("__CONT_NODAEMON") == NULL) {
            conFdaemonize();
        }

        if (logFset_outputfile(o_arg) == -1) {
            perror(o_arg);
            exit(1);
        }
    }

    thrFmod_init();

    /* Bring the statistics interface online */
    tasFinit();
    tasFstat_counter_add("sys", 0, "startup", utlFtime());

    logFmsg(0, BUILD_STRING);
    logFmsg(0,
            "Copyright (c) 2001, 2004 Alex Leigh.");
    logFmsg(0, "Contains contributions by Eric Lindvall.");
    logFmsg(0, "Use is subject to license terms.");

    global_config = xmlFload_file(c_arg);
    if (global_config == NULL) {
        logFmsg(2, "Could not load configuration file. [f: %s] [e: %d]",
                c_arg, errno);
        exit(0);
    }
    base_config_tag = xmlFbase_tag(global_config);

    assert(base_config_tag != NULL);

    base_config_tag =
            xmlFfind_first_tag_children(base_config_tag, "configuration");

    rndFinit();

    /* Initialize TLS memory system. */
    tlsFinit();

    dlFinit();

    /* Load the module.cfg */
    if (dlFload_config() != STATUS_OK) {
        logFmsgid(2, "CON", 2);
        exit(5);
    }

    /* Initialize mmap memory system -- this creates a thread */
    mmcFinit();

    started = 1;

    pthread_cond_broadcast(&startup_conditional);

    /* Never exit */
    pthread_exit(NULL);

    return 0;
}


/*
 * This function returns when the init functions have all been executed.
 */
void conFwait_for_init(void) {

#ifdef LINUX
    while (1) {
        if (started == 1)
            return;
        thrFsleep(500);
    }
#else
    pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER;

    if (started == 1)
        return;

    pthread_cond_wait(&startup_conditional, &mut);
#endif
}

static void print_version(void) {

    printf("%s\n", BUILD_STRING);
    printf("Copyright (c) 2001, 2004 Alex Leigh.\n");
    printf
            ("Contains contributions by Eric Lindvall.\nUse is subject to license terms.\n\n");


    exit(0);
}

int op_show_version(netTconn *conn) {
    netFconn_writef(conn, "%s\r\n", BUILD_STRING);
    netFconn_writef(conn, "Copyright (c) 2001, 2004 Alex Leigh\r\n");
    netFconn_writef(conn, "Contains contributions by Eric Lindvall\r\n");

    return STATUS_OK;
}

int set_userid(MECHA_UNUSED_ARGUMENT void *p, lstTset *opts) {
    const char *u_arg = lstFset_get(opts, "user");
    int uid, gid;

    if (u_arg == NULL) return STATUS_ERROR;

    uid = utlFgetuid((const char *) u_arg);
    gid = utlFgetgid(u_arg);

    if (gid != -1) {
        if (setregid(gid, gid) != 0) {
            logFmsg(2, "Unable to change GID to %d. Failing.",
                    gid);
            exit(5);
        } else {
            logFmsg(0, "Changed GID to %d.", gid);
        }
    } else {
        logFmsg(2, "No such user: %s.", u_arg);
        exit(5);
    }

    if (uid != -1) {
        if (setreuid(uid, uid) != 0) {
            logFmsg(2, "Unable to change UID to %s(%d). Failing.",
                    u_arg, uid);
            exit(5);
        } else {
            logFmsg(0, "Changed UID to %s(%d).", u_arg, uid);
        }
    } else {
        logFmsg(2, "No such user: %s.", u_arg);
        exit(5);
    }

    return STATUS_PROCEED;
}

