blob: 9f1a64fa8da8797e7409baea5178c21f829c1114 [file] [log] [blame]
/*
* "$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 $".
*/