• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Common run loop APIs for CUPS backends.
3  *
4  * Copyright © 2007-2014 by Apple Inc.
5  * Copyright © 2006-2007 by Easy Software Products, all rights reserved.
6  *
7  * Licensed under Apache License v2.0.  See the file "LICENSE" for more
8  * information.
9  */
10 
11 /*
12  * Include necessary headers.
13  */
14 
15 #include "backend-private.h"
16 #include <limits.h>
17 #include <sys/select.h>
18 
19 
20 /*
21  * 'backendDrainOutput()' - Drain pending print data to the device.
22  */
23 
24 int					/* O - 0 on success, -1 on error */
backendDrainOutput(int print_fd,int device_fd)25 backendDrainOutput(int print_fd,	/* I - Print file descriptor */
26                    int device_fd)	/* I - Device file descriptor */
27 {
28   int		nfds;			/* Maximum file descriptor value + 1 */
29   fd_set	input;			/* Input set for reading */
30   ssize_t	print_bytes,		/* Print bytes read */
31 		bytes;			/* Bytes written */
32   char		print_buffer[8192],	/* Print data buffer */
33 		*print_ptr;		/* Pointer into print data buffer */
34   struct timeval timeout;		/* Timeout for read... */
35 
36 
37   fprintf(stderr, "DEBUG: backendDrainOutput(print_fd=%d, device_fd=%d)\n",
38           print_fd, device_fd);
39 
40  /*
41   * Figure out the maximum file descriptor value to use with select()...
42   */
43 
44   nfds = (print_fd > device_fd ? print_fd : device_fd) + 1;
45 
46  /*
47   * Now loop until we are out of data from print_fd...
48   */
49 
50   for (;;)
51   {
52    /*
53     * Use select() to determine whether we have data to copy around...
54     */
55 
56     FD_ZERO(&input);
57     FD_SET(print_fd, &input);
58 
59     timeout.tv_sec  = 0;
60     timeout.tv_usec = 0;
61 
62     if (select(nfds, &input, NULL, NULL, &timeout) < 0)
63       return (-1);
64 
65     if (!FD_ISSET(print_fd, &input))
66       return (0);
67 
68     if ((print_bytes = read(print_fd, print_buffer,
69 			    sizeof(print_buffer))) < 0)
70     {
71      /*
72       * Read error - bail if we don't see EAGAIN or EINTR...
73       */
74 
75       if (errno != EAGAIN && errno != EINTR)
76       {
77 	fprintf(stderr, "DEBUG: Read failed: %s\n", strerror(errno));
78 	_cupsLangPrintFilter(stderr, "ERROR", _("Unable to read print data."));
79 	return (-1);
80       }
81 
82       print_bytes = 0;
83     }
84     else if (print_bytes == 0)
85     {
86      /*
87       * End of file, return...
88       */
89 
90       return (0);
91     }
92 
93     fprintf(stderr, "DEBUG: Read %d bytes of print data...\n",
94 	    (int)print_bytes);
95 
96     for (print_ptr = print_buffer; print_bytes > 0;)
97     {
98       if ((bytes = write(device_fd, print_ptr, (size_t)print_bytes)) < 0)
99       {
100        /*
101         * Write error - bail if we don't see an error we can retry...
102 	*/
103 
104         if (errno != ENOSPC && errno != ENXIO && errno != EAGAIN &&
105 	    errno != EINTR && errno != ENOTTY)
106 	{
107 	  _cupsLangPrintError("ERROR", _("Unable to write print data"));
108 	  return (-1);
109 	}
110       }
111       else
112       {
113         fprintf(stderr, "DEBUG: Wrote %d bytes of print data...\n", (int)bytes);
114 
115         print_bytes -= bytes;
116 	print_ptr   += bytes;
117       }
118     }
119   }
120 }
121 
122 
123 /*
124  * 'backendRunLoop()' - Read and write print and back-channel data.
125  */
126 
127 ssize_t					/* O - Total bytes on success, -1 on error */
backendRunLoop(int print_fd,int device_fd,int snmp_fd,http_addr_t * addr,int use_bc,int update_state,_cups_sccb_t side_cb)128 backendRunLoop(
129     int          print_fd,		/* I - Print file descriptor */
130     int          device_fd,		/* I - Device file descriptor */
131     int          snmp_fd,		/* I - SNMP socket or -1 if none */
132     http_addr_t  *addr,			/* I - Address of device */
133     int          use_bc,		/* I - Use back-channel? */
134     int          update_state,		/* I - Update printer-state-reasons? */
135     _cups_sccb_t side_cb)		/* I - Side-channel callback */
136 {
137   int		nfds;			/* Maximum file descriptor value + 1 */
138   fd_set	input,			/* Input set for reading */
139 		output;			/* Output set for writing */
140   ssize_t	print_bytes,		/* Print bytes read */
141 		bc_bytes,		/* Backchannel bytes read */
142 		total_bytes,		/* Total bytes written */
143 		bytes;			/* Bytes written */
144   int		paperout;		/* "Paper out" status */
145   int		offline;		/* "Off-line" status */
146   char		print_buffer[8192],	/* Print data buffer */
147 		*print_ptr,		/* Pointer into print data buffer */
148 		bc_buffer[1024];	/* Back-channel data buffer */
149   struct timeval timeout;		/* Timeout for select() */
150   time_t	curtime,		/* Current time */
151 		snmp_update = 0;
152 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
153   struct sigaction action;		/* Actions for POSIX signals */
154 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
155 
156 
157   fprintf(stderr,
158           "DEBUG: backendRunLoop(print_fd=%d, device_fd=%d, snmp_fd=%d, "
159 	  "addr=%p, use_bc=%d, side_cb=%p)\n",
160           print_fd, device_fd, snmp_fd, addr, use_bc, side_cb);
161 
162  /*
163   * If we are printing data from a print driver on stdin, ignore SIGTERM
164   * so that the driver can finish out any page data, e.g. to eject the
165   * current page.  We only do this for stdin printing as otherwise there
166   * is no way to cancel a raw print job...
167   */
168 
169   if (!print_fd)
170   {
171 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
172     sigset(SIGTERM, SIG_IGN);
173 #elif defined(HAVE_SIGACTION)
174     memset(&action, 0, sizeof(action));
175 
176     sigemptyset(&action.sa_mask);
177     action.sa_handler = SIG_IGN;
178     sigaction(SIGTERM, &action, NULL);
179 #else
180     signal(SIGTERM, SIG_IGN);
181 #endif /* HAVE_SIGSET */
182   }
183   else if (print_fd < 0)
184   {
185    /*
186     * Copy print data from stdin, but don't mess with the signal handlers...
187     */
188 
189     print_fd = 0;
190   }
191 
192  /*
193   * Figure out the maximum file descriptor value to use with select()...
194   */
195 
196   nfds = (print_fd > device_fd ? print_fd : device_fd) + 1;
197 
198  /*
199   * Now loop until we are out of data from print_fd...
200   */
201 
202   for (print_bytes = 0, print_ptr = print_buffer, offline = -1,
203            paperout = -1, total_bytes = 0;;)
204   {
205    /*
206     * Use select() to determine whether we have data to copy around...
207     */
208 
209     FD_ZERO(&input);
210     if (!print_bytes)
211       FD_SET(print_fd, &input);
212     if (use_bc)
213       FD_SET(device_fd, &input);
214     if (!print_bytes && side_cb)
215       FD_SET(CUPS_SC_FD, &input);
216 
217     FD_ZERO(&output);
218     if (print_bytes || (!use_bc && !side_cb))
219       FD_SET(device_fd, &output);
220 
221     if (use_bc || side_cb)
222     {
223       timeout.tv_sec  = 5;
224       timeout.tv_usec = 0;
225 
226       if (select(nfds, &input, &output, NULL, &timeout) < 0)
227       {
228        /*
229 	* Pause printing to clear any pending errors...
230 	*/
231 
232 	if (errno == ENXIO && offline != 1 && update_state)
233 	{
234 	  fputs("STATE: +offline-report\n", stderr);
235 	  _cupsLangPrintFilter(stderr, "INFO",
236 	                       _("The printer is not connected."));
237 	  offline = 1;
238 	}
239 	else if (errno == EINTR && total_bytes == 0)
240 	{
241 	  fputs("DEBUG: Received an interrupt before any bytes were "
242 	        "written, aborting.\n", stderr);
243           return (0);
244 	}
245 
246 	sleep(1);
247 	continue;
248       }
249     }
250 
251    /*
252     * Check if we have a side-channel request ready...
253     */
254 
255     if (side_cb && FD_ISSET(CUPS_SC_FD, &input))
256     {
257      /*
258       * Do the side-channel request, then start back over in the select
259       * loop since it may have read from print_fd...
260       */
261 
262       if ((*side_cb)(print_fd, device_fd, snmp_fd, addr, use_bc))
263         side_cb = NULL;
264       continue;
265     }
266 
267    /*
268     * Check if we have back-channel data ready...
269     */
270 
271     if (FD_ISSET(device_fd, &input))
272     {
273       if ((bc_bytes = read(device_fd, bc_buffer, sizeof(bc_buffer))) > 0)
274       {
275 	fprintf(stderr,
276 	        "DEBUG: Received " CUPS_LLFMT " bytes of back-channel data\n",
277 	        CUPS_LLCAST bc_bytes);
278         cupsBackChannelWrite(bc_buffer, (size_t)bc_bytes, 1.0);
279       }
280       else if (bc_bytes < 0 && errno != EAGAIN && errno != EINTR)
281       {
282         fprintf(stderr, "DEBUG: Error reading back-channel data: %s\n",
283 	        strerror(errno));
284 	use_bc = 0;
285       }
286       else if (bc_bytes == 0)
287         use_bc = 0;
288     }
289 
290    /*
291     * Check if we have print data ready...
292     */
293 
294     if (FD_ISSET(print_fd, &input))
295     {
296       if ((print_bytes = read(print_fd, print_buffer,
297                               sizeof(print_buffer))) < 0)
298       {
299        /*
300         * Read error - bail if we don't see EAGAIN or EINTR...
301 	*/
302 
303 	if (errno != EAGAIN && errno != EINTR)
304 	{
305 	  fprintf(stderr, "DEBUG: Read failed: %s\n", strerror(errno));
306 	  _cupsLangPrintFilter(stderr, "ERROR",
307 	                       _("Unable to read print data."));
308 	  return (-1);
309 	}
310 
311         print_bytes = 0;
312       }
313       else if (print_bytes == 0)
314       {
315        /*
316         * End of file, break out of the loop...
317 	*/
318 
319         break;
320       }
321 
322       print_ptr = print_buffer;
323 
324       fprintf(stderr, "DEBUG: Read %d bytes of print data...\n",
325               (int)print_bytes);
326     }
327 
328    /*
329     * Check if the device is ready to receive data and we have data to
330     * send...
331     */
332 
333     if (print_bytes && FD_ISSET(device_fd, &output))
334     {
335       if ((bytes = write(device_fd, print_ptr, (size_t)print_bytes)) < 0)
336       {
337        /*
338         * Write error - bail if we don't see an error we can retry...
339 	*/
340 
341         if (errno == ENOSPC)
342 	{
343 	  if (paperout != 1 && update_state)
344 	  {
345 	    fputs("STATE: +media-empty-warning\n", stderr);
346 	    fputs("DEBUG: Out of paper\n", stderr);
347 	    paperout = 1;
348 	  }
349         }
350 	else if (errno == ENXIO)
351 	{
352 	  if (offline != 1 && update_state)
353 	  {
354 	    fputs("STATE: +offline-report\n", stderr);
355 	    _cupsLangPrintFilter(stderr, "INFO",
356 	                         _("The printer is not connected."));
357 	    offline = 1;
358 	  }
359 	}
360 	else if (errno != EAGAIN && errno != EINTR && errno != ENOTTY)
361 	{
362 	  _cupsLangPrintError("ERROR", _("Unable to write print data"));
363 	  return (-1);
364 	}
365       }
366       else
367       {
368         if (paperout && update_state)
369 	{
370 	  fputs("STATE: -media-empty-warning\n", stderr);
371 	  paperout = 0;
372 	}
373 
374 	if (offline && update_state)
375 	{
376 	  fputs("STATE: -offline-report\n", stderr);
377 	  _cupsLangPrintFilter(stderr, "INFO",
378 	                       _("The printer is now connected."));
379 	  offline = 0;
380 	}
381 
382         fprintf(stderr, "DEBUG: Wrote %d bytes of print data...\n", (int)bytes);
383 
384         print_bytes -= bytes;
385 	print_ptr   += bytes;
386 	total_bytes += bytes;
387       }
388     }
389 
390    /*
391     * Do SNMP updates periodically...
392     */
393 
394     if (snmp_fd >= 0 && time(&curtime) >= snmp_update)
395     {
396       if (backendSNMPSupplies(snmp_fd, addr, NULL, NULL))
397         snmp_update = INT_MAX;
398       else
399         snmp_update = curtime + 5;
400     }
401   }
402 
403  /*
404   * Return with success...
405   */
406 
407   return (total_bytes);
408 }
409 
410 
411 /*
412  * 'backendWaitLoop()' - Wait for input from stdin while handling side-channel
413  *                       queries.
414  */
415 
416 int					/* O - 1 if data is ready, 0 if not */
backendWaitLoop(int snmp_fd,http_addr_t * addr,int use_bc,_cups_sccb_t side_cb)417 backendWaitLoop(
418     int          snmp_fd,		/* I - SNMP socket or -1 if none */
419     http_addr_t  *addr,			/* I - Address of device */
420     int          use_bc,		/* I - Use back-channel? */
421     _cups_sccb_t side_cb)		/* I - Side-channel callback */
422 {
423   int			nfds;		/* Number of file descriptors */
424   fd_set		input;		/* Input set for reading */
425   time_t		curtime = 0,	/* Current time */
426 			snmp_update = 0;/* Last SNMP status update */
427   struct timeval	timeout;	/* Timeout for select() */
428 
429 
430   fprintf(stderr, "DEBUG: backendWaitLoop(snmp_fd=%d, addr=%p, side_cb=%p)\n",
431 	  snmp_fd, addr, side_cb);
432 
433  /*
434   * Now loop until we receive data from stdin...
435   */
436 
437   if (snmp_fd >= 0)
438     snmp_update = time(NULL) + 5;
439 
440   for (;;)
441   {
442    /*
443     * Use select() to determine whether we have data to copy around...
444     */
445 
446     FD_ZERO(&input);
447     FD_SET(0, &input);
448     if (side_cb)
449       FD_SET(CUPS_SC_FD, &input);
450 
451     if (snmp_fd >= 0)
452     {
453       curtime         = time(NULL);
454       timeout.tv_sec  = curtime >= snmp_update ? 0 : snmp_update - curtime;
455       timeout.tv_usec = 0;
456 
457       nfds = select(CUPS_SC_FD + 1, &input, NULL, NULL, &timeout);
458     }
459     else
460       nfds = select(CUPS_SC_FD + 1, &input, NULL, NULL, NULL);
461 
462     if (nfds < 0)
463     {
464      /*
465       * Pause printing to clear any pending errors...
466       */
467 
468       if (errno == EINTR)
469       {
470 	fputs("DEBUG: Received an interrupt before any bytes were "
471 	      "written, aborting.\n", stderr);
472 	return (0);
473       }
474 
475       sleep(1);
476       continue;
477     }
478 
479    /*
480     * Check for input on stdin...
481     */
482 
483     if (FD_ISSET(0, &input))
484       break;
485 
486    /*
487     * Check if we have a side-channel request ready...
488     */
489 
490     if (side_cb && FD_ISSET(CUPS_SC_FD, &input))
491     {
492      /*
493       * Do the side-channel request, then start back over in the select
494       * loop since it may have read from print_fd...
495       */
496 
497       if ((*side_cb)(0, -1, snmp_fd, addr, use_bc))
498         side_cb = NULL;
499       continue;
500     }
501 
502    /*
503     * Do SNMP updates periodically...
504     */
505 
506     if (snmp_fd >= 0 && curtime >= snmp_update)
507     {
508       if (backendSNMPSupplies(snmp_fd, addr, NULL, NULL))
509         snmp_fd = -1;
510       else
511         snmp_update = curtime + 5;
512     }
513   }
514 
515  /*
516   * Return with success...
517   */
518 
519   return (1);
520 }
521