/*
 * tnmInit.c --
 *
 *	This is the initialization of the Tnm Tcl extension with
 *	commands to retrieve management information from TCP/IP
 *	networks.
 *
 * Copyright (c) 1993-1996 Technical University of Braunschweig.
 * Copyright (c) 1996-1997 University of Twente.
 *
 * See the file "license.terms" for information on usage and redistribution
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
 */

#include "tnmInt.h"
#include "tnmPort.h"

/*
 * Forward declarations for procedures defined later in this file:
 */

static void
InitVars		_ANSI_ARGS_((Tcl_Interp *interp));

static int
SourceRcFile		_ANSI_ARGS_((Tcl_Interp *interp, char *fileName));

static int
SourceInitFiles		_ANSI_ARGS_((Tcl_Interp *interp));

static int
InitCmds		_ANSI_ARGS_((Tcl_Interp *interp, int safe));

/*
 * The following global variable is used to hold the 
 * startup time stamp.
 */

Tcl_Time tnmStartTime;

/*
 * The following structure defines the commands in the Tnm extension.
 */

typedef struct {
    char *name;			/* Name of the command. */
    Tcl_CmdProc *proc;		/* String-based procedure for command. */
    Tcl_ObjCmdProc *objProc;	/* Object-based procedure for command. */
    int isSafe;			/* If non-zero, command will be present
                                 * in safe interpreter. Otherwise it will
                                 * be hidden. */
} CmdInfo;

static CmdInfo tnmCmds[] = {
    { "Tnm::dns",	Tnm_DnsCmd,		(Tcl_ObjCmdProc *) NULL, 0 },
    { "Tnm::http",	Tnm_HttpCmd,		(Tcl_ObjCmdProc *) NULL, 0 },
    { "Tnm::icmp",	Tnm_IcmpCmd,		(Tcl_ObjCmdProc *) NULL, 0 },
    { "Tnm::ined",	Tnm_InedCmd,		(Tcl_ObjCmdProc *) NULL, 0 },
    { "Tnm::job",	(Tcl_CmdProc *) NULL,	Tnm_JobObjCmd,		 1 },
    { "Tnm::map",	(Tcl_CmdProc *) NULL,	Tnm_MapObjCmd,		 1 },
    { "Tnm::mib",	(Tcl_CmdProc *) NULL,	Tnm_MibObjCmd,		 0 },
    { "Tnm::netdb",	(Tcl_CmdProc *) NULL,	Tnm_NetdbObjCmd,	 0 },
    { "Tnm::ntp",	Tnm_NtpCmd,		(Tcl_ObjCmdProc *) NULL, 0 },
    { "Tnm::snmp",	Tnm_SnmpCmd,		(Tcl_ObjCmdProc *) NULL, 0 },
    { "Tnm::sunrpc",	Tnm_SunrpcCmd,		(Tcl_ObjCmdProc *) NULL, 0 },
    { "Tnm::syslog",	Tnm_SyslogCmd,		(Tcl_ObjCmdProc *) NULL, 0 },
    { "Tnm::udp",	Tnm_UdpCmd,		(Tcl_ObjCmdProc *) NULL, 0 },
    { (char *) NULL,	(Tcl_CmdProc *) NULL,	(Tcl_ObjCmdProc *) NULL, 0 }
};


/*
 *----------------------------------------------------------------------
 *
 * InitVars --
 *
 *	This procedure initializes all global Tcl variables that are 
 *	exported by the Tnm extension.
 *
 * Results:
 *	A standard Tcl result.
 *
 * Side effects:
 *	Tcl variables are created.
 *
 *----------------------------------------------------------------------
 */

static void
InitVars(interp)
    Tcl_Interp *interp;
{
    char *machine, *os, *vers, *user, *tmp, *p;
    char buffer[20];
    Tcl_DString arch;

    TnmInitPath(interp);

    Tcl_SetVar2(interp, "tnm", "version", TNM_VERSION, TCL_GLOBAL_ONLY);
    Tcl_SetVar2(interp, "tnm", "url", TNM_URL, TCL_GLOBAL_ONLY);

    /*
     * Get the startup time of the Tnm extension.
     */

    if (! tnmStartTime.sec && ! tnmStartTime.usec) {
	TnmGetTime(&tnmStartTime);
    }
    sprintf(buffer, "%ld", tnmStartTime.sec);
    Tcl_SetVar2(interp, "tnm", "start", buffer, TCL_GLOBAL_ONLY);

    /*
     * Check if the current version of the Tnm extension is still valid
     * or if it has expired. Note, this is only useful when distribution
     * demo or test versions. This check should be turned off on all
     * stable and final releases.
     */

#ifdef TNM_EXPIRE_TIME
    if (tnmStartTime.sec > TNM_EXPIRE_TIME) {
	panic("Tnm Tcl extension expired. Please upgrade to a newer version.");
    }
    sprintf(buffer, "%ld", TNM_EXPIRE_TIME);
    Tcl_SetVar2(interp, "tnm", "expire", buffer, TCL_GLOBAL_ONLY);
#endif

    /*
     * Set the host name. We are only interested in the name and not
     * in a fully qualified domain name. This makes the result
     * predictable and thus portable.
     */

    tmp = ckstrdup(Tcl_GetHostName());
    p = strchr(tmp, '.');
    if (p) *p = '\0';
    Tcl_SetVar2(interp, "tnm", "host", tmp, TCL_GLOBAL_ONLY);
    ckfree(tmp);

    /*
     * Get the user name. We try a sequence of different environment
     * variables in the hope to find something which works on all
     * systems.
     */

    user = getenv("USER");
    if (user == NULL) {
	user = getenv("USERNAME");
	if (user == NULL) {
	    user = getenv("LOGNAME");
	    if (user == NULL) {
		user = "unknown";
	    }
	}
    }
    Tcl_SetVar2(interp, "tnm", "user", user, TCL_GLOBAL_ONLY);

    /*
     * Search for a directory which allows to hold temporary files.
     * Save the directory name in the tnm(tmp) variable.
     */

    tmp = getenv("TEMP");
    if (! tmp) {
	tmp = getenv("TMP");
	if (! tmp) {
	    tmp = "/tmp";
	    if (access(tmp, W_OK) != 0) {
		tmp = ".";
	    }
	}
    }
    for (p = tmp; *p; p++) {
	if (*p == '\\') {
	    *p = '/';
	}
    }
    Tcl_SetVar2(interp, "tnm", "tmp", tmp, TCL_GLOBAL_ONLY);

    /*
     * Determine the architecture string which is used to store 
     * machine dependend files in the Tnm library area.
     */

    machine = Tcl_GetVar2(interp, "tcl_platform", "machine", TCL_GLOBAL_ONLY);
    os = Tcl_GetVar2(interp, "tcl_platform", "os", TCL_GLOBAL_ONLY);
    vers = Tcl_GetVar2(interp, "tcl_platform", "osVersion", TCL_GLOBAL_ONLY);

    Tcl_DStringInit(&arch);
    if (machine && os && vers) {
	Tcl_DStringAppend(&arch, machine, -1);
	Tcl_DStringAppend(&arch, "-", 1);
	Tcl_DStringAppend(&arch, os, -1);
	Tcl_DStringAppend(&arch, "-", 1);
	Tcl_DStringAppend(&arch, vers, -1);
    } else {
	Tcl_DStringAppend(&arch, "unknown-os", -1);
    }

    /*
     * Remove all white spaces and slashes from the architecture string 
     * because these characters are a potential source of problems and 
     * I really do not like white spaces in a directory name.
     */

    {
	char *d = Tcl_DStringValue(&arch);
	char *s = Tcl_DStringValue(&arch);

	while (*s) {
	    *d = *s;
	    if ((!isspace(*s)) && (*s != '/')) d++;
	    s++;
	}
	*d = '\0';
    } 

    Tcl_SetVar2(interp, "tnm", "arch", 
		Tcl_DStringValue(&arch), TCL_GLOBAL_ONLY);
    Tcl_DStringFree(&arch);
}

/*
 *----------------------------------------------------------------------
 *
 * SourceRcFiles --
 *
 *	This procedure evaluates a users Tnm initialization script.
 *
 * Results:
 *	1 if the file was found, 0 otherwise.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

static int
SourceRcFile(interp, fileName)
    Tcl_Interp *interp;
    char *fileName;
{
    Tcl_DString temp;
    char *fullName;
    int result = 0;

    if (! fileName) {
	return 0;
    }

    Tcl_DStringInit(&temp);
    fullName = Tcl_TranslateFileName(interp, fileName, &temp);
    if (fullName == NULL) {
	TnmWriteMessage(interp->result);
	TnmWriteMessage("\n");
    } else {
	Tcl_Channel channel;
	channel = Tcl_OpenFileChannel(NULL, fullName, "r", 0);
	if (channel) {
	    Tcl_Close((Tcl_Interp *) NULL, channel);
	    result = 1;
	    if (Tcl_EvalFile(interp, fullName) != TCL_OK) {
		TnmWriteMessage(interp->result);
		TnmWriteMessage("\n");
	    }
	}
    }
    Tcl_DStringFree(&temp);

    return result;
}

/*
 *----------------------------------------------------------------------
 *
 * SourceInitFiles --
 *
 *	This procedure evaluates the Tnm initialization scripts.
 *
 * Results:
 *	A standard Tcl result.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

static int
SourceInitFiles(interp)
    Tcl_Interp *interp;
{
    char *fileName, *library;
    static char cmd[] = "source $tnm(library)/library/init.tcl";

    library = Tcl_GetVar2(interp, "tnm", "library", TCL_GLOBAL_ONLY);
    if (! library) {
	panic("Tnm Tcl variable tnm(library) undefined.");
    }
    if (Tcl_GlobalEval(interp, cmd) != TCL_OK) {
	return TCL_ERROR;
    }

    /*
     * Load the user specific startup file. We check to see if we
     * we have a readable startup file so that we only complain
     * about errors when we are expected to complain.
     */

    fileName = getenv("TNM_RCFILE");
    if (fileName) {
	SourceRcFile(interp, fileName);
    } else {
	if (! SourceRcFile(interp, "~/.tnmrc")) {
	    SourceRcFile(interp, "~/.scottyrc");
	}
    }

    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *
 * InitCmds --
 *
 *	This procedure initializes the commands provided by the
 *	Tnm extension. The safe parameter determines if the command
 *	set should be restricted to the safe command subset.
 *
 * Results:
 *	A standard Tcl result.
 *
 * Side effects:
 *	Tcl variables are created.
 *
 *----------------------------------------------------------------------
 */

static int
InitCmds(interp, safe)
    Tcl_Interp *interp;
    int safe;
{
    CmdInfo *cmdInfoPtr;
    Tcl_CmdInfo info;

    for (cmdInfoPtr = tnmCmds; cmdInfoPtr->name != NULL; cmdInfoPtr++) {
	/*
	 * Check if the command already exists and return an error
	 * to ensure we detect name clashes while loading the Tnm
	 * extension.
	 */
	if (Tcl_GetCommandInfo(interp, cmdInfoPtr->name, &info)) {
	    Tcl_AppendResult(interp, "command \"", cmdInfoPtr->name,
			     "\" already exists", (char *) NULL);
	    return TCL_ERROR;
	}
	if (cmdInfoPtr->objProc) {
	    Tcl_CreateObjCommand(interp, cmdInfoPtr->name, cmdInfoPtr->objProc,
			      (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
	} else {
	    Tcl_CreateCommand(interp, cmdInfoPtr->name, cmdInfoPtr->proc,
			      (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
	}
	/*
	 * Hide all unsafe commands from the interpreter
	 * if it is a safe Tcl interpreter.
	 */
	if (safe && ! cmdInfoPtr->isSafe) {
            Tcl_HideCommand(interp, cmdInfoPtr->name, cmdInfoPtr->name);
	}
    }

    if (! safe) {
#ifdef HAVE_GDMO
	if (Tnm_GdmoInit(interp) != TCL_OK) {
	    return TCL_ERROR;
	}
#endif

#ifdef HAVE_OSIMIS
	if (Tnm_CmipInit(interp) != TCL_OK) {
	    return TCL_ERROR;
	}
#endif
    }

    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *
 * TnmInit --
 *
 *	This procedure is the platform independent entry point for 
 *	trusted and untrusted Tcl interpreters.
 *
 * Results:
 *	A standard Tcl result.
 *
 * Side effects:
 *	Tcl variables are created.
 *
 *----------------------------------------------------------------------
 */

int
TnmInit(interp, safe)
    Tcl_Interp *interp;
    int safe;
{ 
    if (Tcl_PkgRequire(interp, "Tcl", TCL_VERSION, 1) == NULL) {
        return TCL_ERROR;
    }

    if (Tcl_PkgProvide(interp, "Tnm", TNM_VERSION) != TCL_OK) {
        return TCL_ERROR;
    }

    InitVars(interp);
    TnmInitDns(interp);
    if (InitCmds(interp, safe) != TCL_OK) {
	return TCL_ERROR;
    }

    return SourceInitFiles(interp);
}
