/******************************************************************************
(c) 2001-2004 patrick Caulfield patrick@debian.org
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
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.
******************************************************************************/
#include <sys/types.h>
#include <sys/uio.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/ioctl.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/utsname.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <termios.h>
#include <signal.h>
#include <string.h>
#include <fcntl.h>
#include <dirent.h>
#include <ctype.h>
#include <regex.h>
#include <stdlib.h>
#include <utmp.h>
#include <grp.h>
#include <signal.h>
#include <assert.h>
#ifdef HAVE_NET_IF_ETHER_H
#include <net/if.h>
#include <net/if_ether.h>
#endif
#ifdef HAVE_NET_ETHERNET_H
#include <net/ethernet.h>
#endif
#ifdef HAVE_NETINET_ETHER_H
#include <netinet/ether.h>
#endif
#include <string>
#include "interfaces.h"
#include "moprc.h"
#ifdef __FreeBSD__
#define ether_addr_octet octet
#endif
static int mop_socket;
static unsigned char last_message[1500];
static int last_message_len;
static int show_info = 0;
static int do_moprc(u_int8_t *, int, int, int);
static int send_boot(u_int8_t *macaddr, int interface);
static LATinterfaces *iface;
static int usage(FILE *f, char *cmd)
{
fprintf(f, "\nUsage: %s [?hVv] [-i <interface>] <node name>|<macaddr>\n", cmd);
fprintf(f, " -? Show this usage message\n");
fprintf(f, " -h Show this usage message\n");
fprintf(f, " -V Show the version of moprc\n");
fprintf(f, " -i Ethernet interface to use (default to first found)\n");
fprintf(f, " -t Trigger (reboot) the server\n");
fprintf(f, " -b Make ^H send DEL\n");
fprintf(f, " -v Show target information\n");
fprintf(f, " -p <ms> Set poll interval (default 200)\n");
fprintf(f, "\n");
fprintf(f, "Node names are read from /etc/ethers\n");
fprintf(f, "MAC addresses in colon-seperated form. eg:\n");
fprintf(f, "\n%s -i eth1 08:00:2B:2B:AD:99\n", cmd);
if (geteuid() != 0)
fprintf(f, "\nYou will probably need to be root to run this program.\n");
fprintf(f, "\n");
return -1;
}
/* Send a MOP message to a specified MAC address */
static int send_message(unsigned char *buf, int len, int interface, u_int8_t *macaddr)
{
int status;
if (len < 46) len = 46;
status = iface->send_packet(interface, macaddr, buf, len);
if (status < 0)
perror("send message");
memmove(last_message, buf, len);
last_message_len = len;
return status;
}
static int send_last_message(int interface, u_int8_t *macaddr)
{
return send_message(last_message, last_message_len, interface, macaddr);
}
int main(int argc, char *argv[])
{
int opt;
int interface = -1;
int trigger=0;
int convert_bs = 0;
int poll_interval = 200; /* in ms */
char ifname_buf[255];
char *ifname;
struct ether_addr addr;
if (argc < 2)
{
return usage(stdout, argv[0]);
}
/* Look for MOPRC_INTERFACE environment variable */
ifname = getenv("MOPRC_INTERFACE");
/* Get command-line options */
opterr = 0;
while ((opt=getopt(argc,argv,"?hVvtbi:p:")) != EOF)
{
switch(opt)
{
case 'h':
return usage(stdout, argv[0]);
case '?':
return usage(stdout, argv[0]);
case 'v':
show_info++;
break;
case 't':
trigger++;
break;
case 'b':
convert_bs++;
break;
case 'i':
strcpy(ifname_buf, optarg);
ifname = ifname_buf;
break;
case 'p':
poll_interval = atoi(optarg);
break;
case 'V':
printf("\nMoprc version %s\n\n", VERSION);
exit(0);
break;
}
}
if (!argv[optind])
{
return usage(stderr, argv[0]);
}
/* Check for a hostname in /etc/ethers */
if (ether_hostton(argv[optind], &addr) != 0)
{
#ifndef __APPLE__
struct ether_addr *addr1;
/* Otherwise parse ethernet MAC address */
addr1 = ether_aton(argv[optind]);
if (addr1)
{
addr = *addr1;
}
else
#endif
{
fprintf(stderr, "unknown node name or bad MAC address %s\n", argv[optind]);
return 3;
}
}
/* Initialise the platform-specific interface code */
iface = LATinterfaces::Create();
if (iface->Start(LATinterfaces::ProtoMOP) == -1)
{
fprintf(stderr, "Can't create MOP protocol socket: %s\n", strerror(errno));
exit(1);
}
// If no interface on the command-line then use defaults
interface = iface->find_interface(ifname);
if (interface == -1)
{
if (ifname)
fprintf(stderr, "Cannot resolve interface %s\n", ifname);
else
fprintf(stderr, "Cannot find any ethernet interfaces\n");
return 2;
}
mop_socket = iface->get_fd(interface);
if (iface->bind_socket(interface))
return 3;
if (trigger)
{
return send_boot(addr.ether_addr_octet, interface);
}
return do_moprc(addr.ether_addr_octet, interface, convert_bs, poll_interval*1000);
}
static int readmop(unsigned char *buf, int buflen)
{
int ifn;
unsigned char macaddr[6];
bool more;
return iface->recv_packet(mop_socket, ifn, macaddr, buf, buflen, more);
}
static int send_reserve(u_int8_t *macaddr, int interface)
{
unsigned char buf[32];
memset(buf, 0, sizeof(buf));
buf[0] = 9;
buf[1] = 0;
buf[2] = MOPRC_CMD_RESERVE;
return send_message(buf, 11, interface, macaddr);
}
static int send_release(u_int8_t *macaddr, int interface)
{
unsigned char buf[32];
memset(buf, 0, sizeof(buf));
buf[0] = 1;
buf[1] = 0;
buf[2] = MOPRC_CMD_RELEASE;
return send_message(buf, 3, interface, macaddr);
}
static int send_reqid(u_int8_t *macaddr, int interface)
{
unsigned char buf[32];
memset(buf, 0, sizeof(buf));
buf[0] = 5;
buf[1] = 0;
buf[2] = MOPRC_CMD_REQUESTID;
return send_message(buf, 7, interface, macaddr);
}
static int send_boot(u_int8_t *macaddr, int interface)
{
unsigned char buf[32];
memset(buf, 0, sizeof(buf));
buf[0] = 12;
buf[1] = 0;
buf[2] = MOPRC_CMD_BOOT;
buf[13] = 0xFF;
return send_message(buf, 14, interface, macaddr);
}
static int send_data(unsigned char *data, int len, u_int8_t *macaddr, int interface)
{
unsigned char buf[len+46];
static char message = 0;
memset(buf, 0, sizeof(buf));
buf[0] = len+2;
buf[1] = 0;
buf[2] = MOPRC_CMD_COMMANDPOLL;
buf[3] = message;
memcpy(buf+4, data, len);
message = 1-message;
return send_message(buf, len+4, interface, macaddr);
}
static void print_ascic(unsigned char *buf, int len)
{
int i;
for (i=0; i <len; i++)
{
if (isprint(buf[i]))
printf("%c", buf[i]);
else
printf(".");
}
printf("\n");
}
static int show_system_info(unsigned char *info, int len)
{
int index=0;
int functions = 0;
while (index < len)
{
int type = info[index] | info[index+1]<<8;
int infolen = info[index+2];
index += 3;
if (show_info || type == 2)
{
switch (type)
{
case 1: /* Maintenance version */
printf("Maintenance Version: %d.%d.%d\n",
info[index],info[index+1],info[index+2]);
break;
case 2: /* Functions */
functions = info[index];
break;
/* These are sent by terminal servers */
case 102:
printf("ROM version: ");
print_ascic(&info[index], infolen);
break;
case 103:
printf("S/W version: ");
print_ascic(&info[index], infolen);
break;
case 105:
printf("Node Name: ");
print_ascic(&info[index], infolen);
break;
case 106:
printf("Identification: ");
print_ascic(&info[index], infolen);
break;
/* Other stuff */
case 201:
printf("Operating System: ");
print_ascic(&info[index], infolen);
break;
case 202:
printf("Software Version: ");
print_ascic(&info[index], infolen);
break;
case 203:
printf("Node Name: ");
print_ascic(&info[index], infolen);
break;
default:
break;
}
index += infolen;
}
}
if (show_info) printf("\n");
return functions & 0x20; /* Do we do CCP? */
}
static int do_moprc(u_int8_t *macaddr, int interface, int convert_bs, int timeout)
{
enum {STARTING, CONNECTED} state=STARTING;
fd_set in;
unsigned char buf[1500];
struct timeval tv;
struct termios old_term;
struct termios new_term;
int len;
int last_msg_tag = 99; /* Dummy */
int status;
int waiting_ack;
int resends = 0;
int termfd = STDIN_FILENO;
int stdin_is_tty;
int last_packet_was_empty=1;
tcgetattr(termfd, &old_term);
new_term = old_term;
/* Set local terminal characteristics */
new_term.c_iflag &= ~BRKINT;
new_term.c_iflag |= IGNBRK | INLCR;
new_term.c_lflag &= ~ISIG;
new_term.c_cc[VMIN] = 1;
new_term.c_cc[VTIME] = 0;
new_term.c_lflag &= ~ICANON;
new_term.c_lflag &= ~(ECHO | ECHOCTL | ECHONL);
tcsetattr(termfd, TCSANOW, &new_term);
/* Send connect packets */
send_reserve(macaddr, interface);
send_reqid(macaddr, interface);
waiting_ack = 1;
/* Main loop */
FD_ZERO(&in);
FD_SET(mop_socket, &in);
tv.tv_sec = 5;
tv.tv_usec = 0;
stdin_is_tty = isatty(STDIN_FILENO);
/* Loop for input */
while ( (status = select(FD_SETSIZE, &in, NULL, NULL, &tv)) >= 0)
{
/* No response after 3 tries */
if (waiting_ack && resends >= 3)
{
printf("\nTarget does not respond\n");
break;
}
/* Waiting for an ACK but not got one. Resend the last
packet */
if (status == 0 && waiting_ack)
{
send_last_message(interface, macaddr);
resends++;
continue;
}
/* No data, poll for any input from the terminal server */
if (status == 0 && !waiting_ack)
{
unsigned char dummybuf[1];
send_data(dummybuf, 0, macaddr, interface);
waiting_ack = 1;
resends = 0;
}
/* Data from the terminal server */
if (FD_ISSET(mop_socket, &in))
{
int cmd;
int datalen;
len = readmop(buf, sizeof(buf));
if (len < 0)
{
perror("reading from Remote");
return -1;
}
if (len == 0) continue;
waiting_ack = 0;
cmd = buf[2];
datalen = buf[0] | buf[1] <<8;
/* Ignore duplicates */
if (last_msg_tag == buf[3])
continue;
last_msg_tag = buf[3];
switch (cmd)
{
case MOPRC_CMD_RESERVE:
case MOPRC_CMD_RELEASE:
case MOPRC_CMD_REQUESTID:
fprintf(stderr, "Got unsupported MOPRC function %d\n", cmd);
break;
/* Got some data to display */
case MOPRC_CMD_RESPONSE:
if (datalen > 2)
{
fwrite(buf+4, datalen-2, 1, stdout);
fflush(stdout);
last_packet_was_empty = 0;
}
else
{
last_packet_was_empty = 1;
}
break;
/* Response to our REQUESTID message means
we are connected */
case MOPRC_CMD_SYSTEMID:
if (state == STARTING)
{
if (show_system_info(&buf[6], datalen-1) == 0)
{
printf("target does not support remote console\n");
goto finished;
}
if (stdin_is_tty)
printf("Console connected (press CTRL/D when finished)\n");
state = CONNECTED;
}
break;
default:
fprintf(stderr, "Got unknown MOPRC function %d\n", cmd);
break;
}
}
/* Keyboard input */
if (FD_ISSET(STDIN_FILENO, &in))
{
int i;
len = read(STDIN_FILENO, buf, stdin_is_tty ? sizeof(buf) : 1);
if (len < 0)
{
perror("reading from stdin");
return -1;
}
if (len == 0) break;
/* Convert LF to CR & check for ^D */
for (i=0; i<len; i++)
{
if (buf[i] == 4) goto finished;
if (buf[i] == '\n') buf[i] = '\r';
if (convert_bs && buf[i] == '\b') buf[i] = 127;
}
waiting_ack = 1;
resends = 0;
send_data(buf, len, macaddr, interface);
}
/* Reset for another select */
tv.tv_usec = timeout;
tv.tv_sec = 0;
FD_ZERO(&in);
FD_SET(mop_socket, &in);
if (!waiting_ack && last_packet_was_empty) FD_SET(STDIN_FILENO, &in);
}
finished:
/* Send disconnect */
send_release(macaddr, interface);
tcsetattr(termfd, TCSANOW, &old_term);
printf("\n");
return 0;
}
syntax highlighted by Code2HTML, v. 0.9.1