From: jakob@ptt-iat (Jakob Schripsema)
Newsgroups: comp.dcom.servers
Subject: Re: Output to remote printer truncated
Date: 5 Oct 92 08:38:23 GMT
Organization: PTT Telecom I&AT Groningen

In article <s900387.717859459@minyos.xx.rmit.OZ.AU> s900387@minyos.xx.rmit.oz.au (Craig Macbride) writes:
>I have recently seen a problem whereby a SCO Unix box, sending to a
>printer through a Synoptics terminal server, ends up with truncated
>printouts. Is there any easy modification to the "software" (ie.
>source in the manual) provided with the Synoptics box that will make
>this problem go away?
I had the same problem with a Spider terminal server. Paul Gomans from Spider
Systems gave me the following solution:

: The problem you are seeing with losing print information looks like it is
: due to the implementation of the "shutdown" and "close" functions on the
: host. The difficulty in providing general code for many system implementations
: is that they are all different and implement functions in different ways.
: 
: /* this shutdown should send a FIN once all the data has been sent	*/
: 
: 	if (shutdown(sock, 1) == -1)
: 		error ("shutdown failed"); 
: 
: /* read until Port has sent all data on serial line and responded with */
: /* a FIN - this will probably be interpreted as an EOF		 */
: 
: 	while (readerr = read (sock, in_buff, sizeof (in_buff)) > 0);
: 
: /* now able to close the connection since all data received AND output*/
: 
: 	close (sock);
: 
: The issue on some hosts is that the "shutdown" is not doing what is expected.
: 
: Another possible solution is to use the "linger" option on sockets which will
: linger on the close until FINs are exchanged. You could use this
: _instead_ of the shutdown option. The syntax of the linger option is used in
: the function "setsockopt" and is something like:
: 
: 
: 	struct linger optval;
: 	
: 	optval.l_onoff = 1;
: 	optval.1_linger = 300;		/* linger time in seconds	*/
: 
: 	setsockopt (sock, SOL_SOCKET, SO_LINGER, &optval, sizeof (optval));
: 
: The final option is the one you mentioned where you simply insert a "sleep"
: 

This works. To save you some work here's the code I use :

/*
 * Remote print
 *
 * Print on printer connected to terminalserver
 *
 * Exit values:
 *	0:	Ok
 *	1:	Error, retry
 *	2:	Error, don't retry
 * 
 * Jakob Schripsema, PTT Telecom, The Netherlands
 */

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <string.h>
#include <netdb.h>
#include <time.h>

#define	CL_BSIZE	2048	/* Local buffer size */

int s;				/* connected socket descriptor */

struct hostent *hp;		/* pointer to host info for remote host */

struct sockaddr_in myaddr_in;	/* for local socket address */
struct sockaddr_in peeraddr_in;	/* for peer socket address */

main(argc, argv)
int argc;
char *argv[];
{
	int addrlen, i, j;
	int len,datacnt=0;
	u_short port;
	char buf[CL_BSIZE];	/* Local buffer */
	int tries;		/* #tries for connect */
	time_t curr_time;	/* Current time, used for logging */
	struct linger lngval;	/* Data for LINGER socket option */
	char *bp;		/* General purpose character pointer */
	FILE *prf;		/* File to print */

	if (argc != 3) {
		fprintf(stderr, "Usage:  %s host port < file\n", argv[0]);
		exit(2);
	}

	memset ((char *)&myaddr_in, 0, sizeof(struct sockaddr_in));
	memset ((char *)&peeraddr_in, 0, sizeof(struct sockaddr_in));

	peeraddr_in.sin_family = AF_INET;
	hp = gethostbyname (argv[1]);
	if (hp == NULL) {
		fprintf(stderr, "%s: %s not found in /etc/hosts\n",
				argv[0], argv[1]);
		exit(1);
	}
	if (argc == 2)
		port = 23;
	else
		port = atoi(argv[2]);
	peeraddr_in.sin_addr.s_addr=((struct in_addr *)(hp->h_addr))->s_addr;
	peeraddr_in.sin_port = htons(port);

	/* Try to connect to the remote server at the address
	 * which was just built into peeraddr.
	 */
	tries = 5;
	while (tries--) {
		/* Create the socket. */
		s = socket (AF_INET, SOCK_STREAM, 0);
		if (s == -1) {
			perror(argv[0]);
			fprintf(stderr, "%s: unable to create socket\n",
				argv[0]);
			exit(1);
		}
		if (connect(s, &peeraddr_in, sizeof(struct sockaddr_in)) == 0)
			break;
#ifdef	LOG
		curr_time = time(NULL);
		printf("Connect failed at %s",ctime(&curr_time));
		fflush(stdout);
		perror(argv[0]);
#endif
		if (tries >= 0) {
			close (s);
			sleep(10);
		}
	}
	if (tries < 0) {
		perror(argv[0]);
		fprintf(stderr, "%s: unable to connect to remote\n", argv[0]);
		exit(2);
	}
#ifdef	LOG
	curr_time = time(NULL);
	printf("Connect done at %s",ctime(&curr_time));fflush(stdout);
#endif
	addrlen = sizeof(struct sockaddr_in);
	if (getsockname(s, &myaddr_in, &addrlen) == -1) {
		perror(argv[0]);
		fprintf(stderr, "%s: unable to read socket address\n", argv[0]);
		exit(1);
	}

	/* Sent out all the data to the remote server.
	 */
#ifdef	notdef
	if ((prf = fopen(argv[2],"r")) == NULL) {
		perror(argv[2]);
		exit(1);
	}
#else
	prf = stdin;
#endif
#ifdef	BLOCK_IO
	while (len=fread(buf,1,CL_BSIZE,prf)) {
		datacnt += len;
		i = 0;
		do {
			i += send(s,&buf[i],len-i,0);
		} while (i<len);
	}
#else	/* LINE_IO */
	while (fgets(buf,CL_BSIZE,prf) != NULL) {
		/* Strip \n if found */
		if ((bp=strchr(buf,'\n')) != NULL)
			*bp = '\0';
		/* Add CR/LF */
		strcat(buf,"\r\n");
		len = strlen(buf);
		datacnt += len;
		i = 0;
		do {
			i += send(s,&buf[i],len-i,0);
		} while (i<len);
	}
#endif

#ifdef	undef
	/* Send a FIN at the end of data */
	/* Works on:
	 *	Unisys U6000/55	SysVr3
	 *	HP 9000/827 HP-UX 8.02
	 */
	if (shutdown(s, 1) == -1) {
		perror(argv[0]);
		fprintf(stderr, "%s: unable to shutdown socket\n", argv[0]);
		exit(1);
	}
#ifdef	LOG
	curr_time = time(NULL);
	printf("Shutdown done at %s",ctime(&curr_time));fflush(stdout);
#endif
#endif

	/* If shutdown doesn't work as expected, try this */
	/* Works on:
	 *	Unisys U6000/55	SysVr3
	 *	Unisys U6000/35 SysVr4
	 *	HP 9000/827 HP-UX 8.02
	 */
	lngval.l_onoff = 1;
	lngval.l_linger = 300;	/* Linger time-out is seconds */
	if (setsockopt(s,SOL_SOCKET,SO_LINGER,&lngval,sizeof(lngval)) < 0) {
		perror(argv[0]);
		fprintf(stderr,"%s: unable to do setsockopt\n",argv[0]);
		exit(1);
	}
#ifdef	LOG
	curr_time = time(NULL);
	printf("Setsockopt done at %s",ctime(&curr_time));fflush(stdout);
#endif

#ifdef	notdef
	/* Wait for end of file from the other end */
	while (read(s,buf,sizeof(buf)) > 0)
		;
#ifdef	LOG
	curr_time = time(NULL);
	printf("Read from socket done\n");fflush(stdout);
#endif
#endif

	/* Now close connection and let the terminal server recover
	 * from this shock
	 */
	close(s);
#ifdef	LOG
	curr_time = time(NULL);
	printf("Close socket done at %s",ctime(&curr_time));fflush(stdout);
#endif
	return 0;
}

Xref: sparky comp.dcom.servers:91 comp.sys.sun.hardware:4661