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