|
|
/*
|
|
|
* Side-channel API code for CUPS.
|
|
|
*
|
|
|
* Copyright © 2007-2019 by Apple Inc.
|
|
|
* Copyright © 2006 by Easy Software Products.
|
|
|
*
|
|
|
* Licensed under Apache License v2.0. See the file "LICENSE" for more
|
|
|
* information.
|
|
|
*/
|
|
|
|
|
|
/*
|
|
|
* Include necessary headers...
|
|
|
*/
|
|
|
|
|
|
#include "sidechannel.h"
|
|
|
#include "cups-private.h"
|
|
|
#include "debug-internal.h"
|
|
|
#ifdef _WIN32
|
|
|
# include <io.h>
|
|
|
#else
|
|
|
# include <unistd.h>
|
|
|
# include <sys/select.h>
|
|
|
# include <sys/time.h>
|
|
|
#endif /* _WIN32 */
|
|
|
#ifdef HAVE_POLL
|
|
|
# include <poll.h>
|
|
|
#endif /* HAVE_POLL */
|
|
|
|
|
|
|
|
|
/*
|
|
|
* Buffer size for side-channel requests...
|
|
|
*/
|
|
|
|
|
|
#define _CUPS_SC_MAX_DATA 65535
|
|
|
#define _CUPS_SC_MAX_BUFFER 65540
|
|
|
|
|
|
|
|
|
/*
|
|
|
* '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/macOS 10.5@
|
|
|
*/
|
|
|
|
|
|
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/macOS 10.5@
|
|
|
*/
|
|
|
|
|
|
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; /* Message buffer */
|
|
|
ssize_t bytes; /* Bytes read */
|
|
|
int templen; /* Data length from message */
|
|
|
int nfds; /* Number of file descriptors */
|
|
|
#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 */
|
|
|
|
|
|
|
|
|
DEBUG_printf(("cupsSideChannelRead(command=%p, status=%p, data=%p, "
|
|
|
"datalen=%p(%d), timeout=%.3f)", command, status, data,
|
|
|
datalen, datalen ? *datalen : -1, timeout));
|
|
|
|
|
|
/*
|
|
|
* 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;
|
|
|
|
|
|
while ((nfds = poll(&pfd, 1,
|
|
|
timeout < 0.0 ? -1 : (int)(timeout * 1000))) < 0 &&
|
|
|
(errno == EINTR || errno == EAGAIN))
|
|
|
;
|
|
|
|
|
|
#else /* select() */
|
|
|
FD_ZERO(&input_set);
|
|
|
FD_SET(CUPS_SC_FD, &input_set);
|
|
|
|
|
|
stimeout.tv_sec = (int)timeout;
|
|
|
stimeout.tv_usec = (int)(timeout * 1000000) % 1000000;
|
|
|
|
|
|
while ((nfds = select(CUPS_SC_FD + 1, &input_set, NULL, NULL,
|
|
|
timeout < 0.0 ? NULL : &stimeout)) < 0 &&
|
|
|
(errno == EINTR || errno == EAGAIN))
|
|
|
;
|
|
|
|
|
|
#endif /* HAVE_POLL */
|
|
|
|
|
|
if (nfds < 1)
|
|
|
{
|
|
|
*command = CUPS_SC_CMD_NONE;
|
|
|
*status = nfds==0 ? CUPS_SC_STATUS_TIMEOUT : CUPS_SC_STATUS_IO_ERROR;
|
|
|
return (-1);
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
* Read a side-channel message for the format:
|
|
|
*
|
|
|
* Byte(s) Description
|
|
|
* ------- -------------------------------------------
|
|
|
* 0 Command code
|
|
|
* 1 Status code
|
|
|
* 2-3 Data length (network byte order)
|
|
|
* 4-N Data
|
|
|
*/
|
|
|
|
|
|
if ((buffer = _cupsBufferGet(_CUPS_SC_MAX_BUFFER)) == NULL)
|
|
|
{
|
|
|
*command = CUPS_SC_CMD_NONE;
|
|
|
*status = CUPS_SC_STATUS_TOO_BIG;
|
|
|
|
|
|
return (-1);
|
|
|
}
|
|
|
|
|
|
while ((bytes = read(CUPS_SC_FD, buffer, _CUPS_SC_MAX_BUFFER)) < 0)
|
|
|
if (errno != EINTR && errno != EAGAIN)
|
|
|
{
|
|
|
DEBUG_printf(("1cupsSideChannelRead: Read error: %s", strerror(errno)));
|
|
|
|
|
|
_cupsBufferRelease(buffer);
|
|
|
|
|
|
*command = CUPS_SC_CMD_NONE;
|
|
|
*status = CUPS_SC_STATUS_IO_ERROR;
|
|
|
|
|
|
return (-1);
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
* Watch for EOF or too few bytes...
|
|
|
*/
|
|
|
|
|
|
if (bytes < 4)
|
|
|
{
|
|
|
DEBUG_printf(("1cupsSideChannelRead: Short read of " CUPS_LLFMT " bytes", CUPS_LLCAST bytes));
|
|
|
|
|
|
_cupsBufferRelease(buffer);
|
|
|
|
|
|
*command = CUPS_SC_CMD_NONE;
|
|
|
*status = CUPS_SC_STATUS_BAD_MESSAGE;
|
|
|
|
|
|
return (-1);
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
* Validate the command code in the message...
|
|
|
*/
|
|
|
|
|
|
if (buffer[0] < CUPS_SC_CMD_SOFT_RESET ||
|
|
|
buffer[0] >= CUPS_SC_CMD_MAX)
|
|
|
{
|
|
|
DEBUG_printf(("1cupsSideChannelRead: Bad command %d!", buffer[0]));
|
|
|
|
|
|
_cupsBufferRelease(buffer);
|
|
|
|
|
|
*command = CUPS_SC_CMD_NONE;
|
|
|
*status = CUPS_SC_STATUS_BAD_MESSAGE;
|
|
|
|
|
|
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 (!datalen || 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, (size_t)templen);
|
|
|
}
|
|
|
|
|
|
_cupsBufferRelease(buffer);
|
|
|
|
|
|
DEBUG_printf(("1cupsSideChannelRead: Returning status=%d", *status));
|
|
|
|
|
|
return (0);
|
|
|
}
|
|
|
|
|
|
|
|
|
/*
|
|
|
* 'cupsSideChannelSNMPGet()' - Query a SNMP OID's value.
|
|
|
*
|
|
|
* This function asks the backend to do a SNMP OID query on behalf of the
|
|
|
* filter, port monitor, or backend using the default community name.
|
|
|
*
|
|
|
* "oid" contains a numeric OID consisting of integers separated by periods,
|
|
|
* for example ".1.3.6.1.2.1.43". Symbolic names from SNMP MIBs are not
|
|
|
* supported and must be converted to their numeric forms.
|
|
|
*
|
|
|
* On input, "data" and "datalen" provide the location and size of the
|
|
|
* buffer to hold the OID value as a string. HEX-String (binary) values are
|
|
|
* converted to hexadecimal strings representing the binary data, while
|
|
|
* NULL-Value and unknown OID types are returned as the empty string.
|
|
|
* The returned "datalen" does not include the trailing nul.
|
|
|
*
|
|
|
* @code CUPS_SC_STATUS_NOT_IMPLEMENTED@ is returned by backends that do not
|
|
|
* support SNMP queries. @code CUPS_SC_STATUS_NO_RESPONSE@ is returned when
|
|
|
* the printer does not respond to the SNMP query.
|
|
|
*
|
|
|
* @since CUPS 1.4/macOS 10.6@
|
|
|
*/
|
|
|
|
|
|
cups_sc_status_t /* O - Query status */
|
|
|
cupsSideChannelSNMPGet(
|
|
|
const char *oid, /* I - OID to query */
|
|
|
char *data, /* I - Buffer for OID value */
|
|
|
int *datalen, /* IO - Size of OID buffer on entry, size of value on return */
|
|
|
double timeout) /* I - Timeout in seconds */
|
|
|
{
|
|
|
cups_sc_status_t status; /* Status of command */
|
|
|
cups_sc_command_t rcommand; /* Response command */
|
|
|
char *real_data; /* Real data buffer for response */
|
|
|
int real_datalen, /* Real length of data buffer */
|
|
|
real_oidlen; /* Length of returned OID string */
|
|
|
|
|
|
|
|
|
DEBUG_printf(("cupsSideChannelSNMPGet(oid=\"%s\", data=%p, datalen=%p(%d), "
|
|
|
"timeout=%.3f)", oid, data, datalen, datalen ? *datalen : -1,
|
|
|
timeout));
|
|
|
|
|
|
/*
|
|
|
* Range check input...
|
|
|
*/
|
|
|
|
|
|
if (!oid || !*oid || !data || !datalen || *datalen < 2)
|
|
|
return (CUPS_SC_STATUS_BAD_MESSAGE);
|
|
|
|
|
|
*data = '\0';
|
|
|
|
|
|
/*
|
|
|
* Send the request to the backend and wait for a response...
|
|
|
*/
|
|
|
|
|
|
if (cupsSideChannelWrite(CUPS_SC_CMD_SNMP_GET, CUPS_SC_STATUS_NONE, oid,
|
|
|
(int)strlen(oid) + 1, timeout))
|
|
|
return (CUPS_SC_STATUS_TIMEOUT);
|
|
|
|
|
|
if ((real_data = _cupsBufferGet(_CUPS_SC_MAX_BUFFER)) == NULL)
|
|
|
return (CUPS_SC_STATUS_TOO_BIG);
|
|
|
|
|
|
real_datalen = _CUPS_SC_MAX_BUFFER;
|
|
|
if (cupsSideChannelRead(&rcommand, &status, real_data, &real_datalen, timeout))
|
|
|
{
|
|
|
_cupsBufferRelease(real_data);
|
|
|
return (CUPS_SC_STATUS_TIMEOUT);
|
|
|
}
|
|
|
|
|
|
if (rcommand != CUPS_SC_CMD_SNMP_GET)
|
|
|
{
|
|
|
_cupsBufferRelease(real_data);
|
|
|
return (CUPS_SC_STATUS_BAD_MESSAGE);
|
|
|
}
|
|
|
|
|
|
if (status == CUPS_SC_STATUS_OK)
|
|
|
{
|
|
|
/*
|
|
|
* Parse the response of the form "oid\0value"...
|
|
|
*/
|
|
|
|
|
|
real_oidlen = (int)strlen(real_data) + 1;
|
|
|
real_datalen -= real_oidlen;
|
|
|
|
|
|
if ((real_datalen + 1) > *datalen)
|
|
|
{
|
|
|
_cupsBufferRelease(real_data);
|
|
|
return (CUPS_SC_STATUS_TOO_BIG);
|
|
|
}
|
|
|
|
|
|
memcpy(data, real_data + real_oidlen, (size_t)real_datalen);
|
|
|
data[real_datalen] = '\0';
|
|
|
|
|
|
*datalen = real_datalen;
|
|
|
}
|
|
|
|
|
|
_cupsBufferRelease(real_data);
|
|
|
|
|
|
return (status);
|
|
|
}
|
|
|
|
|
|
|
|
|
/*
|
|
|
* 'cupsSideChannelSNMPWalk()' - Query multiple SNMP OID values.
|
|
|
*
|
|
|
* This function asks the backend to do multiple SNMP OID queries on behalf
|
|
|
* of the filter, port monitor, or backend using the default community name.
|
|
|
* All OIDs under the "parent" OID are queried and the results are sent to
|
|
|
* the callback function you provide.
|
|
|
*
|
|
|
* "oid" contains a numeric OID consisting of integers separated by periods,
|
|
|
* for example ".1.3.6.1.2.1.43". Symbolic names from SNMP MIBs are not
|
|
|
* supported and must be converted to their numeric forms.
|
|
|
*
|
|
|
* "timeout" specifies the timeout for each OID query. The total amount of
|
|
|
* time will depend on the number of OID values found and the time required
|
|
|
* for each query.
|
|
|
*
|
|
|
* "cb" provides a function to call for every value that is found. "context"
|
|
|
* is an application-defined pointer that is sent to the callback function
|
|
|
* along with the OID and current data. The data passed to the callback is the
|
|
|
* same as returned by @link cupsSideChannelSNMPGet@.
|
|
|
*
|
|
|
* @code CUPS_SC_STATUS_NOT_IMPLEMENTED@ is returned by backends that do not
|
|
|
* support SNMP queries. @code CUPS_SC_STATUS_NO_RESPONSE@ is returned when
|
|
|
* the printer does not respond to the first SNMP query.
|
|
|
*
|
|
|
* @since CUPS 1.4/macOS 10.6@
|
|
|
*/
|
|
|
|
|
|
cups_sc_status_t /* O - Status of first query of @code CUPS_SC_STATUS_OK@ on success */
|
|
|
cupsSideChannelSNMPWalk(
|
|
|
const char *oid, /* I - First numeric OID to query */
|
|
|
double timeout, /* I - Timeout for each query in seconds */
|
|
|
cups_sc_walk_func_t cb, /* I - Function to call with each value */
|
|
|
void *context) /* I - Application-defined pointer to send to callback */
|
|
|
{
|
|
|
cups_sc_status_t status; /* Status of command */
|
|
|
cups_sc_command_t rcommand; /* Response command */
|
|
|
char *real_data; /* Real data buffer for response */
|
|
|
int real_datalen; /* Real length of data buffer */
|
|
|
size_t real_oidlen, /* Length of returned OID string */
|
|
|
oidlen; /* Length of first OID */
|
|
|
const char *current_oid; /* Current OID */
|
|
|
char last_oid[2048]; /* Last OID */
|
|
|
|
|
|
|
|
|
DEBUG_printf(("cupsSideChannelSNMPWalk(oid=\"%s\", timeout=%.3f, cb=%p, "
|
|
|
"context=%p)", oid, timeout, cb, context));
|
|
|
|
|
|
/*
|
|
|
* Range check input...
|
|
|
*/
|
|
|
|
|
|
if (!oid || !*oid || !cb)
|
|
|
return (CUPS_SC_STATUS_BAD_MESSAGE);
|
|
|
|
|
|
if ((real_data = _cupsBufferGet(_CUPS_SC_MAX_BUFFER)) == NULL)
|
|
|
return (CUPS_SC_STATUS_TOO_BIG);
|
|
|
|
|
|
/*
|
|
|
* Loop until the OIDs don't match...
|
|
|
*/
|
|
|
|
|
|
current_oid = oid;
|
|
|
oidlen = strlen(oid);
|
|
|
last_oid[0] = '\0';
|
|
|
|
|
|
do
|
|
|
{
|
|
|
/*
|
|
|
* Send the request to the backend and wait for a response...
|
|
|
*/
|
|
|
|
|
|
if (cupsSideChannelWrite(CUPS_SC_CMD_SNMP_GET_NEXT, CUPS_SC_STATUS_NONE,
|
|
|
current_oid, (int)strlen(current_oid) + 1, timeout))
|
|
|
{
|
|
|
_cupsBufferRelease(real_data);
|
|
|
return (CUPS_SC_STATUS_TIMEOUT);
|
|
|
}
|
|
|
|
|
|
real_datalen = _CUPS_SC_MAX_BUFFER;
|
|
|
if (cupsSideChannelRead(&rcommand, &status, real_data, &real_datalen,
|
|
|
timeout))
|
|
|
{
|
|
|
_cupsBufferRelease(real_data);
|
|
|
return (CUPS_SC_STATUS_TIMEOUT);
|
|
|
}
|
|
|
|
|
|
if (rcommand != CUPS_SC_CMD_SNMP_GET_NEXT)
|
|
|
{
|
|
|
_cupsBufferRelease(real_data);
|
|
|
return (CUPS_SC_STATUS_BAD_MESSAGE);
|
|
|
}
|
|
|
|
|
|
if (status == CUPS_SC_STATUS_OK)
|
|
|
{
|
|
|
/*
|
|
|
* Parse the response of the form "oid\0value"...
|
|
|
*/
|
|
|
|
|
|
if (strncmp(real_data, oid, oidlen) || real_data[oidlen] != '.' ||
|
|
|
!strcmp(real_data, last_oid))
|
|
|
{
|
|
|
/*
|
|
|
* Done with this set of OIDs...
|
|
|
*/
|
|
|
|
|
|
_cupsBufferRelease(real_data);
|
|
|
return (CUPS_SC_STATUS_OK);
|
|
|
}
|
|
|
|
|
|
if ((size_t)real_datalen < sizeof(real_data))
|
|
|
real_data[real_datalen] = '\0';
|
|
|
|
|
|
real_oidlen = strlen(real_data) + 1;
|
|
|
real_datalen -= (int)real_oidlen;
|
|
|
|
|
|
/*
|
|
|
* Call the callback with the OID and data...
|
|
|
*/
|
|
|
|
|
|
(*cb)(real_data, real_data + real_oidlen, real_datalen, context);
|
|
|
|
|
|
/*
|
|
|
* Update the current OID...
|
|
|
*/
|
|
|
|
|
|
current_oid = real_data;
|
|
|
strlcpy(last_oid, current_oid, sizeof(last_oid));
|
|
|
}
|
|
|
}
|
|
|
while (status == CUPS_SC_STATUS_OK);
|
|
|
|
|
|
_cupsBufferRelease(real_data);
|
|
|
|
|
|
return (status);
|
|
|
}
|
|
|
|
|
|
|
|
|
/*
|
|
|
* '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/macOS 10.5@
|
|
|
*/
|
|
|
|
|
|
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; /* Message buffer */
|
|
|
ssize_t 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_MAX ||
|
|
|
datalen < 0 || datalen > _CUPS_SC_MAX_DATA || (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, (int)(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
|
|
|
*/
|
|
|
|
|
|
if ((buffer = _cupsBufferGet((size_t)datalen + 4)) == NULL)
|
|
|
return (-1);
|
|
|
|
|
|
buffer[0] = (char)command;
|
|
|
buffer[1] = (char)status;
|
|
|
buffer[2] = (char)(datalen >> 8);
|
|
|
buffer[3] = (char)(datalen & 255);
|
|
|
|
|
|
bytes = 4;
|
|
|
|
|
|
if (datalen > 0)
|
|
|
{
|
|
|
memcpy(buffer + 4, data, (size_t)datalen);
|
|
|
bytes += datalen;
|
|
|
}
|
|
|
|
|
|
while (write(CUPS_SC_FD, buffer, (size_t)bytes) < 0)
|
|
|
if (errno != EINTR && errno != EAGAIN)
|
|
|
{
|
|
|
_cupsBufferRelease(buffer);
|
|
|
return (-1);
|
|
|
}
|
|
|
|
|
|
_cupsBufferRelease(buffer);
|
|
|
|
|
|
return (0);
|
|
|
}
|