/* * "$Id: sidechannel.c 6649 2007-07-11 21:46:42Z mike $" * * Side-channel API code for the Common UNIX Printing System (CUPS). * * Copyright 2007 by Apple Inc. * Copyright 2006 by Easy Software Products. * * 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" * which should have been included with this file. If this file is * file is missing or damaged, see the license at "http://www.cups.org/". * * This file is subject to the Apple OS-Developed Software exception. * * Contents: * * cupsSideChannelDoRequest() - Send a side-channel command to a backend * and wait for a response. * cupsSideChannelRead() - Read a side-channel message. * cupsSideChannelWrite() - Write a side-channel message. */ /* * Include necessary headers... */ #include "sidechannel.h" #include "string.h" #include <unistd.h> #include <errno.h> #ifdef __hpux # include <sys/time.h> #else # include <sys/select.h> #endif /* __hpux */ #ifndef WIN32 # include <sys/time.h> #endif /* !WIN32 */ #ifdef HAVE_POLL # include <sys/poll.h> #endif /* HAVE_POLL */ /* * 'cupsSideChannelDoRequest()' - Send a side-channel command to a backend and wait for a response. * * This function is normally only called by filters, drivers, or port * monitors in order to communicate with the backend used by the current * printer. Programs must be prepared to handle timeout or "not * implemented" status codes, which indicate that the backend or device * do not support the specified side-channel command. * * The "datalen" parameter must be initialized to the size of the buffer * pointed to by the "data" parameter. cupsSideChannelDoRequest() will * update the value to contain the number of data bytes in the buffer. * * @since CUPS 1.3@ */ cups_sc_status_t /* O - Status of command */ cupsSideChannelDoRequest( cups_sc_command_t command, /* I - Command to send */ char *data, /* O - Response data buffer pointer */ int *datalen, /* IO - Size of data buffer on entry, number of bytes in buffer on return */ double timeout) /* I - Timeout in seconds */ { cups_sc_status_t status; /* Status of command */ cups_sc_command_t rcommand; /* Response command */ if (cupsSideChannelWrite(command, CUPS_SC_STATUS_NONE, NULL, 0, timeout)) return (CUPS_SC_STATUS_TIMEOUT); if (cupsSideChannelRead(&rcommand, &status, data, datalen, timeout)) return (CUPS_SC_STATUS_TIMEOUT); if (rcommand != command) return (CUPS_SC_STATUS_BAD_MESSAGE); return (status); } /* * 'cupsSideChannelRead()' - Read a side-channel message. * * This function is normally only called by backend programs to read * commands from a filter, driver, or port monitor program. The * caller must be prepared to handle incomplete or invalid messages * and return the corresponding status codes. * * The "datalen" parameter must be initialized to the size of the buffer * pointed to by the "data" parameter. cupsSideChannelDoRequest() will * update the value to contain the number of data bytes in the buffer. * * @since CUPS 1.3@ */ int /* O - 0 on success, -1 on error */ cupsSideChannelRead( cups_sc_command_t *command, /* O - Command code */ cups_sc_status_t *status, /* O - Status code */ char *data, /* O - Data buffer pointer */ int *datalen, /* IO - Size of data buffer on entry, number of bytes in buffer on return */ double timeout) /* I - Timeout in seconds */ { char buffer[16388]; /* Message buffer */ int bytes; /* Bytes read */ int templen; /* Data length from message */ #ifdef HAVE_POLL struct pollfd pfd; /* Poll structure for poll() */ #else /* select() */ fd_set input_set; /* Input set for select() */ struct timeval stimeout; /* Timeout value for select() */ #endif /* HAVE_POLL */ /* * Range check input... */ if (!command || !status) return (-1); /* * See if we have pending data on the side-channel socket... */ #ifdef HAVE_POLL pfd.fd = CUPS_SC_FD; pfd.events = POLLIN; if (timeout < 0.0) { if (poll(&pfd, 1, -1) < 1) return (-1); } else if (poll(&pfd, 1, (long)(timeout * 1000)) < 1) return (-1); #else /* select() */ FD_ZERO(&input_set); FD_SET(CUPS_SC_FD, &input_set); if (timeout < 0.0) { if (select(CUPS_SC_FD + 1, &input_set, NULL, NULL, NULL) < 1) return (-1); } else { stimeout.tv_sec = (int)timeout; stimeout.tv_usec = (int)(timeout * 1000000) % 1000000; if (select(CUPS_SC_FD + 1, &input_set, NULL, NULL, &stimeout) < 1) return (-1); } #endif /* HAVE_POLL */ /* * Read a side-channel message for the format: * * Byte(s) Description * ------- ------------------------------------------- * 0 Command code * 1 Status code * 2-3 Data length (network byte order) <= 16384 * 4-N Data */ while ((bytes = read(CUPS_SC_FD, buffer, sizeof(buffer))) < 0) if (errno != EINTR && errno != EAGAIN) return (-1); /* * Validate the command code in the message... */ if (buffer[0] < CUPS_SC_CMD_SOFT_RESET || buffer[0] > CUPS_SC_CMD_GET_STATE) return (-1); *command = (cups_sc_command_t)buffer[0]; /* * Validate the data length in the message... */ templen = ((buffer[2] & 255) << 8) | (buffer[3] & 255); if (templen > 0 && (!data || !datalen)) { /* * Either the response is bigger than the provided buffer or the * response is bigger than we've read... */ *status = CUPS_SC_STATUS_TOO_BIG; } else if (templen > *datalen || templen > (bytes - 4)) { /* * Either the response is bigger than the provided buffer or the * response is bigger than we've read... */ *status = CUPS_SC_STATUS_TOO_BIG; } else { /* * The response data will fit, copy it over and provide the actual * length... */ *status = (cups_sc_status_t)buffer[1]; *datalen = templen; memcpy(data, buffer + 4, templen); } return (0); } /* * 'cupsSideChannelWrite()' - Write a side-channel message. * * This function is normally only called by backend programs to send * responses to a filter, driver, or port monitor program. * * @since CUPS 1.3@ */ int /* O - 0 on success, -1 on error */ cupsSideChannelWrite( cups_sc_command_t command, /* I - Command code */ cups_sc_status_t status, /* I - Status code */ const char *data, /* I - Data buffer pointer */ int datalen, /* I - Number of bytes of data */ double timeout) /* I - Timeout in seconds */ { char buffer[16388]; /* Message buffer */ int bytes; /* Bytes written */ #ifdef HAVE_POLL struct pollfd pfd; /* Poll structure for poll() */ #else /* select() */ fd_set output_set; /* Output set for select() */ struct timeval stimeout; /* Timeout value for select() */ #endif /* HAVE_POLL */ /* * Range check input... */ if (command < CUPS_SC_CMD_SOFT_RESET || command > CUPS_SC_CMD_GET_STATE || datalen < 0 || datalen > 16384 || (datalen > 0 && !data)) return (-1); /* * See if we can safely write to the side-channel socket... */ #ifdef HAVE_POLL pfd.fd = CUPS_SC_FD; pfd.events = POLLOUT; if (timeout < 0.0) { if (poll(&pfd, 1, -1) < 1) return (-1); } else if (poll(&pfd, 1, (long)(timeout * 1000)) < 1) return (-1); #else /* select() */ FD_ZERO(&output_set); FD_SET(CUPS_SC_FD, &output_set); if (timeout < 0.0) { if (select(CUPS_SC_FD + 1, NULL, &output_set, NULL, NULL) < 1) return (-1); } else { stimeout.tv_sec = (int)timeout; stimeout.tv_usec = (int)(timeout * 1000000) % 1000000; if (select(CUPS_SC_FD + 1, NULL, &output_set, NULL, &stimeout) < 1) return (-1); } #endif /* HAVE_POLL */ /* * Write a side-channel message in the format: * * Byte(s) Description * ------- ------------------------------------------- * 0 Command code * 1 Status code * 2-3 Data length (network byte order) <= 16384 * 4-N Data */ buffer[0] = command; buffer[1] = status; buffer[2] = datalen >> 8; buffer[3] = datalen & 255; bytes = 4; if (datalen > 0) { memcpy(buffer + 4, data, datalen); bytes += datalen; } while (write(CUPS_SC_FD, buffer, bytes) < 0) if (errno != EINTR && errno != EAGAIN) return (-1); return (0); } /* * End of "$Id: sidechannel.c 6649 2007-07-11 21:46:42Z mike $". */