/* $Cambridge: hermes/src/prayer/servers/session_main.c,v 1.3 2008/09/16 09:59:57 dpc22 Exp $ */
/************************************************
 *    Prayer - a Webmail Interface              *
 ************************************************/

/* Copyright (c) University of Cambridge 2000 - 2008 */
/* See the file NOTICE for conditions of use and distribution. */

#include "server.h"

BOOL session_main_exec(struct config * config, int argc, char *argv[])
{
    struct pool *p = pool_create(8192);
    char *cmd;
    char **nargv = pool_alloc(p, (argc + 2) * sizeof(char *));
    int i;

    cmd = pool_printf(p, "%s/prayer-session", config->bin_dir);

    for (i = 0; i < argc; i++)
        nargv[i] = argv[i];
    nargv[argc++] = "--no-exec";
    nargv[argc] = NIL;

    execv(cmd, nargv);
    log_fatal("execv(%s) failed: %s", cmd, strerror(errno));
    /* NOTREACHED */
    return (NIL);
}

/* ====================================================================== */

static BOOL session_main_write_pid(struct config *config)
{
    char *name = pool_printf(NIL, "%s/prayer-session.pid", config->pid_dir);
    FILE *file;

    if ((file = fopen(name, "w")) == NIL)
        return (NIL);

    fprintf(file, "%lu\n", (unsigned long) getpid());
    fclose(file);

    return (T);
}

/* ====================================================================== */

int main(int argc, char *argv[])
{
    BOOL want_foreground = NIL;
    BOOL no_exec = NIL;
    char *config_filename = PRAYER_CONFIG_FILE;
    struct config *config = config_create();
    int i;
    extern char **environ;
    struct ssl_config ssl_config;

    /* mm_init() must be first thing that we do! */
    mm_init();

    /* Disable supplementary groups, look for temporary unprivileged group */
    if (getuid() == 0) {
        struct group *group;

        setgroups(0, NIL);

        if ((group = getgrnam("other")) != NIL)
            setgid(group->gr_gid);
        else if ((group = getgrnam("nobody")) != NIL)
            setgid(group->gr_gid);
    }

    if (getenv("PRAYER_CONFIG_FILE"))
        config_filename = getenv("PRAYER_CONFIG_FILE");

    initsetproctitle("prayer-session", argc, argv, environ);

    response_record_version(VERSION_PRAYER);

    os_signal_init();

    for (i = 1; i < argc; i++) {
        if (!strcmp(argv[i], "--help")) {
            fprintf(stderr, "Options:\n");
            fprintf(stderr,
                    ("  --config-file:     Define prayer config file\n"));
            fprintf(stderr,
                    ("                    "
                     "(Overrides compilation default and PRAYER_CONFIG_FILE)\n"));
            fprintf(stderr,
                    ("  --config-option:   Override single configuration option\n"));
            fprintf(stderr, "\n");
            fprintf(stderr, ("  --debug:           Enable debugging\n"));
            fprintf(stderr,
                    ("  --foreground:      "
                     "Run single threaded session server in foreground\n"));
            fprintf(stderr,
                    ("  --help:            Show this list of options\n"));
            exit(0);

        } else if (!strcmp(argv[i], "--foreground"))
            want_foreground = T;
        else if (!strcmp(argv[i], "--no-exec"))
            no_exec = T;
        else if (!strncmp(argv[i],
                          "--config-file=", strlen("--config-file="))) {
            config_filename = strdup(argv[i] + strlen("--config-file="));
        } else if (!strcmp(argv[i], "--config-option")) {
            i++;                /* Processes next argv */
        } else {
            fprintf(stderr, "Unknown option: %s\n", argv[i]);
            exit(1);
        }
    }

    config = config_create();
    if (!config_parse_file(config, config_filename))
        exit(1);

    for (i = 1; i < argc; i++) {
        if (!strcmp(argv[i], "--config-option")) {
            if (++i < argc) {
                if (!config_parse_option(config, strdup(argv[i])))
                    exit(1);
            } else
                fprintf(stderr, "--config processes following option");
        }
    }

    if (!config_check(config))
        exit(1);

    if (!config_expand(config))
        exit(1);

    /* Generate a clean copy of the configuration without all of the noise */
    {
        struct config *config_clean = config_clone_parsed(config);

        config_free(config);
        config = config_clean;
    }

    /* Shouldn't be relevant now that prayer-session started from prayer */
    /* Don't need root privs after ports bound, files open */
    if (getuid() == 0) {
        if ((config->prayer_uid == 0) || (config->prayer_gid == 0))
            log_fatal("Configured to run as root!");

        if (config->prayer_gid) {
            setgid(config->prayer_gid);

            if (config->prayer_user && config->prayer_user[0])
                initgroups(config->prayer_user, config->prayer_gid);
        }

        if (config->prayer_uid)
            setuid(config->prayer_uid);

        if (getuid() == 0)
            log_fatal("Failed to lose root priveledges");       /* Stop impossible loop */

        /* Reexec after losing root privs to prevent core dump paranoia */
        if (no_exec == NIL) {
            session_main_exec(config, argc, argv);
            log_fatal("session_main_exec() failed");    /* Should be redundant */
        }
    }

    if (getuid() == 0)
        log_fatal("Configured to run as root!");

    config_mkdirs(config);
    log_misc_init(config, argv[0], "prayer_session");

    if (config->limit_vm)
        os_limit_vm(config->limit_vm);

    config_extract_ssl(config, &ssl_config);
    iostream_init(&ssl_config); /* Required for SSL stuff */
    cmd_dispatch_init();        /* Initialise cmd_dispatch table */

    if (!session_config_local_domain_open(config))
        exit(1);

    if (config->tmp_dir)
        chdir(config->tmp_dir);

    session_main_write_pid(config);

    session_server(config, want_foreground);

    return (0);
}
