/* swadmin.c - switch administrator code
**
** SWADMIN PROJECT - NETWORK 2000
** Network Development
**
** Copyright (c) 1994, 1996 - Carnegie Mellon University - All Rights Reserved
**
** Erikas Aras Napjus (erikas@cmu.edu)
*/

/**********************************************************************
 *
 *           Copyright 1996 by Carnegie Mellon University
 * 
 *                       All Rights Reserved
 * 
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose and without fee is hereby granted,
 * provided that the above copyright notice appear in all copies and that
 * both that copyright notice and this permission notice appear in
 * supporting documentation, and that the name of CMU not be
 * used in advertising or publicity pertaining to distribution of the
 * software without specific, written prior permission.
 * 
 * CMU DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
 * CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
 * SOFTWARE.
 * 
 * Author: Erikas Napjus <erikas@cmu.edu>
 * 
 **********************************************************************/

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */

#include <stdio.h>
#include <assert.h>

#ifdef HAVE_TERMIOS_H
# include <termios.h>
#endif /* HAVE_TERMIOS_H */

#include <curses.h>      /* Curses support */

#ifdef HAVE_STRINGS_H
# include <strings.h>
#else /* HAVE_STRINGS_H */
# include <string.h>
#endif /* HAVE_STRINGS_H */

/* CMU SNMP Library */
#include <snmp/snmp.h>

#include "swadmin.h"
#include "screen.h"
#include "menuitem.h"
#include "query.h"
#include "client.h"
#include "version.h"

static char rcsid[] = 
"$Id: swadmin.c,v 1.3 1997/02/17 16:20:53 ryan Exp $";

int errorcode = 0;

extern int password;
extern char passwd[];

struct Hub
{
  char hostname[20];
  char domain[30];
  int ipaddr[4];
  struct Hub *next;
} ;

struct Building
{
  char name[50];
  struct Hub *hublist;
  struct Building *next;
} *build_head;

void Usage(char *Err)
{
  if (Err != NULL)
    fprintf(stderr, "\nUnknown argument '%s'\n", Err);

  fprintf(stderr, "\nusage: swadmin [--version] [--config=/path/to/file]\n");
  fprintf(stderr, "--version:  Print version information.\n");
  fprintf(stderr, "--config=:  Config file to use.\n\n");
  fprintf(stderr, "The config file used is either:\n");
  fprintf(stderr, "\t the specified file\n");
  fprintf(stderr, "\t the file specified by SWADMIN_CONF\n");
  fprintf(stderr, "\t ./switches\n");
  fprintf(stderr, "\t %s\n", DEF_CONF);
  fprintf(stderr, "\n");
  exit(-1);
}

void Version(void)
{
  fprintf(stderr, "Cisco Switch Administrator, version %s\n", 
	  swadmin_Version());
  fprintf(stderr, "CMU SNMP Library Version %s\n", 
	  snmp_Version());
  exit(-1);
}

/* main - central control routine for hubadmin
*/
void main(int argc, char **argv)
{
  WINDOW *core;
  char hostname[20], domain[30], building[50];
  char this_ipaddr[20];
  char fqdn[50];
  int ip[4], incr, leng;
  int hold_oid;
  struct Hub *hub_ptr;
  struct Building *build_ptr, *build_old, *build_new;

  char *Config = NULL;
  char line[1024];
  FILE *database = NULL;

  /* Args */

  /* Hideous argument parsing, 'cause nothing standard knows how to handle
   * long arguments.
   */
  {
    int arg;

    for (arg=1; arg<argc; arg++) {

      /* Check this arg */

      if (argv[arg][0] == '-') {
        if (argv[arg][1] != '-') {
	  
          /* Short args */
          if (argv[arg][1] == 'v')
            Version();

          else if (argv[arg][1] == 'c')
	    Config = argv[++arg];

          else
            Usage(argv[arg]);
        } else {

          /* Long args */
          if (!strcmp(&(argv[arg][2]), "version"))
            Version();

          else if (!strncmp(&(argv[arg][2]), "config=", 7))
	    Config = (argv[arg]+7);

          else
            Usage(argv[arg]);

        } /* End of long args */
      } else {
        goto EndOfDashArgs;
      } /* End of - args */

    } /* End of loop */

  EndOfDashArgs:
    /* ASSERT:  argv[arg] is the first non-parse argument */
    ;
  }

  init_client(25);

/* initialize internal database */
  build_head = NULL;
  build_ptr = build_old = NULL;
  hub_ptr = NULL;

  /* open database file*/
  if (Config != NULL)
    database = fopen(Config, "r");

  if (database == NULL) {
    char *env;
    env = getenv("SWADMIN_CONF");
    if (env)
      database = fopen(env, "r");
  }

  if (database == NULL)
    database = fopen("switches", "r");

  if (database == NULL)
    database = fopen(DEF_CONF, "r");

  if (database == NULL) {
    fprintf(stderr, "Unable to locate configuration file!\n");
    exit(1);
  }

  while (!feof(database)) {

    /* First, read the line */
    if (fgets((char *)line, 1024, database) == NULL)
      break;

    /* Is this a comment? */
    if ((line[0] == '#') || 
	(line[0] == ';') ||
	(strlen((char *)line) < 6)) {
      /* A few blank spaces should actually be ignored.  I hope. */
      ;
	} else {
	  sscanf(line, "%s %s %d.%d.%d.%d %s\n", 
		 hostname, domain, &ip[0], &ip[1], &ip[2], &ip[3], building);

	  /* do the funky capitalization thing for building names */
	  leng = strlen(building);
      
	  if ((building[0] >= 'a') && (building[0] <= 'z'))
	    building[0]+=('A'-'a');
	  for (incr=0; incr < leng; incr++)
	    if (building[incr]=='_') {
	      building[incr]=' ';
	      if ((building[incr+1] >= 'a') && (building[incr+1] <= 'z'))
		building[incr+1]+=('A'-'a');
	    }

	  /* find the correct building record; 
	   * add if there isn't one already 
	   */
	  build_ptr = build_head;
	  build_old = NULL;

	  while (build_ptr != NULL) {
	    if (strcmp(build_ptr->name, building) >= 0)
	      break;
	    build_old = build_ptr;
	    build_ptr = build_ptr->next;
	  }

	  if ((build_ptr == NULL) || (strcmp(build_ptr->name, building) != 0)) {
	    build_new = (struct Building *) malloc(sizeof(struct Building));
	    assert(build_new != NULL);
	    if (build_old == NULL)
	      build_head = build_new;
	    else
	      build_old->next = build_new;
	    strcpy(build_new->name, building);
	    build_new->next = build_ptr;
	    build_new->hublist = NULL;
	    build_ptr = build_new;
	  }
      
	  hub_ptr = (struct Hub *) malloc(sizeof(struct Hub));
	  assert (hub_ptr != NULL);
	  hub_ptr->next = build_ptr->hublist;
	  build_ptr->hublist = hub_ptr;
	  strcpy(hub_ptr->hostname, hostname);
	  strcpy(hub_ptr->domain, domain);
	  for (incr=0; incr < 4; incr++)
	    hub_ptr->ipaddr[incr] = ip[incr];
	}
  }
  fclose(database);

  /* prepare the primary curses stuff */
  core = initscr();
  screen_init_all();
  refresh();
  cbreak();
  noecho();

  incr=0;

  screen_init();
  screen_help_default();

/* the top menu - select a building */
 top:
  init_item();
  
  build_ptr = build_head;
  while (build_ptr != NULL)
    {
      add_item(build_ptr->name, (void *) build_ptr);
      build_ptr = build_ptr->next;
    }

  clear_legend();
  set_legend(0, 
"From this menu, use the arrow keys or standard emacs commands to select");
  set_legend(1, 
"the building where the switch you wish to examine is located. This");
  set_legend(2, 
"information is automatically generated from CINDI nightly. Note that");
  set_legend(3, 
"switches in the Data Communications Room or Operations appear in those");
  set_legend(4, "categories instead of Cyert Hall.");

  build_ptr = (struct Building *) menu_item("building", 
	"All Quacks Must Duck");

  if (build_ptr == NULL)
    switch (errorcode)
      {
      case ERR_QUIT:
	goto end;
      case ERR_BACK:
      case ERR_TOP:
	clear_stack();
	errorcode = 0;
	printf("\07");
	goto top;
      }
  pushd(-1); /* because we're going forward */

/* select a hub within the building */
 second:
  init_item();

  for (hub_ptr = build_ptr->hublist; hub_ptr != NULL; hub_ptr = hub_ptr->next)
    add_item(hub_ptr->hostname, (void *) hub_ptr);

  clear_legend();
  set_legend(0, 
"From this menu, select the individual switch which you would like to");
  set_legend(1,
"examine from the list of switches located in the building you have");
  set_legend(2,
"previously selected. Note that the building name is displayed at the top");
  set_legend(3, 
"of the screen.");

  hub_ptr = (struct Hub *) menu_item("hub", build_ptr->name);

  if (hub_ptr == NULL)
    switch (errorcode)
      {
      case ERR_QUIT:
	goto end;
      case ERR_TOP:
	clear_stack();
      case ERR_BACK:
	errorcode = 0;
	goto top;
      }

  pushd(-1); /* because we're going forward */

/* prepare the necessary information about the selected hub */
  sprintf(this_ipaddr, "%d.%d.%d.%d", hub_ptr->ipaddr[0], hub_ptr->ipaddr[1],
	  hub_ptr->ipaddr[2], hub_ptr->ipaddr[3]);

  sprintf(fqdn, "%s.%s", hub_ptr->hostname, hub_ptr->domain);

/* primary command menu */
 control:
  init_item();

  add_item("Show Switch Software", (void *)20);
  add_item("Show Switch Hardware", (void *)1);
  add_item("Show Port Status", (void *)100);
  add_item("Show Connected Devices", (void *)9);
  add_item("Show Historical Statistics", (void *)6);
  add_item("Show Current Port Statistics", (void *)2);
  add_item("Show Current Module Statistics", (void *)5);
  add_item("Set Switch Parameters", (void *)3);
  add_item("Set Port Configuration", (void *)4);
  add_item("Enter Switch Password", (void *)8);

  clear_legend();
  set_legend(0,
"From this menu, you can select what action you would like to take upon");
  set_legend(1,
"the switch you have selected. You can see the current configuration of");
  set_legend(2,
"the switch (software or hardware), the status of modules and ports,");
  set_legend(3,
"connected routers and switches, historical statistics, current statistics,");
  set_legend(4,
"or change parameters for the entire switch or for a given port.");

  switch ((int)menu_item("option", fqdn))
    {
    case 0:
      switch (errorcode)
	{
	case ERR_QUIT:
	  goto end;
	case ERR_TOP:
	  clear_stack();
	  errorcode = 0;
	  goto top;
	case ERR_BACK:
	  errorcode = 0;
	  goto second;
	}
    case 1:
/* "show switch hardware" */
      switch (get_switch_hardware(this_ipaddr, "public", fqdn))
	{
	case ERR_QUIT:
	  goto end;
	case ERR_TOP:
	  clear_stack();
	  goto top;
	case ERR_BACK:
	default:
	  goto control;
	}
    case 20:
/* "show switch software" */
      switch (get_switch_software(this_ipaddr, "public", fqdn))
	{
	case ERR_QUIT:
	  goto end;
	case ERR_TOP:
	  clear_stack();
	  goto top;
	case ERR_BACK:
	default:
	  goto control;
	}
    case 100:
/* "show switch modules" */
      pushd(-1); /* because we're going forward into a menu */
      switch (select_module(this_ipaddr, "public", fqdn, 0, BROWSING))
	{
	case ERR_QUIT:
	  goto end;
	case ERR_TOP:
	  clear_stack();
	  goto top;
	case ERR_BACK:
	default:
	  goto control;
	}
    case 5:
/* "show current port hub statistics" */
      pushd(-1); /* going forward into menu */
      switch(select_module(this_ipaddr, "public", fqdn, 0, CURRENTMOD))
	{
	case ERR_QUIT:
	  goto end;
	case ERR_TOP:
	  clear_stack();
	  goto top;
	case ERR_BACK:
	default:
	  goto control;
	}
    case 8:
/* "enter hub password" */
      switch(get_password(this_ipaddr, fqdn))
	{
	case ERR_QUIT:
	  goto end;
	case ERR_TOP:
	  clear_stack();
	  goto top;
	case ERR_ALERT:
	case ERR_BACK:
	default:
	  goto control;
	}
    case 9:
/* "show machines connected to hub" */
      switch(cdp_neighbors(this_ipaddr, "public", fqdn))
	{
	case ERR_QUIT:
	  goto end;
	case ERR_TOP:
	  clear_stack();
	  goto top;
	case ERR_BACK:
	default:
	  goto control;
	}
    case 6:
/* "show background hub statistics" */
      pushd(-1); /* going forward into menu */
      switch(select_module(this_ipaddr, "public", fqdn, 0, BACKGROUND))
	{
	case ERR_QUIT:
	  goto end;
	case ERR_TOP:
	  clear_stack();
	  goto top;
	default:
	case ERR_BACK:
	  goto control;
	}
    case 2:
/* "show current port statistics" */
      pushd(-1); /* going forward into menu */
      switch(select_module(this_ipaddr, "public", fqdn, 0, CURRENT))
	{
	case ERR_QUIT:
	  goto end;
	case ERR_TOP:
	  clear_stack();
	  goto top;
	default:
	case ERR_BACK:
	  goto control;
	}
    case 3:
/* "hub control" */
      pushd(-1); /* going into menu */
    control_hub:
      if (!password)
	{
	  switch(get_password(this_ipaddr, fqdn))
	    {
	    case ERR_QUIT:
	      goto end;
	    case ERR_BACK:
	      break;
	    case ERR_TOP:
	      clear_stack();
	      goto top;
	    case ERR_ALERT:
	      goto control;
	    }
	}
      init_item();
      add_item("Clear Counters", (void *)101);
      add_item("Reset Switch (Cold Boot)", (void *)100);

      hold_oid = (int)menu_item("option", fqdn);
      if (hold_oid == 0)
	switch (errorcode)
	  {
	  case ERR_QUIT:
	    goto end;
	  case ERR_BACK:
	    goto control;
	  case ERR_TOP:
	    clear_stack();
	    goto top;
	  }
      switch (hold_oid)
	{
	case 101:
	  if (!snmpsettime(this_ipaddr, passwd, 
			   ".1.3.6.1.4.1.9.5.1.1.12.0", 0, NULL))
	    screen_error("Unable to clear counters");
	  if (!snmpsettime(this_ipaddr, passwd, 
			   ".1.3.6.1.4.1.9.5.1.1.13.0", 0, NULL))
	    screen_error("Unable to clear counters");
	  break;
	case 100:
	  if (!snmpset(this_ipaddr, passwd, ".1.3.6.1.4.1.9.5.1.1.9.0", 2, NULL))
	    screen_error("Unable to reset switch");
	  break;
	}
      goto control_hub;
/* "port control" */
    case 4:
      if (!password)
	{
	  switch(get_password(this_ipaddr, fqdn))
	    {
	    case ERR_QUIT:
	      goto end;
	    case ERR_BACK:

	      break;
	    case ERR_TOP:
	      clear_stack();
	      goto top;
	    case ERR_ALERT:
	      goto control;
	    }
	}
      pushd(-1); /* going forward into menu */
      switch(select_module(this_ipaddr, passwd, fqdn, 0, CONTROL))
	{
	case ERR_QUIT:
	  goto end;
	case ERR_BACK:
	  goto control;
	case ERR_TOP:
	  clear_stack();
	  goto top;
	}
    }
/* close curses and exit program */
 end:
  screen_close_all();
  endwin();
  close_client();
}
