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