/* * * bjnp specifc run loop APIs for the Common UNIX Printing System (CUPS). * Copyright 2008-2014 by Louis Lagendijk * * based on: * * 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. * * 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 or later. * * 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. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /* * Include necessary headers. */ #include "bjnp.h" #ifdef __hpux # include #else # include #endif /* __hpux */ #include #include #include /* * '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 */ 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 */ 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 */ #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 */ #endif /* cups >= 1.3 */ 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)); sigemptyset(&action.sa_mask); action.sa_handler = SIG_IGN; sigaction(SIGTERM, &action, NULL); #else signal(SIGTERM, SIG_IGN); #endif /* HAVE_SIGSET */ } /* * Figure out the maximum file descriptor value to use with select()... */ device_fd = bjnp_get_device_fd(printer); nfds = (print_fd > device_fd ? print_fd : device_fd) + 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... */ while (1) { /* * Use select() to determine whether we have data to copy around... */ FD_ZERO(&input); FD_ZERO(&output); /* * Accept new printdata only when no data is left */ if (!print_bytes) { FD_SET(print_fd, &input); } /* * accept backchannel data from printer, used for acks */ FD_SET(device_fd, &input); /* * 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); } #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) { if (errno == EINTR && total_bytes == 0) { fputs("DEBUG: Received an interrupt before any bytes were " "written, aborting!\n", stderr); return 0; } 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; #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... */ 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 != EWOULDBLOCK && 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 */ #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; } #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(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 (total_bytes); }