/*
 *   Copyright 1997 Bent Bagger (OZ6BL)
 *              All Rights Reserved
 *
 *   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.
 *
 */

#define RCS_ID $Id: monitor.c,v 1.8 2001/01/20 04:30:47 oz6bl Exp $

/*
	monitor.c

	Pacsat Upload for Linux

	This program monitors the downlink from the satellite
	All packets addresses to 'BBSTAT' or 'mycall' are sent to the
	parent process ('pg') via 'monitorpipe'.

	The program terminates when time 'maxwait' has elapsed without
	hearing any packets.

	Bent Bagger, OZ6BL
	Bregnerodvej 151
	DK-3460 Birkerod

	e-mail:	oz6bl@amsat.org

	History:

	0.1	Cloned from upload.c

	0.2	Made to compile with Debian 2.0 (glibc2)
		Removed some type inconsistencies

	0.3	Changed the (obsolete) use of PF_INET, SOCK_PACKET to
		PF_PACKET, SOCK_PACKET
*/

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <dirent.h>
#include <sys/socket.h>
#include <features.h>    /* for the glibc version number */
#if __GLIBC__ >= 2 && __GLIBC_MINOR >= 1
#include <netpacket/packet.h>
#include <net/ethernet.h>     /* the L2 protocols */
#else
#include <asm/types.h>
#include <linux/if_packet.h>
#include <linux/if_ether.h>   /* The L2 protocols */
#endif
#include <netax25/ax25.h>
#include <netrose/rose.h>
#include <netinet/in.h>
#include <signal.h>
#include <ctype.h>
#include <netax25/axconfig.h>
#include <netax25/axlib.h>

extern void socket_error(char *, int) ;
extern int writen(int, char *, int) ;
extern void error(char *) ;
extern char satellite[];
extern char myCall[];
extern int maxIdle;

#define MAXBUFFER 512

int monitorfd[];
int s;
unsigned char buffer[MAXBUFFER];
	
char writebuf[1024];

/*
 *	Convert a call from the shifted ascii form used in an
 *	AX.25 packet.
 */
int ConvertCall(unsigned char *c, unsigned char *call) {
	unsigned char *ep = c + 6;
	int ct = 0;

	while (ct < 6) {
		if (((*c >> 1) & 127) == ' ') break;
		*call = (*c >> 1) & 127;
		call++;
		ct++;
		c++; }
	if ((*ep & 0x1E) != 0) {	
		*call = '-';
		call++;
		call += sprintf((char *)call, "%d", (int)(((*ep) >> 1) & 0x0F)); }
	*call = '\0';
	if (*ep & 1) return 0;
	return 1;
}

void send_to_PG(char *msg) {
	writen(monitorfd[1], msg, strlen(msg) + 1);
}

/*
 *	Input function when data is received on the input stream
 */
void GetFrame(void) {
	int n=1;
	int via, bufSize;
	unsigned char protocol;
	struct sockaddr sa;
	int asize = sizeof(sa);
	unsigned char toCall[10];
	unsigned char fromCall[10];
	unsigned char viaCall[10];
	char writebuf[128];

	asize = sizeof(sa) ;

	if ((bufSize = recvfrom (s, buffer, sizeof (buffer), 0, &sa, &asize)) == -1) {
		perror ("recv") ;
		return ; }
	/* Process the received frame */

	/* Check for runts/pygmygs */
	if (bufSize < 15) {
		char	st[10];
		int	x;
		sprintf (writebuf, "Runt:");
		for (x = 0; x < bufSize ; x++) {
			sprintf (st, " %02x", buffer [x]);
			strcat (writebuf, st);
		}
		send_to_PG (writebuf);
		return;
	}
	
	/* decode the to/from address */
	/* dont expect via address, but saves last if any */
	via = ConvertCall(buffer + n, toCall);
	n += 7;

	via = ConvertCall(buffer+ n, fromCall);
	n += 7;

	while (via)
	{
		via = ConvertCall(buffer + n, viaCall);
		n += 7;
	}

	/* check for a UI frame */
	if ((buffer[n] & 0xEF) != 0003) {
		/* Ignore non UI frames */
		return; }

	n++;
	protocol = buffer[n++];

	if (strcmp ((char *)toCall, "BBSTAT") == 0) {
		buffer [bufSize - 1] = '\0';
		sprintf (writebuf, "%s\n", buffer + n);
		send_to_PG (writebuf); }
	else if (strcmp ((char *)toCall, myCall) == 0) {
		/* This packet is sent directly to ME */
		buffer[bufSize - 1] = '\0';
		sprintf  (writebuf, "%s\n", buffer + n);
		send_to_PG (writebuf); }
	     else { ; }
}

void monitor(void) {
	int opt ;
	int addr_len ;
	struct full_sockaddr_ax25 addr ;
	int retval ;
	fd_set rx ;
	struct timeval timeout ;

	if ((s = socket(PF_PACKET, SOCK_PACKET, htons(ETH_P_AX25))) < 0) {
		socket_error("Socket open failed", errno); }
#ifdef DEBUG
	opt = 1;
	if (setsockopt(s, SOL_SOCKET, SO_DEBUG, &opt, sizeof(opt)) == -1)
		socket_error("setsockopt (SO_DEBUG) failed", errno);
#endif
	addr_len = ax25_aton(myCall, &addr);

	for (;;)
	{
		FD_ZERO(&rx);
		FD_SET(s, &rx);
		timeout.tv_sec = maxIdle ;
		timeout.tv_usec = 0 ;
		retval = select(s + 1, &rx, NULL, NULL, &timeout);
		if (retval) {
			if (FD_ISSET(s, &rx)) GetFrame(); }
		else /* time out */ {
			sprintf (writebuf, "No data in %d seconds", maxIdle) ;
			send_to_PG (writebuf) ;
			sleep (1) ;
			exit (0) ; }
		
	}
}

