/*
 * gcc -Wall -O2 xctrld.c -o /sbin/xctrld
 *
 * --- T2-COPYRIGHT-NOTE-BEGIN ---
 * This copyright note is auto-generated by ./scripts/Create-CopyPatch.
 * 
 * T2 SDE: misc/archive/xctrld.c
 * Copyright (C) 2004 - 2005 The T2 SDE Project
 * Copyright (C) 1998 - 2003 ROCK Linux Project
 * 
 * More information can be found in the files COPYING and README.
 * 
 * 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; version 2 of the License. A copy of the
 * GNU General Public License can be found in the file COPYING.
 * --- T2-COPYRIGHT-NOTE-END ---
 */

#include <fcntl.h>
#include <netinet/in.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <errno.h> 

#define PORT 2225

#define BUFSIZE 1024

FILE * out = NULL;

#define xprf(format, args... ) fprintf(out,"Cliffords Control-Daemon: " format "\r", ## args)

/*  This function is very dirty - but it seams to to the only way that
 *  works without using pty devices. making the netfd directly the stdin
 *  stdout and stderr channels of /bin/sh caused a lot of problems ...
 */
void do_session(int netfd) {
	int p_in[2],p_out[2];
	int rc,maxfd,pid,c;
	struct timeval tv;
	char buf[BUFSIZE];
	fd_set rfds;
	
	snprintf(buf,BUFSIZE,"\n"
"Hello! This is a mini telnet daemon which is not using a pty device.\n"
"So you wont have job control or other things where ptys are needed.\n"
"\nThis session has the PID %d. Have fun.\n\n",(int)getpid());
	write(netfd,buf,strlen(buf));
	
	pipe(p_in); pipe(p_out);
	if (signal(SIGCHLD,SIG_DFL) == SIG_ERR) xprf("signal: %s",strerror(errno));
	
	if ( (pid=fork()) == 0 ) {
		close(0); dup2(p_in[0],0);
		close(1); dup2(p_out[1],1);
		close(2); dup2(p_out[1],2);
		close(p_in[0]);  close(p_in[1]);
		close(p_out[0]); close(p_out[1]); close(netfd);
		setenv("USER","root",1);    setenv("HOME","/root",1);
		setenv("LOGNAME","root",1); setenv("TERM","linux",1);
		execl("/bin/sh","/bin/sh","--login","-i",NULL);
		xprf("execl: %s",strerror(errno)); exit(1);
	}
	
	close(p_in[0]); close(p_out[1]);
	
	while (waitpid(pid,NULL,WNOHANG) != pid) {
		FD_ZERO(&rfds); FD_SET(netfd,&rfds); FD_SET(p_out[0],&rfds);
		maxfd = netfd>p_out[0] ? netfd : p_out[0];
		tv.tv_sec=1; tv.tv_usec=0;
		rc=select(maxfd+1, &rfds, NULL, NULL, NULL);
		if (rc == -1) { xprf("select: %s",strerror(errno)); return; }
		if (FD_ISSET(netfd, &rfds)) {
			rc=read(netfd,buf,BUFSIZE);
			for (c=0; c<rc; c++)
				if (buf[c]!='\r') write(p_in[1],buf+c,1);
		}
		if (FD_ISSET(p_out[0], &rfds)) {
			rc=read(p_out[0],buf,BUFSIZE);
			for (c=0; c<rc; c+=write(netfd,buf,rc)) ;
		}
	}
}

int main(int argc, char ** argv) {
	struct sockaddr_in addr;
	int listenfd,fd;
	
	if (argc != 2 || argv[1][0] == '-') {
		fprintf(stderr,"Usage: %s < log-file | tty-device >\n",argv[0]);
		return 1;
	}
	
	if ( (out=fopen(argv[1],"a+")) == NULL ) {
		fprintf(stderr,"Can't open log file '%s': %s",argv[1],strerror(errno));
		return 1;
	}
	setvbuf(out,NULL,_IONBF,0);
	chdir("/");

	printf("Cliffords Remote Control Daemon starting in 3 seconds ...\n");
	if (fork()) return 0;
	sleep(3);

	fprintf(out,"\n\n\r");
	xprf("Binding port %d and waiting for connections ...\n",PORT);
	if (signal(SIGCHLD,SIG_IGN) == SIG_ERR) xprf("signal: %s",strerror(errno));
	
	if ((listenfd=socket(AF_INET,SOCK_STREAM,0)) == -1) xprf("socket: %s",strerror(errno));
	
	bzero(&addr,sizeof(addr));
	addr.sin_family=AF_INET;
	addr.sin_addr.s_addr = htonl(INADDR_ANY);
	addr.sin_port = htons(PORT);

	if (bind(listenfd,&addr,sizeof(addr)) == -1) xprf("bind: %s",strerror(errno));
	
	if (listen(listenfd,5)==-1) xprf("listen: %s",strerror(errno));
	
	while (1) {
		if ((fd=accept(listenfd,NULL,NULL)) == -1) xprf("accept: %s",strerror(errno));
		if (!fork()) {
			xprf("connection %d opened.\n",(int)getpid());
			do_session(fd);
			xprf("connection %d closed.\n",(int)getpid());
			return 0;
		} else close(fd);
	}
	return 0;
}
