#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <signal.h>
#ifdef HAS_SYSLOG
#include <syslog.h>
#endif
#include "getopt.h"
#include "lutil.h"
#include "xutil.h"
#include "ftn.h"
#include "nodelist.h"
#include "trap.h"
#include "config.h"
#include "version.h"
#include "needed.h"

#if defined(SHORT_PID_T)
#define pid_t short
#elif defined(INT_PID_T)
#define pid_t int
#endif

int master=0;
int forcedcalls=0;
char *forcedphone=NULL;
char *forcedline=NULL;
#if defined(HAS_TCP) || defined(HAS_TERM)
char *inetaddr=NULL;
int tcp_mode=0;
#endif
#ifdef NOISEDEBUG
int junklevel=0;
#endif

int call(faddr *);
fa_list *callall(void);
int answer(char *);
void mkdirs(char*);
void setargspace(char**,char**);
void setproctitle(char*);

void usage(void)
{
#ifdef NOISEDEBUG
#if defined(HAS_TCP) || defined(HAS_TERM)
	confusage("-r<role> -j<num> -a<inetaddr> -t<mode> <node> ...");
#else
	confusage("-r<role> -j<num> <node> ...");
#endif
	fprintf(stderr,_("-j<num>\t\tdamage every <num> byte\t[%d]\n"),
								junklevel);
#else
#if defined(HAS_TCP) || defined(HAS_TERM)
	confusage("-r<role> -a<inetaddr> -t<mode> <node> ...");
#else
	confusage("-r<role> <node> ...");
#endif
#endif
	fprintf(stderr,_("-r 0|1\t\t1 - master, 0 - slave\t[0]\n"));
	fprintf(stderr,_("-n<phone>\tforced phone number\n"));
	fprintf(stderr,_("-l<ttydevice>\tforced tty device\n"));
#if defined(HAS_TCP) || defined(HAS_TERM)
	fprintf(stderr,_("-a<inetaddr>\tuse TCP/IP instead of modem\n"));
	fprintf(stderr,_("-t<mode>\t0 - IFC, 1 - telnet\t[0]\n"));
#endif
	fprintf(stderr,_("  <node>\tshould be in domain form, e.g. f11.n22.z3\n"));
	fprintf(stderr,_("\t\t(this implies master mode)\n"));
	fprintf(stderr,_("\n or: %s tsync|yoohoo|**EMSI_INQC816\n"),myname);
	fprintf(stderr,_("\t\t(this implies slave mode)\n"));
}

int main(argc,argv,envp)
int argc;
char *argv[];
char *envp[];
{
	int c,uid;
	fa_list *callist=NULL,**tmpl;
	faddr *tmp;
	int rc,maxrc,callno=0,succno=0;
	char *answermode=NULL,*p;
#ifdef NEED_FORK
	pid_t child,waitret;
	int status;
#endif

	setargspace(argv,envp);

#if defined(HAS_SYSLOG) && defined(CICOLOG)
	logfacility=CICOLOG;
#endif

	setmyname(argv[0]);
	catch(myname);
#ifdef NOISEDEBUG
#if defined(HAS_TCP) || defined(HAS_TERM)
	while ((c=getopt(argc,argv,"j:x:r:n:l:a:t:I:h")) != -1)
#else
	while ((c=getopt(argc,argv,"j:x:r:n:l:I:h")) != -1)
#endif
#else
#if defined(HAS_TCP) || defined(HAS_TERM)
	while ((c=getopt(argc,argv,"x:r:n:l:a:t:I:h")) != -1)
#else
	while ((c=getopt(argc,argv,"x:r:n:l:I:h")) != -1)
#endif
#endif
	if (confopt(c,optarg)) switch (c)
	{
#ifdef NOISEDEBUG
	case 'j':	junklevel=atoi(optarg); break;
#endif
	case 'r':	master=atoi(optarg);
			if ((master != 0) && (master != 1))
			{
				usage();
				exit(1);
			}
			break;
	case 'l':	forcedline=xstrcpy(optarg); break;
#if defined(HAS_TCP) || defined(HAS_TERM)
	case 'a':	inetaddr=xstrcpy(optarg); break;
	case 't':	tcp_mode=atoi(optarg); break;
#endif
	case 'n':	forcedphone=xstrcpy(optarg); break;
	default:	usage(); exit(1);
	}

	if (readconfig())
	{
		fprintf(stderr,"Error getting configuration, aborting\n");
		exit(1);
	}

	maxpsize=0L; /* we want classic packet names here */

	tmpl=&callist;

	while (argv[optind])
	{
		for (p=argv[optind];(*p) && (*p == '*');p++);
		if ((strcasecmp(argv[optind],"tsync") == 0) ||
		    (strcasecmp(argv[optind],"yoohoo") == 0) ||
		    (strcasecmp(argv[optind],"binkp") == 0) ||
		    (strncasecmp(p,"EMSI_",5) == 0))
		{
			master=0;
			answermode=argv[optind];
			debug(10,"inbound \"%s\" mode",S(answermode));
		}
		else
		{
			debug(8,"callist entry \"%s\"",argv[optind]);
			if ((tmp=parsefaddr(argv[optind])))
			{
				*tmpl=(fa_list *)xmalloc(sizeof(fa_list));
				(*tmpl)->next=NULL;
				(*tmpl)->addr=tmp;
				tmpl=&((*tmpl)->next);
			}
			else logerr("unrecognizable address \"%s\"",argv[optind]);
		}
		optind++;
	}

	if (callist)
	{
		master=1;
		forcedcalls=1;
	}

	/*
	   The following witchkraft about uid-s is necessary to make
	   access() work right.  Unforunately, access() checks the real
	   uid, if ifcico is invoked with supervisor real uid (as when
	   called by uugetty) it returns X_OK for the magic files that
	   even do not have `x' bit set.  Therefore, `reference' magic
	   file requests are taken for `execute' requests (and the
	   actual execution natually fails).  Here we set real uid equal
	   to effective.  If real uid is not zero, all these fails, but
	   in this case it is not necessary anyway.
	*/

	uid=geteuid();
	seteuid(0);
	setuid(uid);
	seteuid(uid);
	debug(2,"uid=%d, euid=%d",getuid(),geteuid());

	umask(066); /* packets may contain confidential information */

#ifdef HAS_BSD_SIGNALS
	siginterrupt(SIGALRM,1);
	siginterrupt(SIGPIPE,1);
#endif

	p=xstrcpy(inbound);
	p=xstrcat(p,"/tmp/");
	mkdirs(p);
	free(p);

	maxrc=0;
	if (master)
	{

#ifdef NEED_FORK
		if ((child=fork()))
		{
			if (child == -1)
			{
				logerr("$fork() error");
				exit(1);
			}
			while (((waitret=wait(&status)) != -1) &&
			       (waitret != child))
			{
				logerr("wait return %d, status %d,%d",
					waitret,status>>8,status&0xff);
			}
			if (status&0xff) kill(getpid(),status&0xff);
			else exit(status>>8);
		}
#endif

		if (callist == NULL) callist=callall();
		for (tmpl=&callist;*tmpl;tmpl=&((*tmpl)->next))
		{
			callno++;
			rc=call((*tmpl)->addr);
			loginf("call to %s %s (rc=%d)",
				ascfnode((*tmpl)->addr,0x1f),
				rc?"failed":"successful",rc);
			if (rc > maxrc) maxrc=rc;
			if (rc == 0) succno++;
		}
	}
	else /* slave */
	{
#ifdef HAS_TCP
		if (!answermode && tcp_mode==TCPMODE_BINKP)
			answermode=xstrcpy("binkp");
#endif
		setproctitle("-ifcico answering");
		maxrc=answer(answermode);
		callno=1;
		succno=(maxrc == 0);
	}

	if (callno)
		loginf("%d of %d calls, maxrc=%d",succno,callno,maxrc);

	return maxrc;
}
