xref: /illumos-gate/usr/src/cmd/lp/model/netpr/net.c (revision 20a7641f9918de8574b8b3b47dbe35c4bfc78df1)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <stdarg.h>
30 #include <libintl.h>
31 #include <signal.h>
32 #include <errno.h>
33 #include <fcntl.h>
34 #include <unistd.h>
35 #include <string.h>
36 #include <strings.h>
37 #include <syslog.h>
38 #include <sys/types.h>
39 #include <sys/socket.h>
40 #include <sys/file.h>
41 #include <netinet/in.h>
42 #include "netpr.h"
43 
44 #define	TIMEOUT		1
45 
46 static int netpr_send_message(int, char *, ...);
47 static int xfer_cfAfile(int, char *, char *, uint);
48 
49 int
50 bsd_print(int sockfd, caddr_t pa, np_bsdjob_t * bsdjob)
51 {
52 	int filesize;
53 	int xfer;
54 	int net;
55 
56 	syslog(LOG_DEBUG, "bsd_print");
57 
58 	filesize = bsdjob->np_data->np_data_size;
59 	syslog(LOG_DEBUG, "filesize is %d", filesize);
60 
61 
62 	if (netpr_send_message(sockfd, "%c%s\n", XFER_REQUEST,
63 		bsdjob->np_printer) != 0) {
64 		return (NETWORK_ERROR_SEND_RESPONSE);
65 	}
66 
67 	/*
68 	 * control file
69 	 */
70 
71 	if (bsdjob->np_print_order == CONTROL_FIRST) {
72 		if ((xfer_cfAfile(sockfd, bsdjob->np_cfAfile,
73 		    bsdjob->np_cfAfilename,
74 		    bsdjob->np_cfAfilesize)) != 0) {
75 			(void) fprintf(stderr,
76 			    gettext("Netpr: Error sending control file\n"));
77 			syslog(LOG_DEBUG, "Error sending control file");
78 			    return (NETWORK_ERROR_UNKNOWN);
79 
80 		}
81 	}
82 
83 	/* send msg - get ready for transfer */
84 
85 	if ((netpr_send_message(sockfd, "%c%d %s\n", XFER_DATA, filesize,
86 	    bsdjob->np_data->np_dfAfilename)) != 0) {
87 		return (NETWORK_ERROR_SEND_RESPONSE);
88 	}
89 
90 	/*
91 	 * send the file
92 	 */
93 
94 	if ((xfer = xfer_file(sockfd, pa, filesize, bsdjob->np_timeout)) != 0) {
95 		return (xfer);
96 	}
97 
98 	/* send msg - done */
99 	if ((net = netpr_send_message(sockfd, "", NULL)) != 0) {
100 		(void) fprintf(stderr,
101 		gettext("Netpr: network error transfering %s returns: %d\n"),
102 			bsdjob->np_filename, net);
103 		syslog(LOG_DEBUG,
104 			"network error transfering %s returns: %d",
105 			bsdjob->np_filename, net);
106 		return (NETWORK_ERROR_WRITE_FAILED);
107 	}
108 
109 	/*
110 	 * control file
111 	 */
112 
113 	if (bsdjob->np_print_order == DATA_FIRST) {
114 		if ((xfer_cfAfile(sockfd, bsdjob->np_cfAfile,
115 		    bsdjob->np_cfAfilename,
116 		    bsdjob->np_cfAfilesize)) != 0) {
117 
118 			(void) fprintf(stderr,
119 			    gettext("Netpr: Error sending control file\n"));
120 			    syslog(LOG_DEBUG, "Error sending control file");
121 			    return (NETWORK_ERROR_UNKNOWN);
122 		}
123 	}
124 
125 	return (0);
126 }
127 
128 int
129 xfer_file(int sockfd, caddr_t pa, int filesize, int seed)
130 {
131 	int ctr;
132 	int timeout;
133 	int nw;
134 	int error_msg = 0;
135 	int pause = 0;
136 
137 	syslog(LOG_DEBUG, "xfer_file");
138 
139 	/* send file */
140 	ctr = filesize;
141 	timeout = seed = seed ? seed : 10;
142 
143 	while (ctr > 0) {
144 
145 	syslog(LOG_DEBUG, "xfer_file: write while loop => ctr = %d", ctr);
146 	syslog(LOG_DEBUG, "xfer_file: timeout = %d", timeout);
147 
148 		(void) signal(SIGALRM, null_sighandler);
149 		(void) alarm(10);
150 		nw = write(sockfd, pa, ctr);
151 	syslog(LOG_DEBUG, "xfer_file: write while loop => nw = %d", nw);
152 		(void) alarm(0);
153 		if ((nw == 0) || (nw < 0)) {
154 			if (timeout < (seed * 4)) {
155 				(void) sleep(timeout);
156 				timeout *= 2;
157 			} else if (timeout == (seed * 4)) {
158 				(void) sleep(timeout);
159 				timeout *= 2;
160 
161 				/*
162 				 * Send message to user once
163 				 */
164 				if (error_msg == 0) {
165 					error_msg++;
166 					tell_lptell(ERRORMSG,
167 					gettext("Printer not accepting input;"
168 					"possibly offline or out of paper."));
169 				}
170 
171 			} else if (timeout > (seed * 4)) {
172 				(void) sleep(timeout);
173 				if (pause++ > 3)
174 					timeout = (seed * 10);
175 			}
176 
177 		} else {
178 			ctr -= nw;
179 			pa += nw;
180 			if (error_msg) {
181 				tell_lptell(OKMSG, "Current");
182 				error_msg = 0;
183 				pause = 0;
184 			}
185 			timeout = seed;
186 		}
187 	}
188 
189 	return (E_SUCCESS);
190 }
191 
192 static int
193 xfer_cfAfile(int sockfd, char * cfAfile, char * cfAname, uint size)
194 {
195 	int ctr;
196 	caddr_t pa;
197 	int nw = 0;
198 	int timeout;
199 	int printererr;
200 
201 	syslog(LOG_DEBUG, "xfer_cfAfile");
202 
203 	if ((netpr_send_message(sockfd, "%c%d %s\n", XFER_CONTROL,
204 		size, cfAname)) != 0) {
205 		return (NETWORK_ERROR_MSG_FAILED);
206 	}
207 
208 	/* send the control file */
209 	pa = cfAfile;
210 	ctr = size;
211 	syslog(LOG_DEBUG, "xfer_cfAfile : cfAfile %s", pa);
212 	syslog(LOG_DEBUG, "xfer_cfAfile : size %d", size);
213 
214 	/* send control file */
215 	timeout = TIMEOUT;
216 	printererr = 0;
217 	while (ctr > 0) {
218 		(void) signal(SIGALRM, null_sighandler);
219 		(void) alarm(2);
220 		nw = write(sockfd, pa, size);
221 		(void) alarm(0);
222 		if (nw <= 0) {
223 			if (timeout < 16) {
224 				(void) sleep(timeout);
225 				timeout *= 2;
226 			} else if (timeout == 16) {
227 			/* talk with the printer and see what's happening */
228 				/* send message back to caller */
229 				(void) sleep(timeout);
230 				timeout *= 2;
231 				printererr = 1;
232 
233 				tell_lptell(ERRORMSG,
234 				gettext("Printer not accepting input;"
235 				"possibly offline or out of paper."));
236 
237 			} else if (timeout > 16) {
238 				(void) sleep(timeout);
239 			}
240 		}
241 		ctr -= nw;
242 		pa += nw;
243 	}
244 
245 	if (printererr == 1) {
246 		(void) fprintf(stderr, gettext("Printer status ok\n"));
247 		tell_lptell(OKMSG, "Current");
248 	}
249 
250 
251 	/* send msg - done */
252 	if (netpr_send_message(sockfd, "", NULL) != 0) {
253 		return (NETWORK_ERROR_MSG_FAILED);
254 	}
255 
256 	return (0);
257 }
258 
259 /*
260  *  netpr_response() reads in a byte from the network printer
261  */
262 static int
263 netpr_response(int nd)
264 {
265 	char    c;
266 	int msg_given = 0;
267 	int firstloop = 0;
268 
269 	syslog(LOG_DEBUG, "netpr_response");
270 
271 	(void) signal(SIGALRM, null_sighandler);
272 	(void) alarm(2);
273 	while (1) {
274 		errno = 0;
275 		if ((read(nd, &c, 1) != 1)) {
276 
277 			if (firstloop == 0) {
278 				(void) alarm(0);
279 				firstloop++;
280 			}
281 
282 			if (errno == EINTR) {
283 				if (msg_given == 0) {
284 				    tell_lptell(ERRORMSG,
285 				    gettext("Printer not responding;"
286 				    "Either warming up or needs attention"));
287 				    msg_given++;
288 				    syslog(LOG_DEBUG,
289 					"read hanging in netpr_response: %m");
290 				}
291 
292 			} else {
293 				syslog(LOG_DEBUG,
294 					"read in netpr_response failed: %m");
295 				return (NETWORK_READ_RESPONSE_FAILED);
296 			}
297 
298 		} else {
299 			if (c) {
300 				syslog(LOG_DEBUG,
301 					"Printer returned error: %m");
302 				return (NETWORK_PRINTER_REFUSED_CONN);
303 			} else {
304 				if (msg_given)
305 					tell_lptell(OKMSG, "Current");
306 				return (0);
307 			}
308 		}
309 	}
310 
311 }
312 
313 static int
314 netpr_send_message(int nd, char *fmt, ...)
315 {
316 	char    buf[BUFSIZ];
317 	int ctr;
318 	char * pa;
319 	va_list ap;
320 	int timeout = 1;
321 	int nw;
322 	int err_msg = 0;
323 
324 	syslog(LOG_DEBUG, "netpr_send_message");
325 	va_start(ap, fmt);
326 	(void) vsnprintf(buf, sizeof (buf), fmt, ap);
327 	va_end(ap);
328 
329 	pa = buf;
330 	ctr = (strlen(buf) != 0) ? strlen(buf) : 1;
331 
332 	syslog(LOG_DEBUG, "netpr_send_message : ctr = %d", ctr);
333 	while (ctr > 0) {
334 		(void) signal(SIGALRM, null_sighandler);
335 		(void) alarm(2);
336 		nw = write(nd, pa, ctr);
337 	syslog(LOG_DEBUG, "netpr_send_message : nw = %d", nw);
338 		(void) alarm(0);
339 
340 		if (nw <= 0) {
341 			if (timeout < 16) {
342 				(void) sleep(timeout);
343 				timeout *= 2;
344 			} else if (timeout == 16) {
345 				(void) sleep(timeout);
346 				timeout *= 2;
347 				if (err_msg == 0) {
348 					err_msg++;
349 					tell_lptell(ERRORMSG,
350 					gettext("Printer not accepting input;"
351 					"possibly offline or out of paper."));
352 				}
353 			} else
354 				(void) sleep(timeout);
355 		} else {
356 			ctr -= nw;
357 			pa += nw;
358 			if (err_msg)
359 				tell_lptell(OKMSG, "Current");
360 		}
361 	}
362 
363 	return (netpr_response(nd));
364 }
365 
366 /*
367  *  null() is to be used as a signal handler that does nothing.  It is used in
368  *      place of SIG_IGN, because we want the signal to be delivered and
369  *      interupt the current system call.
370  */
371 /*ARGSUSED*/
372 void
373 null_sighandler(int i)
374 {
375 }
376