/* Main-level NOS program:
 *  initialization
 *  keyboard processing
 *  generic user commands
 *
 * Copyright 1991 Phil Karn, KA9Q
 */
#include <stdio.h>
#include <time.h>
#if	defined(__TURBOC__) && defined(MSDOS)
#include <io.h>
#include <conio.h>
#endif
#include "global.h"
#ifdef	ANSIPROTO
#include <stdarg.h>
#endif
#include "files.h"
#include "mbuf.h"
#include "socket.h"
#include "iface.h"
#include "ftpcli.h"
#include "telnet.h"
#include "remote.h"
#include "session.h"
#include "cmdparse.h"
#include "ax25.h"
#include "kiss.h"
#include "enet.h"
#include "timer.h"
#include "proc.h"
#include "tty.h"
#include "daemon.h"
#include "usock.h"
#include "netrom.h"
#include "ip.h"
#include "tcp.h"
#include "udp.h"
#include "hardware.h"
#include "commands.h"
#include "domain.h"
#include "trace.h"

extern struct cmds Cmds[],Startcmds[],Stopcmds[],Attab[];

#ifndef	MSDOS			/* PC uses F-10 key always */
static char Escape = 0x1d;	/* default escape character is ^] */
#endif

char Badhost[] = "Unknown host %s\n";
char *Hostname;
static char Prompt[] = "net> ";
char Nospace[] = "No space!!\n";	/* Generic malloc fail message */
static FILE *Logfp;
struct proc *Cmdpp;
struct proc *Display;
struct session *Command;
static time_t StartTime;		/* time that NOS was started */
int main_exit = FALSE;			/* from main program (flag) */
static int Verbose;

int
main(argc,argv)
int argc;
char *argv[];
{
	char *inbuf,*intmp;
	FILE *fp;
	struct daemon *tp;
	struct mbuf *bp;
	int c;

	StartTime = time(&StartTime);

	while((c = getopt(argc,argv,"s:d:bv")) != EOF){
		switch(c){
		case 's':	/* Number of sockets */
			Nusock = atoi(optarg);
			break;
		case 'd':	/* Root directory for various files */
			initroot(optarg);
			break;
#ifdef	__TURBOC__
		case 'b':	/* Use BIOS for screen output */
			directvideo = 0;
			break;
#endif
		case 'v':
			Verbose = 1;
			break;
		}
	}
	kinit();
	ipinit();
	ioinit();
	sockinit();
	Cmdpp = mainproc("cmdintrp");

	Sessions = (struct session *)callocw(Nsessions,sizeof(struct session));
	Command = Lastcurr = newsession("command interpreter",COMMAND);
	Display = newproc("display",250,display,0,NULLCHAR,NULL,0);
	tprintf("KA9Q NOS version %s\n",Version);
	tprintf("Copyright 1990 by Phil Karn, KA9Q\n");
	rflush();

	/* Start background Daemons */
	for(tp=Daemons;;tp++){
		if(tp->name == NULLCHAR)
			break;
		newproc(tp->name,tp->stksize,tp->fp,0,NULLCHAR,NULL,0);
	}

	if(optind < argc){
		/* Read startup file named on command line */
		if((fp = fopen(argv[optind],READ_TEXT)) == NULLFILE)
			tprintf("Can't read config file %s: %s\n",
			 argv[optind],sys_errlist[errno]);
	} else {
		fp = fopen(Startup,READ_TEXT);
	}
	if(fp != NULLFILE){
		inbuf = mallocw(BUFSIZ);
		intmp = mallocw(BUFSIZ);
		while(fgets(inbuf,BUFSIZ,fp) != NULLCHAR){
			strcpy(intmp,inbuf);
			if(Verbose)
				tprintf("%s",intmp);
			if(cmdparse(Cmds,inbuf,NULL) != 0){
				tprintf("input line: %s",intmp);
			}
		}
		fclose(fp);
		free(inbuf);
		free(intmp);
	}
	/* Now loop forever, processing commands */
	for(;;){
		tprintf(Prompt);
		usflush(Command->output);
		if(recv_mbuf(Command->input,&bp,0,NULLCHAR,0) != -1){
			(void)cmdparse(Cmds,bp->data,Lastcurr);
			free_p(bp);
		}
	}
}
/* Keyboard input process */
void
keyboard(i,v1,v2)
int i;
void *v1;
void *v2;
{
	int c;
	struct mbuf *bp;

	/* Keyboard process loop */
	for(;;){
		c = kbread();
#ifndef	MSDOS
		if(c == Escape && Escape != 0)
			c = -2;
#endif
		if(c == -2 && Current != Command){
			/* Save current tty mode and set cooked */
			swapscreen(Current,Command);
			Lastcurr = Current;
			Current = Command;
		}
		Current->row = MOREROWS;
		psignal(&Current->row,1);
		if(c >= 0){
			/* If the screen driver was in morewait state, this char
			 * has woken him up. Toss it so it doesn't also get taken
			 * as normal input. If the char was a command escape,
			 * however, it will be accepted; this gives the user
			 * a way out of lengthy output.
			 */
			if(!Current->morewait
			 && (bp = ttydriv(Current,(char)c)) != NULLBUF){
				send_mbuf(Current->input,bp,0,NULLCHAR,0);
			}
		}
	}
}
/* Standard commands called from main */
int
dodelete(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	int i;

	for(i=1;i < argc; i++){
		if(unlink(argv[i]) == -1){
			tprintf("Can't delete %s: %s\n",
			 argv[i],sys_errlist[errno]);
		}
	}
	return 0;
}
int
dorename(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	if(rename(argv[1],argv[2]) == -1)
		tprintf("Can't rename: %s\n",sys_errlist[errno]);
	return 0;
}
int
doexit(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	int i;
	time_t StopTime;

	StopTime = time(&StopTime);
	main_exit = FALSE;	/* let everyone know we're out of here */
	reset_all();
	if(Dfile_updater != NULLPROC)
		alert(Dfile_updater,0);	/* don't wait for timeout */
	for(i=0;i<100;i++)
		pwait(NULL);	/* Allow tasks to complete */
        shuttrace();
	log(-1,"NOS was stopped at %s", ctime(&StopTime));
	if(Logfp){
		fclose(Logfp);
		Logfp = NULLFILE;
	}
	iostop();
	exit(0);
	return 0;	/* To satisfy lint */
}
int
dohostname(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	if(argc < 2)
		tprintf("%s\n",Hostname);
	else {
		struct iface *ifp;
		char *name;

                if((ifp = if_lookup(argv[1])) != NULLIF){
			if((name = resolve_a(ifp->addr, FALSE)) == NULLCHAR){
				tprintf("Interface address not resolved\n");
				return 1;
			} else {
				if(Hostname != NULLCHAR)
					free(Hostname);
				Hostname = name;
				tprintf("Hostname set to %s\n", name );
			}
		} else {
			if(Hostname != NULLCHAR)
				free(Hostname);
			Hostname = strdup(argv[1]);
		}
	}
	return 0;
}
int
dolog(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	static char *logname;

	if(argc < 2){
		if(Logfp)
			tprintf("Logging to %s\n",logname);
		else
			tprintf("Logging off\n");
		return 0;
	}
	if(Logfp){
		log(-1,"NOS log closed");
		fclose(Logfp);
		Logfp = NULLFILE;
		free(logname);
		logname = NULLCHAR;
	}
	if(strcmp(argv[1],"stop") != 0){
		logname = strdup(argv[1]);
		Logfp = fopen(logname,APPEND_TEXT);
		log(-1,"NOS was started at %s", ctime(&StartTime));
#ifdef MSDOS
		log(-1,"NOS load information: CS=0x%04x DS=0x%04x", _CS, _DS);
#endif
	}
	return 0;
}
int
dohelp(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	register struct cmds *cmdp;
	int i;
	char buf[66];

	tprintf("Main commands:\n");
	memset(buf,' ',sizeof(buf));
	buf[64] = '\n';
	buf[65] = '\0';
	for(i=0,cmdp = Cmds;cmdp->name != NULL;cmdp++,i = (i+1)%4){
		strncpy(&buf[i*16],cmdp->name,strlen(cmdp->name));
		if(i == 3){
			tprintf(buf);
			memset(buf,' ',sizeof(buf));
			buf[64] = '\n';
			buf[65] = '\0';
		}
	}
	if(i != 0)
		tprintf(buf);
	return 0;
}
/* Attach an interface
 * Syntax: attach <hw type> <I/O address> <vector> <mode> <label> <bufsize> [<speed>]
 */
int
doattach(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	return subcmd(Attab,argc,argv,p);
}
/* Manipulate I/O device parameters */
int
doparam(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	register struct iface *ifp;

	if((ifp = if_lookup(argv[1])) == NULLIF){
		tprintf("Interface \"%s\" unknown\n",argv[1]);
		return 1;
	}
	if(ifp->ioctl == NULLFP){
		tprintf("Not supported\n");
		return 1;
	}
	/* Pass rest of args to device-specific code */
	return (*ifp->ioctl)(ifp,argc-2,argv+2);
}

/* Display or set IP interface control flags */
int
domode(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	register struct iface *ifp;

	if((ifp = if_lookup(argv[1])) == NULLIF){
		tprintf("Interface \"%s\" unknown\n",argv[1]);
		return 1;
	}
	if(argc < 3){
		tprintf("%s: %s\n",ifp->name,
		 (ifp->flags & CONNECT_MODE) ? "VC mode" : "Datagram mode");
		return 0;
	}
	switch(argv[2][0]){
	case 'v':
	case 'c':
	case 'V':
	case 'C':
		ifp->flags = CONNECT_MODE;
		break;
	case 'd':
	case 'D':
		ifp->flags = DATAGRAM_MODE;
		break;
	default:
		tprintf("Usage: %s [vc | datagram]\n",argv[0]);
		return 1;
	}
	return 0;
}

#ifndef	MSDOS
int
doescape(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	if(argc < 2)
		tprintf("0x%x\n",Escape);
	else 
		Escape = *argv[1];
	return 0;
}
#endif	MSDOS
/* Generate system command packet. Synopsis:
 * remote [-p port#] [-k key] [-a hostname] <hostname> reset|exit|kickme
 */
int
doremote(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	struct sockaddr_in fsock;
	int s,c;
	char *data,x;
	int16 port,len;
	char *key = NULLCHAR;
	int klen;
	int32 addr = 0;
	char *cmd,*host;

	port = IPPORT_REMOTE;	/* Set default */
	optind = 1;		/* reinit getopt() */
	while((c = getopt(argc,argv,"a:p:k:s:")) != EOF){
		switch(c){
		case 'a':
			addr = resolve(optarg);
			break;
		case 'p':
			port = atoi(optarg);
			break;
		case 'k':
			key = optarg;
			klen = strlen(key);
			break;
		case 's':
			Rempass = strdup(optarg);
			return 0;	/* Only set local password */
		}
	}
	if(optind > argc - 2){
		tprintf("Insufficient args\n");
		return -1;
	}
	host = argv[optind++];
	cmd = argv[optind];
	if((s = socket(AF_INET,SOCK_DGRAM,0)) == -1){
		tprintf("socket failed\n");
		return 1;
	}
	len = 1;
	/* Did the user include a password or kickme target? */
	if(addr != 0)
		len += sizeof(int32);

	if(key != NULLCHAR)
		len += klen;

	if(len == 1)
		data = &x;
	else
		data = mallocw(len);

	fsock.sin_family = AF_INET;
	fsock.sin_addr.s_addr = resolve(host);
	fsock.sin_port = port;

	switch(cmd[0]){
	case 'r':
		data[0] = SYS_RESET;
		if(key != NULLCHAR)
			strncpy(&data[1],key,klen);
		break;
	case 'e':
		data[0] = SYS_EXIT;
		if(key != NULLCHAR)
			strncpy(&data[1],key,klen);
		break;
	case 'k':
		data[0] = KICK_ME;
		if(addr != 0)
			put32(&data[1],addr);
		break;
	default:
		tprintf("Unknown command %s\n",cmd);
		goto cleanup;
	}
	/* Form the command packet and send it */
	if(sendto(s,data,len,0,(char *)&fsock,sizeof(fsock)) == -1){
		tprintf("sendto failed: %s\n",sys_errlist[errno]);
		goto cleanup;
	}
cleanup:
	if(data != &x)
		free(data);
	close_s(s);
	return 0;
}
int
domore(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	struct session *sp;
	FILE *fp;
	char buf[81];
	int row;

	if((sp = newsession(argv[1],MORE)) == NULLSESSION){
		return -1;
	}
	/* Put tty into raw mode so single-char responses will work */
	sp->ttystate.echo = sp->ttystate.edit = 0;
	if((fp = fopen(argv[1],READ_TEXT)) == NULLFILE){
		tprintf("Can't read %s\n",argv[1]);
		keywait(NULLCHAR,1);
		freesession(sp);
		return 1;
	}
	row = MOREROWS;
	while(fgets(buf,sizeof(buf),fp),!feof(fp)){
		tprintf("%s",buf);
		if(--row == 0){
			row = keywait("--More--",0);
			switch(row){
			case -1:
			case 'q':
			case 'Q':
				goto done;
			case '\n':
			case '\r':
				row = 1;
				break;
			case ' ':
			default:
				row = MOREROWS;
			}
		}
	}
done:	fclose(fp);
	keywait(NULLCHAR,1);
	freesession(sp);
	return 0;
}
/* No-op command */
int
donothing(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	return 0;
}

/* Log messages of the form
 * Tue Jan 31 00:00:00 1987 44.64.0.7:1003 open FTP
 */
#if	defined(ANSIPROTO)
void
log(int s,char *fmt, ...)
{
	va_list ap;
	char *cp;
	long t;
	int i;
	struct sockaddr fsocket;
#ifdef	MSDOS
	int fd;
#endif

	if(Logfp == NULLFILE)
		return;

	time(&t);
	cp = ctime(&t);
	rip(cp);
	i = SOCKSIZE;
	fprintf(Logfp,"%s",cp);
	if(getpeername(s,(char *)&fsocket,&i) != -1)
		fprintf(Logfp," %s",psocket(&fsocket));

	fprintf(Logfp," - ");
	va_start(ap,fmt);
	vfprintf(Logfp,fmt,ap);
	va_end(ap);
	fprintf(Logfp,"\n");
	fflush(Logfp);
#ifdef	MSDOS
	/* MS-DOS doesn't really flush files until they're closed */
	fd = fileno(Logfp);
	if((fd = dup(fd)) != -1)
		close(fd);
#endif

}
#else
/*VARARGS2*/
void
log(s,fmt,arg1,arg2,arg3,arg4,arg5)
int s;
char *fmt;
int arg1,arg2,arg3,arg4,arg5;
{
	char *cp;
	long t;
	int fd,i;
	struct sockaddr fsocket;

	if(Logfp == NULLFILE)
		return;
	time(&t);
	cp = ctime(&t);
	rip(cp);
	i = SOCKSIZE;
	fprintf(Logfp,"%s",cp);
	if(getpeername(s,(char *)&fsocket,&i) != -1)
		fprintf(Logfp," %s",psocket(&fsocket));

	fprintf(Logfp," - ");
	fprintf(Logfp,fmt,arg1,arg2,arg3,arg4,arg5);
	fprintf(Logfp,"\n");
	fflush(Logfp);
#ifdef	MSDOS
	/* MS-DOS doesn't really flush files until they're closed */
	fd = fileno(Logfp);
	if((fd = dup(fd)) != -1)
		close(fd);
#endif
}
#endif

