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