/*
#define RCS_ID $Id: pg.c,v 1.9 2001/01/20 19:12:22 oz6bl Exp $
 *   Copyright 1992, 1993, 1994 John Melton (G0ORX/N6LYT)
 *              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.
 *
 *   This program (in version 0.6) has been modified by Bent Bagger, OZ6BL
 *   (oz6bl@amsat.org) of the OZ7SAT group.
 *
 *   Modifications:
 *	No X11 support
 *      Uses curses for screen output
 *	Scans current directory and uploads all files with extension
 *		.pul and .out.
 *      Teminates when no data is heards with a configurable time
 *      Writes its actions to 'pg.log'
 *      Added a timer for wait for AOS if desired
 */


/*
	pg.c

	Pacsat Upload for Linux 

	The TNC must be setup for KISS protocol.

	John Melton
	G0ORX, N6LYT

	4 Charlwoods Close
	Copthorne
	West Sussex
	RH10 3QZ
	England

	INTERNET:	g0orx@amsat.org
			n6lyt@amsat.org
			john@images.demon.co.uk
			J.D.Melton@slh0613.icl.wins.co.uk

	History:	
		1.1:	Added more printout to signal_child in case of 
			abnormal termination (OZ6BL)
		1.2:	Added a timer 'wait for AOS' that allows to wait
			for an 'open' packet addressed to BBSAT before
			upload attempts are made
		1.3:	Corrected the reaction on receiving 
			ER_NO_SUCH_FILE_NUMBER on continued upload 
			(the slot in the satellite has expired)
		1.4:	Fixed an error that prevented PG from uploading 
			more than one *.out file per invocation.
			Made to compile with Debian 2.0 (glibc2)
		1.5:	Added check for length of 'satellite' parameter
			Exit if no AX.25 port configured
		1.6:	Changed to use one unified configuration file for all 
			satellites and common to PB and PG. The file is /etc/pbpg/pb.conf
			The callsign of the satellite (parameter Satellite) must be given,
			either on the command line (-s) or as an environment variable
			(SATELLITE)

*/

#define VERSION_STRING "version 1.6 by oz6bl"
#define USAGE_STRING "usage: pg [-s satellite] [-m mycall] [-n satname] \n"\
		     "          [-i maxidle] [-a maxWaitforAOS] [-v]\n"
#define WAITING 0
#define UPLOADING 1
#define FILE_DONE 2

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>
#include <dirent.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <dirent.h>
#include <sys/socket.h>
#include <signal.h>
#include <string.h>
#include <ctype.h>

#include <curses.h>
#include <netax25/ax25.h>
#include <netrose/rose.h>

#include "header.h"
#include "ftl0.h"
#include <netax25/axconfig.h>
#include <netax25/axlib.h>


char Satellite[16];	/* satellite call sign, e.g. HL01 */
char satName[16];	/* satellite name e.g. KO-23 */
char myCall[16];	/* ground station call sign */
char MaxIdle[16];	/* max time (seconds) waiting for 'open' - string */
int maxIdle;		/* ditto - integer */
char MaxWaitforAOS[16] = {'\0'}; /* max time (seconds) waiting for AOS */
int maxWaitforAOS;	/* ditto - integer */
int masterState ;
int maxFrame = 200 ;
int T1 = 2 ;
int satellite_heard = 0 ; 


FILE *logfp ;
int fd[2], monitorfd[2]; /* pipes for 'upload' and 'monitor' */
int uploadpid = 0, monitorpid = 0;

/* Screen control pointers */
WINDOW *scr_messages, *scr_status ;

extern void upload(char *);
extern void monitor(void);
	

void ReceivedStatus(void)
{
	int bytes;
	char msg[200];
	
	bytes = read(fd[0], msg, 200) ;
	mvwprintw (scr_status, 0, 0, "%s", msg) ;
	wclrtoeol (scr_status) ;
	wrefresh (scr_status) ;
}

void WaitOpen(void)
{
	int bytes;
	char msg[200];
	
	bytes = read(monitorfd[0], msg, 200);
	if ((strncmp ("Open", msg, 4) == 0) && (masterState == WAITING))
		masterState = UPLOADING ; 
	wprintw (scr_messages, "%s", msg);
	wrefresh (scr_messages) ;
}

void signal_child(int s)
{
	int pid, pstatus ;
	char msg [200] ;

	signal(SIGCHLD, signal_child);

	if ((pid = wait(&pstatus)) == uploadpid) {
		if ((pstatus & 0xFF) == 0) {
			/* normal termination */ 
			uploadpid = 0 ;
			sleep (1) ;
			if ((pstatus & 0xFF00) == 0) {
				/* normal return */
				masterState = FILE_DONE ; } 
			else {
				masterState = WAITING ; 
				mvwprintw (scr_status, 0, 0, "Status: Waiting...") ;
				wclrtoeol (scr_status) ;
				wrefresh (scr_status) ; } }
		else {
			/* not-normal termination */
			sprintf (msg, "Signal_child - Upload, status = %x", pstatus) ;
			perror (msg) ;
			endwin () ;
			exit (1) ; } }
	else if (pid == monitorpid) {
		if ((pstatus & 0xFF) == 0) {
			/* normal termination */ 
			endwin () ;
			exit (0) ; }
		else {
			/* not-normal termination */
                        sprintf (msg, "Signal_child - Monitor, status = %x", pstatus) ;
                        perror (msg) ;
                        endwin () ;
			exit (1) ; } }
}

int doUpload (char *fileName) {
	int n ;
	fd_set infd ;

	for (;;) {
		if (masterState == FILE_DONE) return 0 ;
		if ((masterState == UPLOADING) && (uploadpid == 0)) {
			/* start uplaod */
			if ((uploadpid = fork()) == 0) { 
				upload(fileName); } 
			else if (uploadpid == -1) {
				perror("Cannot fork upload");
				exit (1) ; } }
		FD_ZERO (&infd) ;
		if (masterState == UPLOADING) FD_SET (fd[0], &infd) ;
		FD_SET (monitorfd[0], &infd) ;
		n = select (32, &infd, NULL, NULL, NULL) ; 
		if (n < 0) {
			if (errno == EINTR) continue ; /* restart the operation */
			else {
				perror ("select") ;
				exit (1) ; } }
		if (FD_ISSET (fd[0], &infd)) ReceivedStatus () ;
		if (FD_ISSET (monitorfd[0], &infd)) WaitOpen () ;
	}
}

int Wait_AOS (void) {
	int retval ;
	fd_set fdset ;
	struct timeval timeout ;

	for (;;) {
		FD_ZERO (&fdset) ;
		FD_SET (monitorfd[0], &fdset) ;
		timeout.tv_sec = maxWaitforAOS ;
		timeout.tv_usec = 0 ;
		retval = select (16, &fdset, NULL, NULL, &timeout) ;
		if (retval < 0) {
			if (errno == EINTR) continue ; 
				/* restart the operation */
			else {
				perror ("Wait for AOS") ;
				exit (1) ; }}
		if (retval) {
			if (FD_ISSET(monitorfd[0], &fdset)) WaitOpen() ;
			if (masterState == UPLOADING) return (0) ; }
		else /* time out */ {
			mvwprintw (scr_status, 0, 0, "No AOS in %d seconds",
				   maxWaitforAOS) ;
			wclrtoeol (scr_status) ;
			wrefresh (scr_status) ;
			return (1) ; }
		}
}	

int main(int argc, char **argv)
{
	int within = 0; /* scan config. file: inside found satellite parameters */
	int	among  = 0; /* scan config. file: among specific satellite parameters */
	char *s, s1[100], s2[100], *device, *port ;
	char tmpMyCall[100], tmpMaxDays[100], tmpMaxIdle[100] ;
	char tmpMaxWaitforAOS[100] ;
	int size, n ;
	FILE *paramf ;
	struct dirent *pDirent ;
	DIR *pDir ;
	int i ;
	char target [][5] = {".pul", ".out"} ;

	/* Get the command line options but do not check them yet */

	while ((size = getopt(argc, argv, "s:m:n:i:a:v")) != -1) {
		switch (size) {
			case 's':
				strcpy(Satellite, optarg); break;
			case 'm':
				strcpy(myCall, optarg); break;
			case 'n':
				strcpy(satName, optarg); break;
			case 'i':
				strcpy(MaxIdle, optarg); break;
			case 'a':
				strcpy(MaxWaitforAOS, optarg); break;
			case 'v':
				printf("pg: %s\n", VERSION_STRING); return 1;
			case ':':
			case '?':
				fprintf(stderr, USAGE_STRING); return 1;
		}
	}
 
	/* Determine satellite name */
	if (Satellite [0] == '\0') { /* not on command line */
		if ((s = getenv("SATELLITE")) == NULL) {
			fprintf (stderr, "SATELLITE parameter is not defined\n");
			exit (1);
		} else
			strcpy( Satellite, s);
	}
		  
	/* Open and read the parameter file pb.conf */
	if ((paramf = fopen ("/etc/pbpg/pb.conf", "r")) == NULL) {
		perror ("pg - open pb.conf");
		exit (1);
	}
	n = 0;
	while (! feof (paramf)) {
		fscanf (paramf, "%100s %100s\n", s1, s2);
		n++;
		if (! strcasecmp (s1, "SATELLITE")) {
			if (!among) { among = 1;};
			if (!strcmp (Satellite, s2)) within = 1;
			else if (within) {
				break;}
			continue;
		}
		if (! strcmp (s1, "SATNAME")) {
			if (within && satName[0] == '\0') strcpy (satName, s2); continue;}
		if (! strcmp (s1, "MYCALL")) {
			if (!among || within) strcpy (tmpMyCall, s2); continue;}
		if (! strcmp (s1, "MAXDAYS")) {continue; /* PB parameter - ignore */}
		if (! strcmp (s1, "UDPPORT")) {continue; /* PB parameter - ignore */}
		if (! strcmp (s1, "KISSLOG")) {continue; /* PB parameter - ignore */}
		if (! strcmp (s1, "MAXIDLE")) {
			if (!among || within) strcpy (tmpMaxIdle, s2); continue;}
		if (! strcmp (s1, "MAXWAIT")) {
			if (!among || within) strcpy (tmpMaxWaitforAOS, s2); continue;}
		fprintf (stderr, "Error in config file line <%d>\n", n);
	}
	if (!within) {
		fprintf (stderr, "PG: Satellite %s not found, exiting\n", Satellite);
		exit (1);
	}

	/* Copy config. file values if not defined on the command line */
	if (myCall[0]  ==  '\0') strcpy (myCall, tmpMyCall);
	if (MaxIdle[0] ==  '\0') strcpy (MaxIdle, tmpMaxIdle);
	if (MaxWaitforAOS[0] ==  '\0') strcpy (MaxWaitforAOS, tmpMaxWaitforAOS);

	/*  Check if the needed parameters exist */
	if (strlen(Satellite) > 6 ) {
		printf ("SATELLITE parameter is ill-formed\n");
		return 1; 
	}
	if (myCall [0] == '\0') {
		printf ("MYCALL parameter is not defined\n");
		return 1;
	}
        
	/* Convert string values to numbers */
	if (MaxIdle [0] != '\0') {
		maxIdle = atoi(MaxIdle);
		if (maxIdle <= 0) maxIdle = 60 ; /* seconds */
	}
	if (MaxWaitforAOS [0] != '\0')
		maxWaitforAOS  = atoi(MaxWaitforAOS) ;
	if (maxWaitforAOS == 0) satellite_heard = 1 ;
	/* do not wait for 'open' */

	if ((argc - optind) != 0) {
		fprintf(stderr, USAGE_STRING);
		return 1;
	}
	
	if ((logfp = fopen ("pg.log", "a")) == NULL) {
		perror ("pg: pg.log") ;
		return 1 ; }

	signal(SIGCHLD, signal_child) ;

	strcat(Satellite, "-12") ;
	if (ax25_config_load_ports () == 0) {
		fprintf (stderr, "pg: no AX.25 ports configured\n") ;
		exit (1) ; }
	if (ax25_aton_entry (myCall, s1) == -1) {
		fprintf (stderr, "pg: convert mycall\n") ;
		exit (1) ; }
	if ((port = ax25_config_get_port ((ax25_address *)s1)) == NULL) {
		fprintf (stderr, "pg: get port\n") ;
		exit (1) ; }
	if ((device = ax25_config_get_dev (port)) == NULL) {
		fprintf (stderr, "pg: get device\n") ;
		exit (1) ; } 

	/* Initialize the curses screen management */
	initscr() ;
	cbreak() ;
	noecho() ;
	start_color() ;
	init_pair(1, COLOR_WHITE, COLOR_BLUE) ;

	/* Create the windows */
	scr_messages = newwin (23, 80,  0, 0) ; /* for messages */
	scr_status   = newwin ( 2, 80, 23, 0) ; /* for status etc. */
	scrollok (scr_messages, TRUE) ;

	/* Titles in the status window */
	wbkgdset (scr_status, COLOR_PAIR (1)) ; /* white on blue */
	mvwprintw (scr_status, 1, 0, "%30s | Iface: %s | Sat: %s | Call: %s",
				VERSION_STRING, device, satName, myCall) ;
	wclrtoeol (scr_status) ;
	wrefresh (scr_status) ;

	/* Define the pipes to 'upload' and 'monitor' */
	pipe(fd) ;
	pipe(monitorfd);

	/*Start the monitor process */
	if ((monitorpid = fork()) == 0) {
		/* first child process */
		monitor() ; }
	else if (monitorpid == -1) {
		perror("Cannot fork monitor");
		exit (1) ; }

	/* walk through the current directory */
	for (i = 0 ; i < 2 ; i++ ) {
		/* open the directory */
		pDir = opendir (".") ;
		while (( pDirent = readdir (pDir)) != NULL) {
			if (strstr (pDirent->d_name, target[i])) {
				if (!satellite_heard) {
					if (Wait_AOS() == 0) { satellite_heard = 1 ; }
					else /* time out waiting for AOS */ {
						endwin () ;
						return (0) ; } }
				masterState = UPLOADING ;
				doUpload (pDirent->d_name) ;
				rewinddir (pDir) ; } }
		closedir (pDir) ;
	}
	endwin () ;
	printf ("PG all done\n") ;
	return 0 ;
}
