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