On Thu, Aug 30, 2001 at 05:01:34PM -0500, Simeon Johnston wrote:
> I am looking for some sort of package that will allow me to control a
> device via a com/tty port.
> Basically - I want to be able to control my Cisco router (or any kind of
> router/computer via com/tty) without having to actually log into it.
> Lets say I want to get the current configuration.
> I use minicom and log in and all that jazz.  Then a simple "show
> whatever" will give it to me.
> I would like to create some sort of script to do this automatically and
> have the output be usable by any application.  Able to be logged or sent
> to another application that will do something else etc.
> 
> I want to be able to monitor the router and be able to respond when
> something happens.  If the router goes down or changes IP's I want to be
> able to reconnect via VPN or something.  Have certain daemons restart etc.
> 
> Does this make sense?  Is it possible (or will I have to actually make
> this program)?

Here is something I knocked over a weekend...

florin

-- 

"If it's not broken, let's fix it till it is."

41A9 2BDE 8E11 F1C5 87A6  03EE 34B3 E075 3B90 DFE4
-------------- next part --------------
/*
    router_stats.c - connects via the serial port to a router and gets stats
    Copyright (C) 2001 Florin Iucha <florin at iucha.net>

    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 2 of the License, 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

/* $Id: router_stats.c,v 1.9 2001/07/16 16:13:56 florin Exp florin $ */

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <signal.h>
#include <time.h>

/* baudrate settings are defined in <asm/termbits.h>, which is
included by <termios.h> */
#define BAUDRATE B38400            
#define _POSIX_SOURCE 1 /* POSIX compliant source */


volatile int SIGNAL_HAPPENED = 0;

void custom_handler(int signal)
{
   SIGNAL_HAPPENED = 1;
}

void setup_device(int fd, struct termios* oldtio)
{
   struct termios newtio;

   fcntl(fd, F_SETFL, FNDELAY);

   tcgetattr(fd, oldtio); /* save current serial port settings */
   bzero(&newtio, sizeof(newtio)); /* clear struct for new port settings */

   /* 
      BAUDRATE: Set bps rate. You could also use cfsetispeed and cfsetospeed.
      CRTSCTS : output hardware flow control (only used if the cable has
                all necessary lines. See sect. 7 of Serial-HOWTO)
      CS8     : 8n1 (8bit,no parity,1 stopbit)
      CLOCAL  : local connection, no modem contol
      CREAD   : enable receiving characters
    */
   newtio.c_cflag = BAUDRATE | CS8 | CLOCAL | CREAD;

   /*
      IGNPAR  : ignore bytes with parity errors
      ICRNL   : map CR to NL (otherwise a CR input on the other computer
                will not terminate input)
                otherwise make device raw (no other input processing)
    */
   newtio.c_iflag = IGNPAR | ICRNL;

   /*
    Raw output.
    */
   newtio.c_oflag = 0;

   /*
      ICANON  : enable canonical input
      disable all echo functionality, and don't send signals to calling program
    */
   newtio.c_lflag = ICANON;

   /* 
      initialize all control characters 
      default values can be found in /usr/include/termios.h, and are given
      in the comments, but we don't need them here
    */
   newtio.c_cc[VINTR]    = 0;     /* Ctrl-c */ 
   newtio.c_cc[VQUIT]    = 0;     /* Ctrl-\ */
   newtio.c_cc[VERASE]   = 0;     /* del */
   newtio.c_cc[VKILL]    = 0;     /* @ */
   newtio.c_cc[VEOF]     = 4;     /* Ctrl-d */
   newtio.c_cc[VTIME]    = 0;     /* inter-character timer unused */
   newtio.c_cc[VMIN]     = 0;     /* blocking read until 1 character arrives */
   newtio.c_cc[VSWTC]    = 0;     /* '\0' */
   newtio.c_cc[VSTART]   = 0;     /* Ctrl-q */ 
   newtio.c_cc[VSTOP]    = 0;     /* Ctrl-s */
   newtio.c_cc[VSUSP]    = 0;     /* Ctrl-z */
   newtio.c_cc[VEOL]     = 0;     /* '\0' */
   newtio.c_cc[VREPRINT] = 0;     /* Ctrl-r */
   newtio.c_cc[VDISCARD] = 0;     /* Ctrl-u */
   newtio.c_cc[VWERASE]  = 0;     /* Ctrl-w */
   newtio.c_cc[VLNEXT]   = 0;     /* Ctrl-v */
   newtio.c_cc[VEOL2]    = 0;     /* '\0' */

   /* 
      now clean the modem line and activate the settings for the port
    */
   tcflush(fd, TCIFLUSH);
   tcsetattr(fd, TCSANOW, &newtio);
}


void reset_device(int fd, struct termios* oldtio)
{
   /* restore the old port settings */
   tcsetattr(fd, TCSANOW, oldtio);
}

#define BUFSIZE 1024
#define MAX_COMMANDS 16

struct commands
{
   char* device;

   char* setup_commands[MAX_COMMANDS];
   int setup_count;
   
   char* data_commands[MAX_COMMANDS];
   int data_count;

   char* reset_commands[MAX_COMMANDS];
   int reset_count;

   char* words[MAX_COMMANDS];
   int words_count;
   
   char* buff;

   int toSleep;
};


int read_commands(int argc, char* argv[], struct commands* comm)
{
   int fd;
   int res;

   if (argc < 4)
   {
      printf("Not enough args: use '%s <device> <commandfile>'\n", argv[0]);
      return 1;
   }

   comm->device = strdup(argv[1]);
   if (comm->device == 0)
   {
      puts("Cannot alloc memory.");
      return 1;
   }

   comm->buff = malloc(BUFSIZE);
   if (comm->buff == 0)
   {
      puts("Cannot alloc memory.");
      return 1;
   }
  
   comm->toSleep = atoi(argv[3]);
   
   /* now read the commands */ 
   
   fd = open(argv[2], O_RDONLY);
 
   if (fd == -1)
   {
      perror(argv[2]);
      return 1;
   }

   res = read(fd, comm->buff, BUFSIZE);

   if (res == -1)
   {
      perror(argv[2]);
      return 1;
   }
  
   /* increade BUFSIZE */
   if (res == BUFSIZE)
   {
      puts("BUFFER FULL");   
      return 1;
   }

   /* so the parsing ends... */
   comm->buff[res] = 0;

   comm->setup_count = 0; 
   comm->data_count  = 0; 
   comm->reset_count = 0; 
   comm->words_count = 0; 

   /* parse the thingie */
   {
      int i;

      char* line = strtok(comm->buff, "\n");

      while (line)
      {
         switch (line[0])
         {
         case 'S':
            if (comm->setup_count < MAX_COMMANDS)
               comm->setup_commands[comm->setup_count ++] = strdup(line + 2);
            break;
            
         case 'D':
            if (comm->data_count < MAX_COMMANDS)
               comm->data_commands[comm->data_count ++] = strdup(line + 2);
            break;

         case 'R':
            if (comm->reset_count < MAX_COMMANDS)
               comm->reset_commands[comm->reset_count ++] = strdup(line + 2);
            break;

         case 'G':
            if (comm->words_count < MAX_COMMANDS)
               comm->words[comm->words_count ++] = strdup(line + 2);
            break;

         case '#':
            /* comment */
            break;

         default:
            printf("Line '%s' not understood.\n", line);
         }
         
         line = strtok(0, "\n");
      }
   }

   close (fd);
  
   return 0;
}

void cleanup_commands(struct commands* comm)
{
   int i;

   if (comm->buff)
      free(comm->buff);

   for (i = 0; i < comm->setup_count; i ++)
      free(comm->setup_commands[i]);

   for (i = 0; i < comm->data_count; i ++)
      free(comm->data_commands[i]);

   for (i = 0; i < comm->reset_count; i ++)
      free(comm->reset_commands[i]);

   for (i = 0; i < comm->words_count; i ++)
      free(comm->words[i]);
}

void setup_channel(int fd, struct commands* comm)
{
   int res;
   int i;

   for (i = 0; i < comm->setup_count; i ++)
   {
      int len = strlen(comm->setup_commands[i]);

      res = write(fd, comm->setup_commands[i], len);

      if (res == -1)
      {
         perror(comm->device);
         exit(-1);
      }

      if (res != len)
      {
         printf("Hmmm: tried %d, wrote %d\n", len, res);
      }

      write(fd, "\r", 1);

      sleep(1);

      res = 1;
      while (res)
      {
         res = read(fd, comm->buff, BUFSIZE); 

         if (res == -1)
         {
            if (errno != EAGAIN)
            {
               perror(comm->device);
               exit(-1);
            }

            break;
         }
      }
   }
}

void reset_channel(int fd, struct commands* comm)
{
   int res;
   int i;

   for (i = 0; i < comm->reset_count; i ++)
   {
      int len = strlen(comm->reset_commands[i]);

      res = write(fd, comm->reset_commands[i], len);

      if (res == -1)
      {
         perror(comm->device);
         exit(-1);
      }

      if (res != len)
      {
         printf("Hmmm: tried %d, wrote %d\n", len, res);
      }

      write(fd, "\r", 1);

      sleep(1);

      res = 1;
      while (res)
      {
         res = read(fd, comm->buff, BUFSIZE); 

         if (res == -1)
         {
            if (errno != EAGAIN)
            {
               perror(comm->device);
               exit(-1);
            }

            break;
         }
      }
   }
}

void execute_commands(int fd, struct commands* comm)
{
   int res;
   int last_pos = 0;
   int i;

   /* printf("The time is now %d\n", time(0)); */
   
   for (i = 0; i < comm->data_count; i ++)
   {
      int len = strlen(comm->data_commands[i]);

      res = write(fd, comm->data_commands[i], len);

      if (res == -1)
      {
         perror(comm->device);
         exit(-1);
      }

      if (res != len)
      {
         printf("Hmmm: tried %d, wrote %d\n", len, res);
      }

      write(fd, "\r", 1);

      sleep(1);

      res = 1;
      while (res)
      {
         res = read(fd, comm->buff + last_pos, BUFSIZE - last_pos); 

         if (res == -1)
         {
            if (errno != EAGAIN)
            {
               perror(comm->device);
               exit(-1);
            }

            break;
         }

         last_pos += res;
      }

      if (last_pos)
      {
         char* line = strtok(comm->buff, "\n");

         while (line)
         {
            int i;

            for (i = 0; i < comm->words_count; i ++)
            {
               if (strstr(line, comm->words[i]))
               {
                  write(1, line, strlen(line));
                  write(1, "\n", 1);
                  break;
               }
            }
         
            line = strtok(0, "\n");
         }
         
         fsync(1);
      }
   }
}

int main(int argc, char* argv[])
{
   int fd, res;
   struct termios oldtio;
   int i;
   struct commands comms;

   if (read_commands(argc, argv, &comms))
   {
      return -1;
   } 
   
   /* 
      Open modem device for reading and writing and not as controlling tty
      because we don't want to get killed if linenoise sends CTRL-C.
    */
   fd = open(comms.device, O_RDWR | O_NOCTTY ); 
   if (fd < 0)
   {
      perror(comms.device);
      exit(-1);
   }
   
   setup_device(fd, &oldtio);

   setup_channel(fd, &comms);

   signal(SIGTERM, custom_handler);
   signal(SIGINT, custom_handler);

   execute_commands(fd, &comms);
   while (SIGNAL_HAPPENED == 0 && comms.toSleep)
   {
      sleep(comms.toSleep);
      execute_commands(fd, &comms);
   }
   
   reset_channel(fd, &comms);

   reset_device(fd, &oldtio);

   close(fd);

   return 0;
}


-------------- next part --------------
S:
S:<your password here>
D:show uptime
D:show int eth0
D:show int wan0
D:show int wan0-0
D:stats eth0
D:stats wan0
D:stats wan0-0
R:exit