#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
#ifdef HAVE_POLL
# include <poll.h>
#endif
#define _CUPS_SC_MAX_DATA 65535
#define _CUPS_SC_MAX_BUFFER 65540
cups_sc_status_t
cupsSideChannelDoRequest(
cups_sc_command_t command,
char *data,
int *datalen,
double timeout)
{
cups_sc_status_t status;
cups_sc_command_t rcommand;
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);
}
int
cupsSideChannelRead(
cups_sc_command_t *command,
cups_sc_status_t *status,
char *data,
int *datalen,
double timeout)
{
char *buffer;
ssize_t bytes;
int templen;
int nfds;
#ifdef HAVE_POLL
struct pollfd pfd;
#else
fd_set input_set;
struct timeval stimeout;
#endif
DEBUG_printf(("cupsSideChannelRead(command=%p, status=%p, data=%p, "
"datalen=%p(%d), timeout=%.3f)", command, status, data,
datalen, datalen ? *datalen : -1, timeout));
if (!command || !status)
return (-1);
#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
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
if (nfds < 1)
{
*command = CUPS_SC_CMD_NONE;
*status = nfds==0 ? CUPS_SC_STATUS_TIMEOUT : CUPS_SC_STATUS_IO_ERROR;
return (-1);
}
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);
}
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);
}
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];
templen = ((buffer[2] & 255) << 8) | (buffer[3] & 255);
if (templen > 0 && (!data || !datalen))
{
*status = CUPS_SC_STATUS_TOO_BIG;
}
else if (!datalen || templen > *datalen || templen > (bytes - 4))
{
*status = CUPS_SC_STATUS_TOO_BIG;
}
else
{
*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);
}
cups_sc_status_t
cupsSideChannelSNMPGet(
const char *oid,
char *data,
int *datalen,
double timeout)
{
cups_sc_status_t status;
cups_sc_command_t rcommand;
char *real_data;
int real_datalen,
real_oidlen;
DEBUG_printf(("cupsSideChannelSNMPGet(oid=\"%s\", data=%p, datalen=%p(%d), "
"timeout=%.3f)", oid, data, datalen, datalen ? *datalen : -1,
timeout));
if (!oid || !*oid || !data || !datalen || *datalen < 2)
return (CUPS_SC_STATUS_BAD_MESSAGE);
*data = '\0';
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)
{
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);
}
cups_sc_status_t
cupsSideChannelSNMPWalk(
const char *oid,
double timeout,
cups_sc_walk_func_t cb,
void *context)
{
cups_sc_status_t status;
cups_sc_command_t rcommand;
char *real_data;
int real_datalen;
size_t real_oidlen,
oidlen;
const char *current_oid;
char last_oid[2048];
DEBUG_printf(("cupsSideChannelSNMPWalk(oid=\"%s\", timeout=%.3f, cb=%p, "
"context=%p)", oid, timeout, cb, context));
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);
current_oid = oid;
oidlen = strlen(oid);
last_oid[0] = '\0';
do
{
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)
{
if (strncmp(real_data, oid, oidlen) || real_data[oidlen] != '.' ||
!strcmp(real_data, last_oid))
{
_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;
(*cb)(real_data, real_data + real_oidlen, real_datalen, context);
current_oid = real_data;
strlcpy(last_oid, current_oid, sizeof(last_oid));
}
}
while (status == CUPS_SC_STATUS_OK);
_cupsBufferRelease(real_data);
return (status);
}
int
cupsSideChannelWrite(
cups_sc_command_t command,
cups_sc_status_t status,
const char *data,
int datalen,
double timeout)
{
char *buffer;
ssize_t bytes;
#ifdef HAVE_POLL
struct pollfd pfd;
#else
fd_set output_set;
struct timeval stimeout;
#endif
if (command < CUPS_SC_CMD_SOFT_RESET || command >= CUPS_SC_CMD_MAX ||
datalen < 0 || datalen > _CUPS_SC_MAX_DATA || (datalen > 0 && !data))
return (-1);
#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
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
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);
}