summaryrefslogtreecommitdiff
path: root/bjnp-runloop.c
diff options
context:
space:
mode:
Diffstat (limited to 'bjnp-runloop.c')
-rw-r--r--bjnp-runloop.c758
1 files changed, 357 insertions, 401 deletions
diff --git a/bjnp-runloop.c b/bjnp-runloop.c
index 17f18e3..4163446 100644
--- a/bjnp-runloop.c
+++ b/bjnp-runloop.c
@@ -1,26 +1,26 @@
/*
*
- * bjnp specifc run loop APIs for the Common UNIX Printing System (CUPS).
- * Copyright 2008 by Louis Lagendijk
+ * bjnp specifc run loop APIs for the Common UNIX Printing System (CUPS).
+ * Copyright 2008-2014 by Louis Lagendijk
*
- * based on:
+ * based on:
*
- * Common run loop APIs for the Common UNIX Printing System (CUPS).
+ * Common run loop APIs for the Common UNIX Printing System (CUPS).
*
- * Copyright 2007-2008 by Apple Inc.
- * Copyright 2006-2007 by Easy Software Products, all rights reserved.
+ * Copyright 2007-2008 by Apple Inc.
+ * Copyright 2006-2007 by Easy Software Products, all rights reserved.
*
- * These coded instructions, statements, and computer programs are the
- * property of Apple Inc. and are protected by Federal copyright
- * law. Distribution and use rights are outlined in the file "LICENSE.txt"
- * "LICENSE" which should have been included with this file. If this
- * file is missing or damaged, see the license at "http://www.cups.org/".
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 only.
*
- * This file is subject to the Apple OS-Developed Software exception.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
*
- * Contents:
- *
- * backendRunLoop() - Read and write print and back-channel data.
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
@@ -40,440 +40,396 @@
* 'backendRunLoop()' - Read and write print and back-channel data.
*/
-ssize_t /* O - Total bytes on success, -1 on error */
-bjnp_backendRunLoop (int print_fd, /* I - Print file descriptor */
- int device_fd, /* I - Device file descriptor */
- http_addr_t * addr)
- /* I - address for printer */
+ssize_t /* O - Total bytes on success, */
+/* -1 on error */
+bjnp_backendRunLoop(int print_fd, /* I - Print file descriptor */
+ printer_t *printer) /* I - address for printer */
{
- int send_keep_alive; /* flag that an empty data packet should be sent to printer */
- int nfds; /* Maximum file descriptor value + 1 */
- fd_set input, /* Input set for reading */
- output; /* Output set for writing */
- ssize_t print_bytes, /* Print bytes read */
- total_bytes, /* Total bytes written */
- bytes; /* Bytes written */
- int result; /* result code from select */
- int paperout, /* "Paper out" status */
- ack_pending; /* io slot status */
- int offline; /* "Off-line" status */
- int draining; /* Drain command recieved? */
- char print_buffer[BJNP_PRINTBUF_MAX],
- /* Print data buffer */
- *print_ptr; /* Pointer into print data buffer */
- struct timeval timeout;
+ int send_keep_alive; /* flag that an empty data packet */
+ /* should be sent to printer */
+ int nfds; /* Maximum file descriptor value + 1 */
+ int device_fd; /* Device file descriptor */
+ fd_set input, /* Input set for reading */
+ output; /* Output set for writing */
+ ssize_t print_bytes, /* Print bytes read */
+ total_bytes, /* Total bytes written */
+ bytes; /* Bytes written */
+ int result; /* result code from select */
+ int ack_pending; /* io slot status */
+ int draining; /* Drain command received? */
+ char print_buffer[BJNP_PRINTBUF_MAX], /* Print data buffer */
+ *print_ptr; /* Pointer into print data buffer */
+ struct timeval timeout;
#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
- struct sigaction action; /* Actions for POSIX signals */
+ struct sigaction action; /* Actions for POSIX signals */
#endif /* HAVE_SIGACTION && !HAVE_SIGSET */
#if BJNP_CUPS_VERSION >= 103
- int side_channel_open; /* side channel status */
- cups_sc_command_t command; /* Request command */
- cups_sc_status_t status; /* Request/response status */
- char data[16536]; /* Request/response data */
- int datalen; /* Request/response data size */
- char model[BJNP_MODEL_MAX]; /* printer make & model */
- char dev_id[BJNP_IEEE1284_MAX]; /* IEEE1284 device id */
+ int side_channel_open; /* side channel status */
+ cups_sc_command_t command; /* Request command */
+ cups_sc_status_t status; /* Request/response status */
+ char data[16536]; /* Request/response data */
+ int datalen; /* Request/response data size */
+ char model[BJNP_MODEL_MAX]; /* printer make & model */
+ char dev_id[BJNP_IEEE1284_MAX]; /* IEEE1284 device id */
#endif /* cups >= 1.3 */
- fprintf (stderr,
- "DEBUG: bjnp_backendRunLoop(print_fd=%d, device_fd=%d\n",
- print_fd, device_fd);
-
- /*
- * If we are printing data from a print driver on stdin, ignore SIGTERM
- * so that the driver can finish out any page data, e.g. to eject the
- * current page. We only do this for stdin printing as otherwise there
- * is no way to cancel a raw print job...
- */
-
- if (!print_fd)
- {
-#ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
- sigset (SIGTERM, SIG_IGN);
+ fprintf(stderr,
+ "DEBUG: bjnp_backendRunLoop(print_fd=%d\n", print_fd);
+
+ /*
+ * If we are printing data from a print driver on stdin, ignore SIGTERM
+ * so that the driver can finish out any page data, e.g. to eject the
+ * current page. We only do this for stdin printing as otherwise there
+ * is no way to cancel a raw print job...
+ */
+
+ if (!print_fd) {
+#ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
+ sigset(SIGTERM, SIG_IGN);
#elif defined(HAVE_SIGACTION)
- memset (&action, 0, sizeof (action));
+ memset(&action, 0, sizeof(action));
- sigemptyset (&action.sa_mask);
- action.sa_handler = SIG_IGN;
- sigaction (SIGTERM, &action, NULL);
+ sigemptyset(&action.sa_mask);
+ action.sa_handler = SIG_IGN;
+ sigaction(SIGTERM, &action, NULL);
#else
- signal (SIGTERM, SIG_IGN);
+ signal(SIGTERM, SIG_IGN);
#endif /* HAVE_SIGSET */
}
- /*
- * Figure out the maximum file descriptor value to use with select()...
- */
+ /*
+ * Figure out the maximum file descriptor value to use with select()...
+ */
- nfds = (print_fd > device_fd ? print_fd : device_fd) + 1;
+ device_fd = bjnp_get_device_fd(printer);
+ nfds = (print_fd > device_fd ? print_fd : device_fd) + 1;
- print_bytes = 0;
- print_ptr = print_buffer,
- offline = -1;
- paperout = -1;
- total_bytes = 0;
- ack_pending = 0;
- draining = 0;
- send_keep_alive = 0;
- side_channel_open = 1;
+ print_bytes = 0;
+ print_ptr = print_buffer,
+ total_bytes = 0;
+ ack_pending = 0;
+ draining = 0;
+ send_keep_alive = 0;
+ side_channel_open = 1;
+ int last_marker_level_time = time(NULL);
- /*
- * Now loop until we are out of data from print_fd...
- */
+ /*
+ * Now loop until we are out of data from print_fd...
+ */
- while (1)
- {
- /*
- * Use select() to determine whether we have data to copy around...
- */
+ while (1) {
+ /*
+ * Use select() to determine whether we have data to copy around...
+ */
- FD_ZERO (&input);
- FD_ZERO (&output);
+ FD_ZERO(&input);
+ FD_ZERO(&output);
- /*
- * Accept new printdata only when no data is left
- */
+ /*
+ * Accept new printdata only when no data is left
+ */
- if (!print_bytes)
- FD_SET (print_fd, &input);
+ if (!print_bytes) {
+ FD_SET(print_fd, &input);
+ }
- /*
- * accept backchannel data from printer, used for acks
- */
+ /*
+ * accept backchannel data from printer, used for acks
+ */
- FD_SET (device_fd, &input);
+ FD_SET(device_fd, &input);
- /*
- * Accept side channel data, unless there is print data pending (cups >= 1.3)
- */
+ /*
+ * Accept side channel data, unless there is print data pending
+ * (cups >= 1.3)
+ */
#if BJNP_CUPS_VERSION >= 103
- if (side_channel_open && !print_bytes && !draining)
- FD_SET (CUPS_SC_FD, &input);
+
+ if (side_channel_open && !print_bytes && !draining) {
+ FD_SET(CUPS_SC_FD, &input);
+ }
+
#endif
- /*
- * Check if printer is ready to receive data when we have something to send
- * (printdata is left or keep-alive is to be sent) but no ack is pending
- */
-
- if ((send_keep_alive || print_bytes) && !ack_pending)
- FD_SET (device_fd, &output);
-
- timeout.tv_sec = KEEP_ALIVE_SECONDS;
-
- timeout.tv_usec = 0;
-
- result = select (nfds, &input, &output, NULL, &timeout);
-
- if (result < 0)
- {
- /*
- * Pause printing to clear any pending errors...
- */
-
- if (errno == ENXIO && offline != 1)
- {
- fputs ("STATE: +offline-error\n", stderr);
- _cupsLangPuts (stderr,
- _("INFO: Printer is currently off-line.\n"));
- offline = 1;
- }
- else if (errno == EINTR && total_bytes == 0)
- {
- fputs ("DEBUG: Received an interrupt before any bytes were "
- "written, aborting!\n", stderr);
- return (0);
- }
-
- sleep (1);
- continue;
- }
- if (result == 0)
- {
- /*
- * timeout - no data for printer; make sure that next time we
- * send a keep-alive packet to avoid that connection to printer
- * times out
- */
- if (!ack_pending)
- send_keep_alive = 1;
-
- bjnp_debug (LOG_DEBUG,
- "bjnp_runloop: select timeout send_keep_alive=%d print_fd=%d "
- "device_fd=%d print_bytes=%d ack_pending=%d\n",
- send_keep_alive, print_fd, device_fd, print_bytes,
- ack_pending);
- continue;
- }
+ /*
+ * Check if printer is ready to receive data when we have something to
+ * send (printdata is left or keep-alive is to be sent) but no ack is
+ * pending
+ */
-#if BJNP_CUPS_VERSION >= 103
+ if ((send_keep_alive || print_bytes) && !ack_pending) {
+ FD_SET(device_fd, &output);
+ }
- /*
- * Check if we have a side-channel request ready (cups >= 1.3)...
- */
+ timeout.tv_sec = KEEP_ALIVE_SECONDS;
- if (FD_ISSET (CUPS_SC_FD, &input))
- {
- /*
- * Do the side-channel request
- */
+ timeout.tv_usec = 0;
- datalen = sizeof (data) -1;
+ result = select(nfds, &input, &output, NULL, &timeout);
- if (cupsSideChannelRead (&command, &status, data, &datalen, 1.0) != 0)
- {
- /* side channel is closed, or we lost synchronization */
- side_channel_open = 0;
+ if (result < 0) {
+ if (errno == EINTR && total_bytes == 0) {
+ fputs("DEBUG: Received an interrupt before any bytes were "
+ "written, aborting!\n", stderr);
+ return 0;
}
- else
- {
- bjnp_debug (LOG_DEBUG, "Received side-channel request, command is %d\n", command);
- switch (command)
- {
- case CUPS_SC_CMD_DRAIN_OUTPUT:
-
- /*
- * Our sockets disable the Nagle algorithm and data is sent immediately.
- *
- */
-
- draining = 1;
-
- /* we will do cupsSideChannelWrite() once there is no data left ! */
- break;
-
- case CUPS_SC_CMD_GET_BIDI:
- status = CUPS_SC_STATUS_OK;
- data[0] = CUPS_SC_BIDI_NOT_SUPPORTED;
- datalen = 1;
- cupsSideChannelWrite (command, status, data, datalen, 1.0);
- break;
-
- case CUPS_SC_CMD_GET_DEVICE_ID:
- if (bjnp_backendGetDeviceID
- (dev_id, sizeof (dev_id), model, sizeof (model)) == 0)
- {
- status = CUPS_SC_STATUS_OK;
- strncpy (data, dev_id, sizeof (data));
- datalen = (int) strlen (data);
- }
- else
- {
- status = CUPS_SC_STATUS_IO_ERROR;
- datalen = 0;
- }
- cupsSideChannelWrite (command, status, data, datalen, 1.0);
- break;
+
+ sleep(1);
+ continue;
+ }
+
+ /* report ink/marker levels when interval has expired */
+
+ if (time(NULL) - last_marker_level_time > BJNP_REPORT_INTERVAL) {
+ bjnp_report_levels(printer);
+ last_marker_level_time = time(NULL);
+ }
+
+ if (result == 0) {
+ /*
+ * timeout - no data for printer; make sure that next time we
+ * send a keep-alive packet to avoid that connection to printer
+ * times out
+ */
+ if (!ack_pending) {
+ send_keep_alive = 1;
+ }
+
+ bjnp_debug(LOG_DEBUG,
+ "bjnp_runloop: select timeout send_keep_alive=%d print_fd=%d "
+ "device_fd=%d print_bytes=%d ack_pending=%d\n",
+ send_keep_alive, print_fd, device_fd, print_bytes,
+ ack_pending);
+ continue;
+ }
+
+#if BJNP_CUPS_VERSION >= 103
+
+ /*
+ * Check if we have a side-channel request ready (cups >= 1.3)...
+ */
+
+ if (FD_ISSET(CUPS_SC_FD, &input)) {
+ /*
+ * Do the side-channel request
+ */
+
+ datalen = sizeof(data) - 1;
+
+ if (cupsSideChannelRead(&command, &status, data, &datalen, 1.0) !=
+ 0) {
+ /* side channel is closed, or we lost synchronization */
+ side_channel_open = 0;
+ } else {
+ bjnp_debug(LOG_DEBUG, "Received side-channel request, command is %d\n",
+ command);
+
+ switch (command) {
+ case CUPS_SC_CMD_DRAIN_OUTPUT:
+
+ /*
+ * Our sockets disable the Nagle algorithm and data is
+ * sent immediately.
+ *
+ */
+
+ draining = 1;
+
+ /*
+ * we will do cupsSideChannelWrite() once there is no
+ * data left !
+ */
+ break;
+
+ case CUPS_SC_CMD_GET_BIDI:
+ status = CUPS_SC_STATUS_OK;
+ data[0] = CUPS_SC_BIDI_NOT_SUPPORTED;
+ datalen = 1;
+ cupsSideChannelWrite(command, status, data, datalen,
+ 1.0);
+ break;
+
+ case CUPS_SC_CMD_GET_DEVICE_ID:
+ if (bjnp_backendGetDeviceID
+ (printer, dev_id, sizeof(dev_id), model,
+ sizeof(model)) == 0) {
+ status = CUPS_SC_STATUS_OK;
+ strncpy(data, dev_id, sizeof(data));
+ datalen = (int) strlen(data);
+ } else {
+ status = CUPS_SC_STATUS_IO_ERROR;
+ datalen = 0;
+ }
+
+ cupsSideChannelWrite(command, status, data, datalen,
+ 1.0);
+ break;
#if BJNP_CUPS_VERSION >= 105
- case CUPS_SC_CMD_GET_CONNECTED:
- status = CUPS_SC_STATUS_OK;
- data[0] = (device_fd != -1);
- datalen = 1;
- break;
+
+ case CUPS_SC_CMD_GET_CONNECTED:
+ status = CUPS_SC_STATUS_OK;
+ data[0] = (device_fd != -1);
+ datalen = 1;
+ break;
#endif
+ default:
+
+ /*
+ * this covers the following values
+ *
+ * case CUPS_SC_CMD_GET_STATE:
+ * case CUPS_SC_CMD_SOFT_RESET:
+ *
+ * for CUPS 1.4 and later
+ *
+ * case CUPS_SC_CMD_SNMP_GET:
+ * case CUPS_SC_CMD_SNMP_GET_NEXT:
+ *
+ * these values should not occur
+ * case CUPS_SC_CMD_NONE:
+ * case CUPS_SC_CMD_MAX:
+ */
+
+ status = CUPS_SC_STATUS_NOT_IMPLEMENTED;
+ datalen = 0;
+ cupsSideChannelWrite(command, status, data, datalen,
+ 1.0);
+ break;
+ }
+
+ }
+ }
+
+#endif
+ /*
+ * Check if we have back-channel data (ack) ready...
+ */
+
+ if (FD_ISSET(device_fd, &input)) {
+ switch (bjnp_backchannel(printer, &bytes)) {
+ case BJNP_IO_ERROR:
+ if (errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR) {
+ perror("ERROR: failed to read backchannel data");
+ return -1;
+ }
+
+ break;
+
+ case BJNP_OK:
+ print_bytes -= bytes;
+ print_ptr += bytes;
+ total_bytes += bytes;
+ ack_pending = 0;
+
+ /*
+ * Success, reset error conditions
+ */
+
+ fprintf(stderr, "DEBUG: Wrote %d bytes of print data...\n",
+ (int) bytes);
+ break;
+
+ case BJNP_THROTTLE:
+
+ /*
+ * Data not accepted by printer, check paper out condition
+ */
+
+ ack_pending = 0;
+ break;
+
+ case BJNP_NOT_AN_ACK:
+ /* what we received was not an ack, no action */
+ break;
+
default:
+ /* no action */
+ break;
+ }
+ }
+
+ /*
+ * Check if we have print data ready...
+ */
- /*
- * this covers the following values
- *
- * case CUPS_SC_CMD_GET_STATE:
- * case CUPS_SC_CMD_SOFT_RESET:
- *
- * for CUPS 1.4 and later
- *
- * case CUPS_SC_CMD_SNMP_GET:
- * case CUPS_SC_CMD_SNMP_GET_NEXT:
- *
- * these values should not occur
- * case CUPS_SC_CMD_NONE:
- * case CUPS_SC_CMD_MAX:
+ if (FD_ISSET(print_fd, &input)) {
+ if ((print_bytes = read(print_fd, print_buffer,
+ sizeof(print_buffer))) < 0) {
+ /*
+ * Read error - bail if we don't see EAGAIN or EINTR...
*/
- status = CUPS_SC_STATUS_NOT_IMPLEMENTED;
- datalen = 0;
- cupsSideChannelWrite (command, status, data, datalen, 1.0);
- break;
- }
+ if (errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR) {
+ perror("ERROR: Unable to read print data");
+ return -1;
+ }
- }
- }
-#endif
- /*
- * Check if we have back-channel data (ack) ready...
- */
-
- if (FD_ISSET (device_fd, &input))
- {
- result = bjnp_backchannel (device_fd, &bytes);
- switch (result)
- {
- case BJNP_IO_ERROR:
- perror ("ERROR: failed to read backchannel data");
- return (-1);
- break;
- case BJNP_OK:
- print_bytes -= bytes;
- print_ptr += bytes;
- total_bytes += bytes;
- ack_pending = 0;
-
- /*
- * Success, reset paper out error conditions
- */
-
- if (paperout)
- {
- fputs ("STATE: -media-empty-error\n", stderr);
- paperout = 0;
- }
-
- fprintf (stderr, "DEBUG: Wrote %d bytes of print data...\n",
- (int) bytes);
- break;
- case BJNP_THROTTLE:
- /*
- * Data not accepted by printer, check paper out condition
- */
-
- if ((paperout != 1)
- && (bjnp_get_paper_status (addr) == BJNP_PAPER_OUT))
- {
- fputs ("STATE: +media-empty-error\n", stderr);
- _cupsLangPuts (stderr, _("ERROR: Out of paper!\n"));
- paperout = 1;
- }
- ack_pending = 0;
- break;
-
- case BJNP_NOT_AN_ACK:
- /* what we received was not an ack, no action */
- break;
-
- default:
- /* no action */
- break;
- }
- }
-
- /*
- * Check if we have print data ready...
- */
-
- if (FD_ISSET (print_fd, &input))
- {
- if ((print_bytes = read (print_fd, print_buffer,
- sizeof (print_buffer))) < 0)
- {
- /*
- * Read error - bail if we don't see EAGAIN or EINTR...
- */
-
- if (errno != EAGAIN || errno != EINTR)
- {
- perror ("ERROR: Unable to read print data");
- return (-1);
- }
-
- print_bytes = 0;
- }
- else if (print_bytes == 0)
- {
- /*
- * End of input file, break out of the loop
- */
+ print_bytes = 0;
+ } else if (print_bytes == 0) {
+ /*
+ * End of input file, break out of the loop
+ */
#if BJNP_CUPS_VERSION >= 103
- if (draining)
- {
- command = CUPS_SC_CMD_DRAIN_OUTPUT;
- status = CUPS_SC_STATUS_OK;
- datalen = 0;
- cupsSideChannelWrite (command, status, data, datalen, 1.0);
- draining = 0;
- }
+ if (draining) {
+ command = CUPS_SC_CMD_DRAIN_OUTPUT;
+ status = CUPS_SC_STATUS_OK;
+ datalen = 0;
+ cupsSideChannelWrite(command, status, data, datalen, 1.0);
+ draining = 0;
+ }
+
#endif
- break;
- }
- else
- {
- print_ptr = print_buffer;
-
- fprintf (stderr, "DEBUG: Read %d bytes of print data...\n",
- (int) print_bytes);
- }
- }
-
- /*
- * Check if the device is ready to receive data and we have data to
- * send...
- */
-
- if ((send_keep_alive || print_bytes) && FD_ISSET (device_fd, &output))
- {
- bytes = bjnp_write (device_fd, print_ptr, print_bytes);
- send_keep_alive = 0;
- if (bytes < 0)
- {
- /*
- * Write error - bail if we don't see an error we can retry...
- */
-
- if (errno == ENOSPC)
- {
- if (paperout != 1)
- {
- fputs ("STATE: +media-empty-error\n", stderr);
- _cupsLangPuts (stderr, _("ERROR: Out of paper!\n"));
- paperout = 1;
- }
- }
- else if (errno == ENXIO)
- {
- if (offline != 1)
- {
- fputs ("STATE: +offline-error\n", stderr);
- _cupsLangPuts (stderr,
- _
- ("INFO: Printer is currently off-line.\n"));
- offline = 1;
- }
- }
- else if (errno != !EAGAIN && errno != EINTR && errno != ENOTTY)
- {
- fprintf (stderr,
- _("ERROR: Unable to write print data: %s\n"),
- strerror (errno));
- return (-1);
- }
- }
- else
- {
- if (offline)
- {
- fputs ("STATE: -offline-error\n", stderr);
- _cupsLangPuts (stderr,
- _("INFO: Printer is now on-line.\n"));
- offline = 0;
- }
-
- /*
- * we sent data, wait for the ack before sending more data
- */
-
- ack_pending = 1;
- }
- }
+ break;
+ } else {
+ print_ptr = print_buffer;
+
+ fprintf(stderr, "DEBUG: Read %d bytes of print data...\n",
+ (int) print_bytes);
+ }
+ }
+
+ /*
+ * Check if the device is ready to receive data and we have data to
+ * send...
+ */
+
+ if ((send_keep_alive || print_bytes) && FD_ISSET(device_fd, &output)) {
+ bytes = bjnp_write(printer, print_ptr, print_bytes);
+ send_keep_alive = 0;
+
+ if (bytes < 0) {
+ /*
+ * Write error - bail if we don't see an error we can retry...
+ */
+
+ if (errno != !EAGAIN && errno != EWOULDBLOCK &&
+ errno != EINTR && errno != ENOTTY && errno != ENOSPC) {
+ fprintf(stderr,
+ _("ERROR: Unable to write print data: %s\n"),
+ strerror(errno));
+ return -1;
+ }
+ } else {
+
+ /*
+ * we sent data, wait for the ack before sending more data
+ */
+
+ ack_pending = 1;
+ }
+ }
}
- /*
- * Return with success...
- */
+ /*
+ * Return with success...
+ */
- return (total_bytes);
+ return (total_bytes);
}