/*\
 *	DISTRIBUTION: HNMS v2.0
 *	FILE: hnmsd/server.c
 *
 *	HNMS Server module.
 *
 *	Jude George
 *	NAS Facility, NASA Ames Research Center
 *
 *	Copyright (c) 1994 Jude George
 *
 *	This program is free software; you can redistribute it and/or modify
 *	it under the terms of the GNU General Public License as published by
 *	the Free Software Foundation; either version 1, or (at your option)
 *	any later version.
 *
 *	This program is distributed in the hope that it will be useful,
 *	but WITHOUT ANY WARRANTY; without even the implied warranty of
 *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *	GNU General Public License for more details.
 *
 *	You should have received a copy of the GNU General Public License
 *	along with this program; if not, write to the Free Software
 *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
\*/

#include <stdio.h>

#include "stdhnms.h"

static int		class[MAX_OBJECTS];
static int		reach_status[MAX_OBJECTS];
static int		ip_parent[MAX_OBJECTS];
static int		phys_parent[MAX_OBJECTS];
static int		if_parent[MAX_OBJECTS];

/*\
 *  Generate Network or Subnet objects as such:
 *
 *	Ipaddr     -->   Subnet, if I have the Ipaddr's hnmsIpaddrMask
 *	Subnet     -->   Network
 *	Network	   -->	 Internet (just set it, noo need to create it)
\*/
static void SERVER_make_ip_parent(id)
    const int		id;
{
    VarBindList		vbl;
    int			class;
    unsigned int	ip_address, mask;
    char		ip_addr_str[STRBUFLEN];
    char		mask_str[STRBUFLEN];
    char		*cp, *uname;
    char		parent[STRBUFLEN];
    int			parent_id;
    unsigned int	ts;
    int			rval;

    ts = get_int_time();

    bzero(parent, STRBUFLEN);
    OBJ_get(id, oid_hnmsObjClass, &class, 0, 0, 0, 0);

    switch (class) {
    case OBJ_ipaddr:
	if (!OBJ_get(id, oid_hnmsObjIpParent, 0, 0, 0, 0, 0) &&
	    OBJ_get(id, oid_hnmsObjMask, &mask, 0, 0, 0, 0) &&
	    OBJ_get(id, oid_hnmsObjIpaddr, &ip_address, 0, 0, 0, 0)) {
	    ip_address &= mask;
	    if (ip_address) {
		cp = ipaddr_int2str(ip_address);
		if (cp) {
		    strcpy(ip_addr_str, cp);
		    cp = ipaddr_int2str(mask);
		    if (cp) {
			strcpy(mask_str, cp);
			sprintf(parent, "%s_%s", ip_addr_str, mask_str);
			uname = OBJ_make_uname(OBJ_subnet, parent);
			rval = OBJ_add(0, OBJ_subnet, parent);
			parent_id = ABS(rval);
			if (parent_id) {
			    OBJ_set(id, oid_hnmsObjIpParent, uname,
				    strlen(uname), MIB_displaystring, 0, &ts);
			    OBJ_set(parent_id, oid_hnmsObjIpaddr, &ip_address,
				    0, MIB_ipaddr, 0, &ts);
			    OBJ_set(parent_id, oid_hnmsObjMask, &mask,
				    0, MIB_ipaddr, 0, &ts);
			}
		    }
		}
	    }
	}
	break;

    case OBJ_subnet:
	if (!OBJ_get(id, oid_hnmsObjIpParent, 0, 0, 0, 0, 0) &&
	    OBJ_get(id, oid_hnmsObjIpaddr, &ip_address, 0, 0, 0, 0)) {
	    if (ip_address >= 0xc0000000)
		mask = 0xffffff00;
	    else if (ip_address >= 0x80000000)
		mask = 0xffff0000;
	    else
		mask = 0xff000000;
	    ip_address &= mask;
	    if (ip_address) {
		cp = ipaddr_int2str(ip_address);
		if (cp) {
		    uname = OBJ_make_uname(OBJ_network, cp);
		    rval = OBJ_add(0, OBJ_network, cp);
		    parent_id = ABS(rval);
		    if (parent_id) {
			OBJ_set(id, oid_hnmsObjIpParent, uname, strlen(uname),
				MIB_displaystring, 0, &ts);
			OBJ_set(parent_id, oid_hnmsObjIpaddr, &ip_address, 0,
				MIB_ipaddr, 0, &ts);
		    }
		}
	    }
	}
	break;

    case OBJ_network:
	if (!OBJ_get(id, oid_hnmsObjIpParent, 0, 0, 0, 0, 0)) {
	    uname = OBJ_make_uname(OBJ_internet, "0.0.0.0");
	    OBJ_set(id, oid_hnmsObjIpParent, uname, strlen(uname),
		    MIB_displaystring, 0, &ts);
	}
	break;

    default:;
    }
}

/*\
 *  Create Network and Subnet objects from existing Ipaddr objects.
 *  Create Site and Administrator objects from existing Processor objects.
\*/
int SERVER_make_ip_parents()
{
    unsigned int	current_time, pci;
    static unsigned int	last_create_time = 0;
    
    current_time = get_int_time();
    pci = PARAM_get_int(oid_hnmsModuleServerCreateInterval);
    if (current_time - last_create_time >= pci) {
	last_create_time = current_time;
	OBJ_iterate(SERVER_make_ip_parent, NULL);
    }
    return 0;
}

/*\
 *  Create the internet object.
\*/
int SERVER_make_internet()
{
    OBJ_add(0, OBJ_internet, "0.0.0.0");
    return 0;
}

/*\
 *  For each Ipaddr, cache it's status and the id's of it's parent
 *  Subnet, Processor, and Interface.
\*/
static int SERVER_cache_obj_parents(id)
    const int		id;
{
    int			class;
    int			status;
    static char		uname[STRBUFLEN], ustring[STRBUFLEN];
    char		*cp;
    int			parent_id;
    int			parent_status;
    int			if_index;
    unsigned int	ts;

    ts = get_int_time();

    class = 0;
    OBJ_get(id, oid_hnmsObjClass, &class, 0, 0, 0, 0);
    if (class != OBJ_ipaddr)
	return;

    status = STATUS_UNKNOWN;
    OBJ_get(id, oid_hnmsObjReachStatus, &status, 0, 0, 0, 0);
    if (status == 0)
	return;
    reach_status[id] = status;

    bzero(uname, STRBUFLEN);
    OBJ_get(id, oid_hnmsObjIpParent, &uname, 0, 0, 0, 0);
    ip_parent[id] = OBJ_find(uname);
    bzero(uname, STRBUFLEN);
    OBJ_get(id, oid_hnmsObjPhysParent, &uname, 0, 0, 0, 0);
    phys_parent[id] = OBJ_find(uname);
    bzero(uname, STRBUFLEN);
    bzero(ustring, STRBUFLEN);
    OBJ_get(id, oid_hnmsIpaddrIfIndex, &if_index, 0, 0, 0, 0);
    OBJ_get(id, oid_hnmsObjPhysParent, &uname, 0, 0, 0, 0);
    if (if_index && (uname[0] != 0)) {
	/* wierd...a cast is needed here for suns... thorpej */
#ifdef __sparc
	cp = (char *)strchr((char *)uname, (int)':');
#else
	cp = strchr(uname, (int)':');
#endif
	sprintf(ustring, "%s_%d", ++cp, if_index);
	if_parent[id] = OBJ_find(OBJ_make_uname(OBJ_interface, ustring));
    }

    if (reach_status[id] < reach_status[ip_parent[id]])
	reach_status[ip_parent[id]] = reach_status[id];
    if (reach_status[id] < reach_status[phys_parent[id]])
	reach_status[phys_parent[id]] = reach_status[id];
    if (reach_status[id] < reach_status[if_parent[id]])
	reach_status[if_parent[id]] = reach_status[id];
    return 0;
}

/*\
 *  Assign the status of an object that is a parent of an Ipaddr
 *  (Subnet, Processor, or "parent" Interface).
\*/
static int SERVER_assign_obj_status(id)
{
    unsigned int	ts;

    if (reach_status[id] == 0)
	return;

    ts = get_int_time();
    OBJ_set(id, oid_hnmsObjReachStatus, &(reach_status[id]), 0,
	    MIB_integer, 0, &ts);
    return 0;
}

/*\
 *  Assign reachability status of Subnet and Processor objects
 *  from the reachability status of Ipaddr objects.
\*/
int SERVER_assign_status()
{
    int			*p;
    unsigned int	current_time, pai;
    static unsigned int	last_assign_time = 0;
    
    current_time = get_int_time();
    pai = PARAM_get_int(oid_hnmsModuleServerAssignInterval);
    if (current_time - last_assign_time >= pai) {
	last_assign_time = current_time;

	for (p = reach_status;
	     p < reach_status + (MAX_OBJECTS * sizeof(int)); p++)
	    *p = STATUS_UNKNOWN;
	bzero(ip_parent, MAX_OBJECTS * sizeof(int));
	bzero(phys_parent, MAX_OBJECTS * sizeof(int));
	bzero(if_parent, MAX_OBJECTS * sizeof(int));
	
	OBJ_iterate(SERVER_cache_obj_parents, NULL);
	OBJ_iterate(SERVER_assign_obj_status, NULL);
    }
    return 0;
}
