/*************************************************************

 Copyright (C) 2001, Eric Kratzer

 imp3sockclient.c
 Client socket program for imp3sh named-pipe input/output.

 This software is part of imp3sh and released under the GNU
 GPL. Please see the included 'COPYING' file for more details.


 Description:

   This client will connect to a remote session of imp3sh (via
   the imp3sockserver), pass any commands given on the command
   line, and report any output from those commands to stdout of
   this client.


 Example:

 1) Run imp3sh with filenames (to be used as named pipes)
    as args:

    >imp3sh -i ~/tempin -o ~/tempout &

    NOTE: imp3sh will create the named pipes if they do not
          already exist.

 2) Run the 'imp3sockserver' program (see 'imp3sockserver.c')
    on the same machine imp3sh is running.

 3) Run this client program [from anywhere] with the server
    hostname and port, and commands to imp3sh as args:

    >imp3sockclient <hostname> <port> <commands>

    NOTE: After the client program has successfully 
          written the command(s) to the input pipe, it will
          automatically exit after 1 second of inactivity from
	  imp3sockserver. If you do not want to wait for output,
          use the '-n' switch as the first argument.

**************************************************************/

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

void print_usage(char *prog)
{
    fprintf(stderr, "Usage: %s [-n] <host> <port> <commands>\n", prog);
    exit(1);
}

int main(int argc, char *argv[])
{
    char *host, no_output, buff[8192];
    int fdclient, ret, maxfd, len, iln, port, ndx;
    fd_set read_set;
    struct timeval tv;
    struct sockaddr_in serv_adr;
    struct hostent *serv_ent;

    if (argc < 4)
	print_usage(*argv);

    ndx = 1;
    no_output = 0;
    host = argv[ndx++];
    if (strcmp(host, "-n") == 0) {
	if (argc < 5)
	    print_usage(*argv);
	no_output = 1;
	host = argv[ndx++];
    }

    port = atoi(argv[ndx++]);
    if (port <= 0) {
	fprintf(stderr, "Invalid port specified : \"%d\"\n", port);
	exit(1);
    }

    *buff = '\0';
    for (len = ndx; len < argc; len++) {
	strcat(buff, argv[len]);
	if ((len + 1) != argc)
	    strcat(buff, " ");
    }
    strcat(buff, "\n");
    len = strlen(buff);

    fdclient = socket(AF_INET, SOCK_STREAM, 0);
    if (fdclient == -1) {
	fprintf(stderr, "Error socket() failed : %s\n", strerror(errno));
	exit(1);
    }

    serv_ent = gethostbyname(host);
    if (!serv_ent) {
	fprintf(stderr, "Error gethostbyname() failed : %s\n",
		strerror(errno));
	exit(1);
    }

    memset(&serv_adr, 0, sizeof(serv_adr));
    serv_adr.sin_family = AF_INET;
    serv_adr.sin_port = htons(port);
    memcpy(&serv_adr.sin_addr, serv_ent->h_addr_list[0],
	   serv_ent->h_length);

    ret = connect(fdclient, (struct sockaddr *) &serv_adr, sizeof(serv_adr));
    if (ret == -1) {
	fprintf(stderr, "Error connect() failed : %s\n", strerror(errno));
	exit(1);
    }

    ret = write(fdclient, buff, len);
    if (ret == -1) {
	fprintf(stderr, "Error write() failed : %s\n", strerror(errno));
	close(fdclient);
	exit(1);
    } else if (ret == 0) {
	fprintf(stderr, "Server terminated\n");
	close(fdclient);
	exit(1);
    }

    if (no_output) {
	close(fdclient);
	exit(0);
    }

    while (1) {
	FD_ZERO(&read_set);
	maxfd = 0;

	FD_SET(fdclient, &read_set);
	maxfd = fdclient + 1;

	tv.tv_sec = 1;
	tv.tv_usec = 0;

	ret = select(maxfd, &read_set, NULL, NULL, &tv);

	if (FD_ISSET(fdclient, &read_set)) {
	    iln = read(fdclient, buff, 8192);
	    if (iln == 0) {
		fprintf(stderr, "Server terminated\n");
		close(fdclient);
		exit(1);
	    } else if (iln == -1) {
		fprintf(stderr, "Error read() failed : %s\n",
			strerror(errno));
		close(fdclient);
		exit(1);
	    } else
		write(1, buff, iln);
	} else {		/* select() timed out */
	    close(fdclient);
	    exit(0);
	}
    }

    return 0;
}
