/*
 * $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 "continuity.h"
#include "build.h"

#if HAVE_PRCTL
#include <sys/prctl.h>
#endif

/* 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 forever(void);

static int test_build(void) {
	cTval *v;

	CVNEWSTRCPY(v,"foo");
}

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)
{
    exit(0);
    /* logFmsgid(0, "CON", 1); */
    logFmsg(3, "intr_handler called: rcv'd sigraised %d", sigraised);
    signal(SIGINT, intr_handler);
    return;
}

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

static void forever(void) {
    while(1) {
        thrFsleep(99999);
    }
}

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 *)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, t = 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 't':
                if (t==1) {
                    err++;
                } else {
                    t++;
                }
                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();
    }
    
    if (t==1) {
        exit(test_build());
    }
    
    /* 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();

    /* 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 */
    forever();

  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");
    printf("Built on: %s\n", CONT_DATE);
    printf("Hostname: %s\n", CONT_HOST);
    printf("%s\n\n", CONT_UNAME);
    printf("CFLAGS: %s\n", CONT_CFLAGS);
    printf("LDSHARECMD: %s\n", CONT_LDSHARE);
    printf("LIBS: %s\n\n", CONT_LIBS);
    printf("PRE_BUILD: %s\n", CONT_PREBUILD);
    printf("POST_BUILD: %s\n", CONT_POSTBUILD);

    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");
  netFconn_writef(conn,"Built on: %s\r\n", CONT_DATE);
  netFconn_writef(conn,"Hostname: %s\r\n", CONT_HOST);
    netFconn_writef(conn,"%s\r\n\r\n", CONT_UNAME);
   netFconn_writef(conn,"CFLAGS: %s\r\n", CONT_CFLAGS);
   netFconn_writef(conn,"LDSHARECMD: %s\r\n", CONT_LDSHARE);
   netFconn_writef(conn,"LIBS: %s\r\n\r\n", CONT_LIBS);
   netFconn_writef(conn,"PRE_BUILD: %s\r\n", CONT_PREBUILD);
   netFconn_writef(conn,"POST_BUILD: %s\r\n", CONT_POSTBUILD);

  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);
    }

#if HAVE_PRCTL
    /* if we have the ability to force coredumps -- let's do it */
    prctl (PR_SET_DUMPABLE, 1);
#endif

    return STATUS_PROCEED;
}

