/*
 * Cannibalized from data_xp.c
 *
 * V1.0  12 Jun 98  PSz
 * V1.1  10 Sep 99  PSz  Make timestamp Y2K compliant
 *
 * Companion to data_xp: receive data sent by data_xp, and send it on to
 * the parallel (as yet may not be possible to use serial) port: make an
 * AlphaStation do the work of an X-terminal.
 *
 * This program should be started from inetd.conf, e.g. with a line:
 *  ##prosd	stream	tcp	nowait	prosd	/usr/local/lib/axis/data_wr	data_wr -dev /dev/lp0
 *  ## BEWARE of permissions (ACLs) on /usr/sbin/tcpd , /etc/hosts.allow
 *  ## BEWARE that tcpd can find the data_wr binary (symlink from /usr/sbin)
 *  prosd	stream	tcp	nowait	prosd	/usr/sbin/tcpd		data_wr -dev /dev/lp0
 * While inetd will happily start other copies, subsequent ones will fail
 * with errno=16, "Device busy". You also need something like
 *  data_wr : prosd(.prosd;84.84)@rome.maths.usyd.edu.au : rfc931
 * in file /etc/hosts.allow, and
 *  prosd           88/tcp          # SMS local
 * in file /etc/services.
 *
 * We do not listen to printer status: Apple LaserWriters do not seem to
 * talk back on the parallel port. But mainly, we do not know how to open
 * the /dev/lp0 device so as to write and read back: open with O_RDONLY
 * (or O_RDWR ?) get errno=6, "No such device or address"; we use O_WRONLY.
 */


#define BUFSIZE 4096
#define IDLE_DEFAULT 15


#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>

extern time_t time();
extern int errno;
extern struct tm *localtime();

char *progname;


usage()
{
    printf("Usage: %s\n",progname);
    printf(" -dev <string>   name of printer (output) device. REQUIRED\n");
    printf(" -idle <secs>    seconds to wait for printer status output, default %d\n", IDLE_DEFAULT);
    printf("      Consider the speed of the communication line when setting -idle:\n");
    printf("      you will get repeated 'Have data to send, waiting for printer' messages\n");
    printf("      unless all of the buffer of size [%d] can be sent within IDLE.\n", BUFSIZE);
    printf(" -debug <int>    debug level (bit pattern)\n");
    printf("     dec   hex   oct    messages about\n");
    printf("       1     1     1    open/close files, sockets\n");
    printf("       2     2     2    UNUSED\n");
    printf("       4     4     4    select call output\n");
    printf("       8     8    10    read from printer\n");
    printf("      16    10    20    write to printer\n");
    printf("      32    20    40    read from network\n");
    printf("      64    40   100    repeated errors\n");
    printf("ABORTING\n");
    exit(1);
}


main (argc, argv)
    int		argc;
    char	**argv;
{
    struct sockaddr_in inaddr;
    struct hostent *hp;
    struct linger net_linger;
    struct timeval timeout;
    fd_set rfds, wfds, efds;

    unsigned char rbuf[BUFSIZE];
    unsigned char wbuf[BUFSIZE];

    int stat, i;

    int dfd = 0;
    int sfd = 0;	/* inetd gives us input on fd=0 (stdin) and allows output on fd=1 (stdout) */

    int cflg = -2;
    int pflg = -2;
    int rlen = 0;
    int rflg = -2;
    int wlen = 0;
    int wpos = 0;
    int wtot = 0;
    int wflg = -2;

    int idle = IDLE_DEFAULT;	/* seconds to wait before timing out */

    int debug = 0;


    stamp("Daemon started");


    progname = argv[0];

    if (argc < 1) {
	usage();
    }


    for (i = 1; i < argc; i++) {
	if (!strcmp(argv[i], "-dev")) {
	    if (++i >= argc) usage();
	    if (debug&0x01) stamp("About to open device %s", argv[i]);
	    dfd = open(argv[i], O_WRONLY, 0777);
	    if (dfd <= 0) {
		stamp("Cannot open output device %s", argv[i]);
		stamp("open(O_WRONLY) returned %d, errno=%d", dfd, errno);
		perror ("errno means");
		stamp("ABORTING (use -help for usage info)");
		exit(1);
	    }
	    if (debug&0x01) stamp("Open WRONLY successful");
	    stamp("Connect to printer (output) device %s succeeded", argv[i]);
	}
	else if (!strcmp(argv[i], "-idle")) {
	    if (++i >= argc) usage();
	    idle = atoi(argv[i]);
	    if (idle <= 0 || idle > 200) {
		stamp("Bad delay %s (%d)", argv[i], idle);
		stamp("ABORTING (use -help for usage info)");
		exit(1);
	    }
	}
	else if (!strcmp(argv[i], "-debug")) {
	    if (++i >= argc) usage();
	    debug = atoi(argv[i]);
	}
	else {
	    stamp("Bad argument %s", argv[i]);
	    usage();
	}
    }

    /* make sure required parameters were specified */
    if (dfd == 0) {
	stamp("No printer device specified, use -dev");
	usage();
    }


    if (debug) stamp("Running with debug=%d (0x%x, \\0%o)", debug, debug, debug);


    while (1) {
	FD_ZERO(&rfds);
	FD_ZERO(&wfds);
	FD_ZERO(&efds);
/* We do not read from printer ...
 *	FD_SET(dfd, &rfds);
 *	if (debug&0x04) FD_SET(dfd, &efds);
 */
	if (wlen == 0) {
	    FD_SET(sfd, &rfds);
	    if (debug&0x04) FD_SET(sfd, &efds);
	}
	else {
	    FD_SET(dfd, &wfds);
	}

	/* establish timeout value */
	timeout.tv_sec = idle;
	timeout.tv_usec = 0;


	if (debug&0x04) stamp("Starting select with wlen=%d, wtot=%d", wlen, wtot);
	stat = select(FD_SETSIZE, &rfds, &wfds, &efds, &timeout);
	if (debug&0x04) {
	    int r,w,e;
	    stamp("select returned %d (errno=%d)", stat, errno);
	    stamp("       read   write  err");
	    r=FD_ISSET(sfd, &rfds); w=FD_ISSET(sfd, &wfds); e=FD_ISSET(sfd, &efds);
	    stamp("network: %d     %d     %d",r,w,e);
	    r=FD_ISSET(dfd, &rfds); w=FD_ISSET(dfd, &wfds); e=FD_ISSET(dfd, &efds);
	    stamp("printer: %d     %d     %d",r,w,e);
	}
	if (stat == -1 && errno == 19 && wlen > 0 && FD_ISSET(dfd, &wfds)) {
	    /*
	     * BUG ALERT
	     * errno=19 means 'No such device': we ignore this
	     * BUG ALERT
	     */
	    stat = 0;
	}
	else if (stat < 0) {
	    stamp("select returned %d, errno=%d", stat, errno);
	    perror ("errno means");
	    stamp("ABORTING");
	    exit(1);
	}
	else if (stat == 0) {
	    /* We are idle */
	    if (wlen > 0) {
		if (wflg != -1) {
		    stamp("Have data to send, waiting for printer");
		    wflg = -1;
		}
	    }
	    else {
		if (wtot > 0) {
		    stamp("Sent %d bytes to printer", wtot);
		    wtot = 0;
		    wflg = -2;
		}
	    }
	    continue;
	}


	if (FD_ISSET(dfd, &rfds)) {
	    rlen = read(dfd, rbuf, sizeof(rbuf)-1);
	    if (rlen <= 0) {
		if (rflg != errno || debug&0x0040) {
		    stamp("read (from printer) returned %d, errno=%d", rlen, errno);
		    perror ("errno means");
		    rflg = errno;
		}
		sleep(idle); /* Do not fill up the log file too quickly */
	    }
	    else {
		if (debug&0x08) stamp("%d bytes returned by printer:", rlen);
		rbuf[rlen] = 0;
		printf("%s", rbuf);
		fflush(stdout);
		if (debug&0x08) {
		    printf("\n");
		    stamp("Printer data above");
		}
		rflg = -2;
	    }
	}

	if (wlen == 0 && FD_ISSET(sfd, &rfds)) {
	    wlen = read(sfd, wbuf, sizeof(wbuf));
	    if (wlen <= 0) {
		if (errno == ECONNRESET) {
		    stamp("Network connection lost? Exiting...");
		    exit (0);
		}
		if (pflg != errno || debug&0x0040) {
		    stamp("read (from network) returned %d, errno=%d", wlen, errno);
		    perror ("errno means");
		    pflg = errno;
		}
		sleep(idle); /* Do not fill up the log file too quickly */
		wlen = 0;
	    }
	    else {
		pflg = -2;
		if (debug&0x0020) stamp("%d bytes read from network", wlen);
	    }
	}
	else if (wlen > 0 && FD_ISSET(dfd, &wfds)) {
	    stat = write(dfd, wbuf+wpos, wlen-wpos);
	    if (stat <= 0) {
		if (wflg != errno || debug&0x0040) {
		    stamp("write (to printer) returned %d, errno=%d", stat, errno);
		    perror ("errno means");
		    wflg = errno;
		}
		sleep(idle); /* Do not fill up the log file too quickly */
	    }
	    else {
		if (debug&0x0010) stamp("%d bytes written to printer (out of buffer of %d)", stat, wlen-wpos);
		if (stat < wlen-wpos) {
		    wpos += stat;
		    if (wpos >= wlen) { /* This is sure not to happen, but... */
			wlen = 0;
			wpos = 0;
		    }
		}
		else {
		    wlen = 0;
		    wpos = 0;
		}
		wtot += stat;
		wflg = -2;
	    }
	}
    }
}


/*
 * log messages with time stamp
 */
stamp (fmt, err1, err2, err3)
void *fmt, *err1, *err2, *err3;
{
	register struct tm *tm_ptr;
	time_t now;

	now = time((time_t *)NULL);
	tm_ptr = localtime(&now);

	printf("%04d/%02d/%02d %02d:%02d:%02d (data_wr) ",
	    tm_ptr->tm_year + 1900, tm_ptr->tm_mon + 1, tm_ptr->tm_mday,
	    tm_ptr->tm_hour, tm_ptr->tm_min, tm_ptr->tm_sec);
	printf(fmt, err1, err2, err3);
	printf("\n");
	fflush(stdout);
}
