-CHANGES.txt - 2008-05-15
+CHANGES.txt - 2008-05-19
------------------------
CHANGES IN CUPS V1.4b1
+ - The side-channel API now supports SNMP queries for the
+ standard network backends.
- Added a PageLogFormat directive to the cupsd.conf file to
control the format of lines in the page_log file.
- Filters can now send PPD: messages to stderr to set PPD
#endif /* __hpux */
-/*
- * Local functions...
- */
-
-
-
/*
* 'backendCheckSideChannel()' - Check the side-channel for pending requests.
*/
datalen = 1;
break;
+ case CUPS_SC_CMD_SNMP_GET :
+ case CUPS_SC_CMD_SNMP_GET_NEXT :
+ fprintf(stderr, "DEBUG: CUPS_SC_CMD_SNMP_%s: %d (%s)\n",
+ command == CUPS_SC_CMD_SNMP_GET ? "GET" : "GET_NEXT", datalen,
+ data);
+
+ if (datalen < 2)
+ {
+ status = CUPS_SC_STATUS_BAD_MESSAGE;
+ datalen = 0;
+ break;
+ }
+
+ if (snmp_fd >= 0)
+ {
+ cups_snmp_t packet; /* Packet from printer */
+
+
+ if (!_cupsSNMPStringToOID(data, packet.object_name, CUPS_SNMP_MAX_OID))
+ {
+ status = CUPS_SC_STATUS_BAD_MESSAGE;
+ datalen = 0;
+ break;
+ }
+
+ status = CUPS_SC_STATUS_IO_ERROR;
+ datalen = 0;
+
+ if (_cupsSNMPWrite(snmp_fd, addr, CUPS_SNMP_VERSION_1,
+ _cupsSNMPDefaultCommunity(),
+ command == CUPS_SC_CMD_SNMP_GET ?
+ CUPS_ASN1_GET_REQUEST :
+ CUPS_ASN1_GET_NEXT_REQUEST, 1,
+ packet.object_name))
+ {
+ if (_cupsSNMPRead(snmp_fd, &packet, 1.0))
+ {
+ char *dataptr; /* Pointer into data */
+ int i; /* Looping var */
+
+
+ if (!_cupsSNMPOIDToString(packet.object_name, data, sizeof(data)))
+ {
+ fputs("DEBUG: Bad OID returned!\n", stderr);
+ break;
+ }
+
+ datalen = (int)strlen(data) + 1;
+ dataptr = data + datalen;
+
+ switch (packet.object_type)
+ {
+ case CUPS_ASN1_BOOLEAN :
+ snprintf(dataptr, sizeof(data) - (dataptr - data), "%d",
+ packet.object_value.boolean);
+ break;
+
+ case CUPS_ASN1_INTEGER :
+ snprintf(dataptr, sizeof(data) - (dataptr - data), "%d",
+ packet.object_value.integer);
+ break;
+
+ case CUPS_ASN1_BIT_STRING :
+ case CUPS_ASN1_OCTET_STRING :
+ strlcpy(dataptr, packet.object_value.string,
+ sizeof(data) - (dataptr - data));
+ break;
+
+ case CUPS_ASN1_OID :
+ _cupsSNMPOIDToString(packet.object_value.oid, dataptr,
+ sizeof(data) - (dataptr - data));
+ break;
+
+ case CUPS_ASN1_HEX_STRING :
+ for (i = 0;
+ i < packet.object_value.hex_string.num_bytes &&
+ dataptr < (data + sizeof(data) - 3);
+ i ++, dataptr += 2)
+ sprintf(dataptr, "%02X",
+ packet.object_value.hex_string.bytes[i]);
+ break;
+
+ case CUPS_ASN1_COUNTER :
+ snprintf(dataptr, sizeof(data) - (dataptr - data), "%d",
+ packet.object_value.counter);
+ break;
+
+ case CUPS_ASN1_GAUGE :
+ snprintf(dataptr, sizeof(data) - (dataptr - data), "%u",
+ packet.object_value.gauge);
+ break;
+
+ case CUPS_ASN1_TIMETICKS :
+ snprintf(dataptr, sizeof(data) - (dataptr - data), "%u",
+ packet.object_value.timeticks);
+ break;
+
+ default :
+ fprintf(stderr, "DEBUG: Unknown OID value type %02X!\n",
+ packet.object_type);
+ break;
+ }
+
+ fprintf(stderr, "DEBUG: Returning %s %s\n", data, data + datalen);
+
+ status = CUPS_SC_STATUS_OK;
+ datalen += (int)strlen(data + datalen);
+ }
+ else
+ fputs("DEBUG: SNMP read error...\n", stderr);
+ }
+ else
+ fputs("DEBUG: SNMP write error...\n", stderr);
+ break;
+ }
+
+ status = CUPS_SC_STATUS_NOT_IMPLEMENTED;
+ datalen = 0;
+ break;
+
case CUPS_SC_CMD_GET_DEVICE_ID :
if (snmp_fd >= 0)
{
static const int ppmPrinterIEEE1284DeviceId[] =
{ CUPS_OID_ppmPrinterIEEE1284DeviceId,1,-1 };
- if (_cupsSNMPWrite(snmp_fd, addr, 1, _cupsSNMPDefaultCommunity(),
+
+ status = CUPS_SC_STATUS_IO_ERROR;
+ datalen = 0;
+
+ if (_cupsSNMPWrite(snmp_fd, addr, CUPS_SNMP_VERSION_1,
+ _cupsSNMPDefaultCommunity(),
CUPS_ASN1_GET_REQUEST, 1,
ppmPrinterIEEE1284DeviceId))
{
{
strlcpy(data, packet.object_value.string, sizeof(data));
datalen = (int)strlen(data);
- break;
+ status = CUPS_SC_STATUS_OK;
}
}
+
+ break;
}
if ((device_id = getenv("1284DEVICEID")) != NULL)
};
+ sleep(2);
+
length = 0;
scstatus = cupsSideChannelDoRequest(CUPS_SC_CMD_DRAIN_OUTPUT, buffer,
&length, 5.0);
printf("CUPS_SC_CMD_GET_STATE returned %s, %02X\n", statuses[scstatus],
buffer[0] & 255);
+ length = sizeof(buffer);
+ scstatus = cupsSideChannelSNMPGet(".1.3.6.1.2.1.43.10.2.1.4.1.1", buffer,
+ &length, 5.0);
+ printf("CUPS_SC_CMD_SNMP_GET returned %s, %s\n", statuses[scstatus],
+ buffer);
+
length = 0;
scstatus = cupsSideChannelDoRequest(CUPS_SC_CMD_SOFT_RESET, buffer,
&length, 5.0);
static void
usage(void)
{
- fputs("Usage: betest [-ps] [-s] [-t] device-uri job-id user title copies "
+ fputs("Usage: testbackend [-ps] [-s] [-t] device-uri job-id user title copies "
"options [file]\n", stderr);
exit(1);
}
testi18n.o: ../config.h
testipp.o: ../cups/string.h ../config.h string.h ipp-private.h ipp.h http.h
testipp.o: versioning.h
+testoptions.o: string.h ../config.h cups.h ipp.h http.h versioning.h ppd.h
+testoptions.o: array.h file.h language.h
testlang.o: i18n.h transcode.h language.h array.h versioning.h string.h
testlang.o: ../config.h
testppd.o: ../cups/string.h ../config.h string.h ppd.h array.h versioning.h
testi18n.32.o: testi18n.c ../config.h
testipp.32.o: testipp.c ../cups/string.h ../config.h string.h ipp-private.h ipp.h http.h
testipp.32.o: testipp.c versioning.h
+testoptions.32.o: testoptions.c string.h ../config.h cups.h ipp.h http.h versioning.h ppd.h
+testoptions.32.o: testoptions.c array.h file.h language.h
testlang.32.o: testlang.c i18n.h transcode.h language.h array.h versioning.h string.h
testlang.32.o: testlang.c ../config.h
testppd.32.o: testppd.c ../cups/string.h ../config.h string.h ppd.h array.h versioning.h
testi18n.64.o: testi18n.c ../config.h
testipp.64.o: testipp.c ../cups/string.h ../config.h string.h ipp-private.h ipp.h http.h
testipp.64.o: testipp.c versioning.h
+testoptions.64.o: testoptions.c string.h ../config.h cups.h ipp.h http.h versioning.h ppd.h
+testoptions.64.o: testoptions.c array.h file.h language.h
testlang.64.o: testlang.c i18n.h transcode.h language.h array.h versioning.h string.h
testlang.64.o: testlang.c ../config.h
testppd.64.o: testppd.c ../cups/string.h ../config.h string.h ppd.h array.h versioning.h
testhttp.o \
testi18n.o \
testipp.o \
+ testoptions.o \
testlang.o \
testppd.o \
testsnmp.o \
testi18n \
testipp \
testlang \
+ testoptions \
testppd
TARGETS = \
./testlang
+#
+# testoptions (dependency on static CUPS library is intentional)
+#
+
+testoptions: testoptions.o libcups.a
+ echo Linking $@...
+ $(CC) $(ARCHFLAGS) $(LDFLAGS) -o $@ testoptions.o libcups.a \
+ $(LIBGSSAPI) $(SSLLIBS) $(COMMONLIBS) $(LIBZ)
+ echo Running option API tests...
+ ./testoptions
+
+
#
# testppd (dependency on static CUPS library is intentional)
#
#-copies to the job-media-sheets-completed attribute. The second
form sets the job-media-sheets-completed attribute to #-pages.</dd>
+ <dt>PPD: keyword=value [keyword=value ...]</dt>
+ <dd>Changes or adds keywords to the printer's PPD file. Typically
+ this is used to update installable options or default media settings
+ based on the printer configuration.</dd>
+
<dt>STATE: printer-state-reason [printer-state-reason ...]</dt>
<dt>STATE: + printer-state-reason [printer-state-reason ...]</dt>
<dt>STATE: - printer-state-reason [printer-state-reason ...]</dt>
<p>Messages without one of these prefixes are treated as if they began with
the "DEBUG:" prefix string.</p>
-<h3><a name="COMMUNICATING">Communicating with the Backend</a></h3>
+<h3><a name="COMMUNICATING_BACKEND">Communicating with the Backend</a></h3>
<p>Filters can communicate with the backend via the
<a href="#cupsBackChannelRead"><code>cupsBackChannelRead</code></a> and
data[0] = '\0';
</pre>
+<h3><a name="COMMUNICATING_FILTER">Communicating with Filters</a></h3>
+
<p>Backends communicate with filters using the reciprocal functions
<a href="#cupsBackChannelWrite"><code>cupsBackChannelWrite</code></a>,
<a href="#cupsSideChannelRead"><code>cupsSideChannelRead</code></a>, and
indefinitely for commands using a <code>timeout</code> of -1.0 (probably in a
separate thread for that purpose), or use <code>select</code> or
<code>poll</code> on the <code>CUPS_SC_FD</code> file descriptor (4) to handle
-input and output on several file descriptors at the same time. Backends can pass
-<code>NULL</code> for the <code>data</code> and <code>datalen</code> parameters
-since none of the commands sent by upstream filters contain any data at this
-time.</p>
+input and output on several file descriptors at the same time.</p>
<p>Once a command is processed, the backend uses the
<a href="#cupsSideChannelWrite"><code>cupsSideChannelWrite</code></a> function
<a href="#cups_sc_command_t">cups_sc_command_t</a> command;
<a href="#cups_sc_status_t">cups_sc_status_t</a> status;
+char data[2048];
+int datalen = sizeof(data);
/* Poll for a command... */
-if (!<a href="#cupsSideChannelRead">cupsSideChannelRead</a>(&command, &status, NULL, NULL, 0.0))
+if (!<a href="#cupsSideChannelRead">cupsSideChannelRead</a>(&command, &status, data, &datalen, 0.0))
{
- char data[2048];
- int datalen;
-
switch (command)
{
- /* handle supported commands, file data/datalen/status with values as needed */
+ /* handle supported commands, fill data/datalen/status with values as needed */
default :
status = CUPS_SC_STATUS_NOT_IMPLEMENTED;
<h3><a name="SNMP">Doing SNMP Queries with Network Printers</a></h3>
-<p>Re-write for side-channel-based SNMP queries.</p>
-
-<!--
<p>The Simple Network Management Protocol (SNMP) allows you to get the current
status, page counter, and supply levels from most network printers. Every
piece of information is associated with an Object Identifier (OID), and
every printer has a <em>community</em> name associated with it. OIDs can be
queried directly or by "walking" over a range of OIDs with a common prefix.</p>
-<p>The CUPS SNMP functions provide a simple API for querying network printers.
-Queries are made using a datagram socket that is created using
-<a href="#cupsSNMPOpen"><code>cupsSNMPOpen</code></a> and destroyed using
-<a href="#cupsSNMPClose"><code>cupsSNMPClose</code></a>:</p>
-
-<pre class="example">
-#include <cups/snmp.h>
-
-int snmp = <a href="#cupsSNMPOpen">cupsSNMPOpen</a>(AF_INET);
-
-/* do some queries */
+<p>The two CUPS SNMP functions provide a simple API for querying network
+printers through the side-channel interface. Each accepts a string containing
+an OID like ".1.3.6.1.2.1.43.10.2.1.4.1.1" (the standard page counter OID)
+along with a timeout for the query.</p>
-<a href="#cupsSNMPClose">cupsSNMPClose</a>(snmp);
-</pre>
-
-<p>OIDs are simple C arrays of integers, terminated by a value of -1. For
-example, the page counter OID .1.3.6.1.2.1.43.10.2.1.4.1.1 would be:</p>
+<p>The <a href="#cupsSideChannelSNMPGet"><code>cupsSideChannelSNMPGet</code></a>
+function queries a single OID and returns the value as a string in a buffer
+you supply:</p>
<pre class="example">
-int page_counter_oid[] = { 1, 3, 6, 1, 2, 1, 43, 10, 2, 1, 4, 1, 1, -1 };
-</pre>
-
-<p>You send a query using
-<a href="#cupsSNMPWrite"><code>cupsSNMPWrite</code></a> and read the value back
-using <a href="#cupsSNMPRead"><code>cupsSNMPRead</code></a>. The value is read
-into a structure called <a href="#cups_snmp_t"><code>cups_snmp_t</code></a>:</p>
-
-<pre class="example">
-#include <cups/snmp.h>
+#include <cups/sidechannel.h>
-int page_counter_oid[] = { 1, 3, 6, 1, 2, 1, 43, 10, 2, 1, 4, 1, 1, -1 };
-http_addrlist_t *host = httpAddrGetList("myprinter", AF_UNSPEC, "161");
-int snmp = <a href="#cupsSNMPOpen">cupsSNMPOpen</a>(host->addr.addr.sa_family);
-<a href="#cups_snmp_t">cups_snmp_t</a> packet;
+char data[512];
+int datalen = sizeof(data);
-<a href="#cupsSNMPWrite">cupsSNMPWrite</a>(snmp, &(host->addr), CUPS_SNMP_VERSION_1,
- <a href="#cupsSNMPDefaultCommunity">cupsSNMPDefaultCommunity</a>(), CUPS_ASN1_GET_REQUEST, 1,
- page_counter_oid);
-if (<a href="#cupsSNMPRead">cupsSNMPRead</a>(snmp, &packet, 5000))
+if (<a href="#cupsSideChannelSNMPGet">cupsSideChannelSNMPGet</a>(".1.3.6.1.2.1.43.10.2.1.4.1.1", data, &datalen, 5.0)
+ == CUPS_SC_STATUS_OK)
{
/* Do something with the value */
- printf("Page counter is: %d\n", packet.object_value.integer);
+ printf("Page counter is: %s\n", data);
}
</pre>
-<p>The <a href="#cupsSNMPWalk"><code>cupsSNMPWalk</code></a> function allows you
-to query a whole group of OIDs, calling a function of your choice for each OID
-that is found:</p>
+<p>The
+<a href="#cupsSideChannelSNMPWalk"><code>cupsSideChannelSNMPWalk</code></a>
+function allows you to query a whole group of OIDs, calling a function of your
+choice for each OID that is found:</p>
<pre class="example">
-#include <cups/snmp.h>
+#include <cups/sidechannel.h>
void
-my_callback(<a href="#cups_snmp_t">cups_snmp_t</a> *packet, void *data)
+my_callback(const char *oid, const char *data, int datalen, void *context)
{
/* Do something with the value */
+ printf("%s=%s\n", oid, data);
}
-int printer_mib_oid[] = { 1, 3, 6, 1, 2, 1, 43, -1 };
-http_addrlist_t *host = httpAddrGetList("myprinter", AF_UNSPEC, "161");
-int snmp = <a href="#cupsSNMPOpen">cupsSNMPOpen</a>(host->addr.addr.sa_family);
+...
+
void *my_data;
-<a href="#cupsSNMPWalk">cupsSNMPWalk</a>(snmp, &(host->addr), CUPS_SNMP_VERSION_1,
- <a href="#cupsSNMPDefaultCommunity">cupsSNMPDefaultCommunity</a>(), printer_mib_oid, my_callback, my_data);
+<a href="#cupsSideChannelSNMPWalk">cupsSNMPSideChannelWalk</a>(".1.3.6.1.2.1.43", 5.0, my_callback, my_data);
</pre>
--->
\ No newline at end of file
__cupsSNMPDefaultCommunity
__cupsSNMPIsOID
__cupsSNMPIsOIDPrefixed
+__cupsSNMPOIDToString
__cupsSNMPOpen
__cupsSNMPRead
__cupsSNMPSetDebug
+__cupsSNMPStringToOID
__cupsSNMPWalk
__cupsSNMPWrite
__cupsStrAlloc
_cupsSNMPDefaultCommunity
_cupsSNMPIsOID
_cupsSNMPIsOIDPrefixed
+_cupsSNMPOIDToString
_cupsSNMPOpen
_cupsSNMPRead
_cupsSNMPSetDebug
+_cupsSNMPStringToOID
_cupsSNMPWalk
_cupsSNMPWrite
_cupsStrAlloc
cups_option_t *temp; /* Pointer to new option */
- if (name == NULL || !name[0] || value == NULL ||
- options == NULL || num_options < 0)
+ DEBUG_printf(("cupsAddOption(name=\"%s\", value=\"%s\", num_options=%d, "
+ "options=%p)\n", name, value, num_options, options));
+
+ if (!name || !name[0] || !value || !options || num_options < 0)
+ {
+ DEBUG_printf(("cupsAddOption: Returning %d\n", num_options));
return (num_options);
+ }
/*
* Look for an existing option with the same name...
* No matching option name...
*/
+ DEBUG_puts("cupsAddOption: New option...");
+
if (num_options == 0)
temp = (cups_option_t *)malloc(sizeof(cups_option_t));
else
(num_options + 1));
if (temp == NULL)
+ {
+ DEBUG_puts("cupsAddOption: Unable to expand option array, returning 0");
return (0);
+ }
*options = temp;
temp += num_options;
* Match found; free the old value...
*/
+ DEBUG_puts("cupsAddOption: Option already exists...");
_cupsStrFree(temp->value);
}
temp->value = _cupsStrAlloc(value);
+ DEBUG_printf(("cupsAddOption: Returning %d\n", num_options));
+
return (num_options);
}
int i; /* Looping var */
- if (num_options <= 0 || options == NULL)
+ DEBUG_printf(("cupsFreeOptions(num_options=%d, options=%p)\n", num_options,
+ options));
+
+ if (num_options <= 0 || !options)
return;
for (i = 0; i < num_options; i ++)
int i; /* Looping var */
- if (name == NULL || num_options <= 0 || options == NULL)
+ DEBUG_printf(("cupsGetOption(name=\"%s\", num_options=%d, options=%p)\n",
+ name, num_options, options));
+
+ if (!name || num_options <= 0 || !options)
+ {
+ DEBUG_puts("cupsGetOption: Returning NULL");
return (NULL);
+ }
for (i = 0; i < num_options; i ++)
- if (strcasecmp(options[i].name, name) == 0)
+ if (!strcasecmp(options[i].name, name))
+ {
+ DEBUG_printf(("cupsGetOption: Returning \"%s\"\n", options[i].value));
return (options[i].value);
+ }
+ DEBUG_puts("cupsGetOption: Returning NULL");
return (NULL);
}
quote; /* Quote character */
+ DEBUG_printf(("cupsParseOptions(arg=\"%s\", num_options=%d, options=%p)\n",
+ arg, num_options, options));
+
/*
* Range check input...
*/
if (!arg)
+ {
+ DEBUG_printf(("cupsParseOptions: Returning %d\n", num_options));
return (num_options);
+ }
if (!options || num_options < 0)
+ {
+ DEBUG_puts("cupsParseOptions: Returning 0");
return (0);
+ }
/*
* Make a copy of the argument string and then divide it up...
*/
if ((copyarg = strdup(arg)) == NULL)
+ {
+ DEBUG_puts("cupsParseOptions: Unable to copy arg string");
+ DEBUG_printf(("cupsParseOptions: Returning %d\n", num_options));
return (num_options);
+ }
ptr = copyarg;
while (isspace(*ptr & 255))
*ptr++ = '\0';
+ DEBUG_printf(("cupsParseOptions: name=\"%s\"\n", name));
+
if (*ptr != '=')
{
/*
*/
*ptr++ = '\0';
+ value = ptr;
- if (*ptr == '\'' || *ptr == '\"')
+ while (*ptr && !isspace(*ptr & 255))
{
- /*
- * Quoted string constant...
- */
-
- quote = *ptr++;
- value = ptr;
-
- while (*ptr != quote && *ptr)
+ if (*ptr == ',')
+ ptr ++;
+ else if (*ptr == '\'' || *ptr == '\"')
{
- if (*ptr == '\\' && ptr[1])
- _cups_strcpy(ptr, ptr + 1);
+ /*
+ * Quoted string constant...
+ */
- ptr ++;
- }
+ quote = *ptr;
+ _cups_strcpy(ptr, ptr + 1);
- if (*ptr != '\0')
- *ptr++ = '\0';
- }
- else if (*ptr == '{')
- {
- /*
- * Collection value...
- */
+ while (*ptr != quote && *ptr)
+ {
+ if (*ptr == '\\' && ptr[1])
+ _cups_strcpy(ptr, ptr + 1);
+
+ ptr ++;
+ }
- int depth;
+ if (*ptr)
+ _cups_strcpy(ptr, ptr + 1);
+ }
+ else if (*ptr == '{')
+ {
+ /*
+ * Collection value...
+ */
- value = ptr;
+ int depth;
- for (depth = 1; *ptr; ptr ++)
- if (*ptr == '{')
- depth ++;
- else if (*ptr == '}')
+ for (depth = 0; *ptr; ptr ++)
{
- depth --;
- if (!depth)
+ if (*ptr == '{')
+ depth ++;
+ else if (*ptr == '}')
{
- ptr ++;
-
- if (*ptr != ',')
+ depth --;
+ if (!depth)
+ {
+ ptr ++;
break;
+ }
}
- }
- else if (*ptr == '\\' && ptr[1])
- _cups_strcpy(ptr, ptr + 1);
-
- if (*ptr != '\0')
- *ptr++ = '\0';
- }
- else
- {
- /*
- * Normal space-delimited string...
- */
-
- value = ptr;
-
- while (!isspace(*ptr & 255) && *ptr)
+ else if (*ptr == '\\' && ptr[1])
+ _cups_strcpy(ptr, ptr + 1);
+ }
+ }
+ else
{
- if (*ptr == '\\' && ptr[1])
- _cups_strcpy(ptr, ptr + 1);
+ /*
+ * Normal space-delimited string...
+ */
- ptr ++;
+ while (!isspace(*ptr & 255) && *ptr)
+ {
+ if (*ptr == '\\' && ptr[1])
+ _cups_strcpy(ptr, ptr + 1);
+
+ ptr ++;
+ }
}
}
+ if (*ptr != '\0')
+ *ptr++ = '\0';
+
+ DEBUG_printf(("cupsParseOptions: value=\"%s\"\n", value));
+
/*
* Skip trailing whitespace...
*/
while (isspace(*ptr & 255))
- *ptr++ = '\0';
+ ptr ++;
/*
* Add the string value...
free(copyarg);
+ DEBUG_printf(("cupsParseOptions: Returning %d\n", num_options));
+
return (num_options);
}
cups_option_t *option; /* Current option */
+ DEBUG_printf(("cupsRemoveOption(name=\"%s\", num_options=%d, options=%p)\n",
+ name, num_options, options));
+
/*
* Range check input...
*/
if (!name || num_options < 1 || !options)
+ {
+ DEBUG_printf(("cupsRemoveOption: Returning %d\n", num_options));
return (num_options);
+ }
/*
* Loop for the option...
* Remove this option from the array...
*/
+ DEBUG_puts("cupsRemoveOption: Found option, removing it...");
+
num_options --;
i --;
* Return the new number of options...
*/
+ DEBUG_printf(("cupsRemoveOption: Returning %d\n", num_options));
return (num_options);
}
*
* Side-channel API code for the Common UNIX Printing System (CUPS).
*
- * Copyright 2007 by Apple Inc.
+ * Copyright 2007-2008 by Apple Inc.
* Copyright 2006 by Easy Software Products.
*
* These coded instructions, statements, and computer programs are the
*
* Contents:
*
- * cupsSideChannelDoRequest() - Send a side-channel command to a backend
- * and wait for a response.
+ * cupsSideChannelDoRequest() - Send a side-channel command to a backend and
+ * wait for a response.
* cupsSideChannelRead() - Read a side-channel message.
+ * cupsSideChannelSNMPGet() - Query a SNMP OID's value.
+ * cupsSideChannelSNMPWalk() - Query multiple SNMP OID values.
* cupsSideChannelWrite() - Write a side-channel message.
*/
#include "sidechannel.h"
#include "string.h"
+#include "debug.h"
#include <unistd.h>
#include <errno.h>
#ifdef __hpux
#endif /* HAVE_POLL */
+ DEBUG_printf(("cupsSideChannelRead(command=%p, status=%p, data=%p, "
+ "datalen=%p(%d), timeout=%.3f)\n", command, status, data,
+ datalen, datalen ? *datalen : -1, timeout));
+
/*
* Range check input...
*/
if (timeout < 0.0)
{
if (select(CUPS_SC_FD + 1, &input_set, NULL, NULL, NULL) < 1)
+ {
+ DEBUG_printf(("cupsSideChannelRead: Select error: %s\n", strerror(errno)));
return (-1);
+ }
}
else
{
stimeout.tv_usec = (int)(timeout * 1000000) % 1000000;
if (select(CUPS_SC_FD + 1, &input_set, NULL, NULL, &stimeout) < 1)
+ {
+ DEBUG_puts("cupsSideChannelRead: Select timeout");
return (-1);
+ }
}
#endif /* HAVE_POLL */
while ((bytes = read(CUPS_SC_FD, buffer, sizeof(buffer))) < 0)
if (errno != EINTR && errno != EAGAIN)
+ {
+ DEBUG_printf(("cupsSideChannelRead: Read error: %s\n", strerror(errno)));
return (-1);
+ }
/*
* Validate the command code in the message...
*/
- if (buffer[0] < CUPS_SC_CMD_SOFT_RESET || buffer[0] > CUPS_SC_CMD_GET_STATE)
+ if (buffer[0] < CUPS_SC_CMD_SOFT_RESET ||
+ buffer[0] > CUPS_SC_CMD_SNMP_GET_NEXT)
+ {
+ DEBUG_printf(("cupsSideChannelRead: Bad command %d!\n", buffer[0]));
return (-1);
+ }
*command = (cups_sc_command_t)buffer[0];
memcpy(data, buffer + 4, templen);
}
+ DEBUG_printf(("cupsSideChannelRead: Returning status=%d\n", *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@
+ */
+
+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[2048];/* 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)\n", 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), timeout))
+ return (CUPS_SC_STATUS_TIMEOUT);
+
+ real_datalen = sizeof(real_data);
+ if (cupsSideChannelRead(&rcommand, &status, real_data, &real_datalen, timeout))
+ return (CUPS_SC_STATUS_TIMEOUT);
+
+ if (rcommand != CUPS_SC_CMD_SNMP_GET)
+ return (CUPS_SC_STATUS_BAD_MESSAGE);
+
+ if (status == CUPS_SC_STATUS_OK)
+ {
+ /*
+ * Parse the response of the form "oid\0value"...
+ */
+
+ real_oidlen = strlen(real_data) + 1;
+ real_datalen -= real_oidlen;
+
+ if ((real_datalen + 1) > *datalen)
+ return (CUPS_SC_STATUS_TOO_BIG);
+
+ memcpy(data, real_data + real_oidlen, real_datalen);
+ data[real_datalen] = '\0';
+
+ *datalen = real_datalen;
+ }
+
+ 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@
+ */
+
+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[2048];/* Real data buffer for response */
+ int real_datalen, /* Real length of data buffer */
+ real_oidlen, /* Length of returned OID string */
+ oidlen; /* Length of first OID */
+ const char *current_oid; /* Current OID */
+
+
+ DEBUG_printf(("cupsSideChannelSNMPWalk(oid=\"%s\", timeout=%.3f, cb=%p, "
+ "context=%p)\n", oid, timeout, cb, context));
+
+ /*
+ * Range check input...
+ */
+
+ if (!oid || !*oid || !cb)
+ return (CUPS_SC_STATUS_BAD_MESSAGE);
+
+ /*
+ * Loop until the OIDs don't match...
+ */
+
+ current_oid = oid;
+ oidlen = (int)strlen(oid);
+
+ 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), timeout))
+ return (CUPS_SC_STATUS_TIMEOUT);
+
+ real_datalen = sizeof(real_data);
+ if (cupsSideChannelRead(&rcommand, &status, real_data, &real_datalen,
+ timeout))
+ return (CUPS_SC_STATUS_TIMEOUT);
+
+ if (rcommand != CUPS_SC_CMD_SNMP_GET_NEXT)
+ 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] != '.')
+ {
+ /*
+ * Done with this set of OIDs...
+ */
+
+ return (CUPS_SC_STATUS_OK);
+ }
+
+ real_oidlen = strlen(real_data) + 1;
+ real_datalen -= 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;
+ }
+ }
+ while (status == CUPS_SC_STATUS_OK);
+
+ return (status);
+}
+
+
/*
* 'cupsSideChannelWrite()' - Write a side-channel message.
*
* Range check input...
*/
- if (command < CUPS_SC_CMD_SOFT_RESET || command > CUPS_SC_CMD_GET_STATE ||
+ if (command < CUPS_SC_CMD_SOFT_RESET || command > CUPS_SC_CMD_SNMP_GET_NEXT ||
datalen < 0 || datalen > 16384 || (datalen > 0 && !data))
return (-1);
CUPS_SC_CMD_DRAIN_OUTPUT = 2, /* Drain all pending output */
CUPS_SC_CMD_GET_BIDI = 3, /* Return bidirectional capabilities */
CUPS_SC_CMD_GET_DEVICE_ID = 4, /* Return the IEEE-1284 device ID */
- CUPS_SC_CMD_GET_STATE = 5 /* Return the device state */
+ CUPS_SC_CMD_GET_STATE = 5, /* Return the device state */
+ CUPS_SC_CMD_SNMP_GET = 6, /* Query an SNMP OID @since CUPS 1.4@ */
+ CUPS_SC_CMD_SNMP_GET_NEXT = 7 /* Query the next SNMP OID @since CUPS 1.4@ */
};
typedef enum cups_sc_command_e cups_sc_command_t;
/**** Request command codes ****/
typedef enum cups_sc_status_e cups_sc_status_t;
/**** Response status codes ****/
+typedef void (*cups_sc_walk_func_t)(const char *oid, const char *data,
+ int datalen, void *context);
+ /**** SNMP walk callback ****/
+
/*
* Prototypes...
const char *data, int datalen,
double timeout) _CUPS_API_1_3;
+/**** New in CUPS 1.4 ****/
+extern cups_sc_status_t cupsSideChannelSNMPGet(const char *oid, char *data,
+ int *datalen, double timeout)
+ _CUPS_API_1_4;
+extern cups_sc_status_t cupsSideChannelSNMPWalk(const char *oid, double timeout,
+ cups_sc_walk_func_t cb,
+ void *context) _CUPS_API_1_4;
+
# ifdef __cplusplus
}
_CUPS_API_1_4;
extern int _cupsSNMPIsOIDPrefixed(cups_snmp_t *packet,
const int *prefix) _CUPS_API_1_4;
+extern char *_cupsSNMPOIDToString(const int *src, char *dst,
+ size_t dstsize) _CUPS_API_1_4;
extern int _cupsSNMPOpen(int family) _CUPS_API_1_4;
extern cups_snmp_t *_cupsSNMPRead(int fd, cups_snmp_t *packet,
double timeout) _CUPS_API_1_4;
extern void _cupsSNMPSetDebug(int level) _CUPS_API_1_4;
+extern int *_cupsSNMPStringToOID(const char *src,
+ int *dst, int dstsize)
+ _CUPS_API_1_4;
extern int _cupsSNMPWalk(int fd, http_addr_t *address, int version,
const char *community, const int *prefix,
double timeout, cups_snmp_cb_t cb,
* specified OID.
* _cupsSNMPIsOIDPrefixed() - Test whether a SNMP response uses the
* specified OID prefix.
+ * _cupsSNMPOIDToString() - Convert an OID to a string.
* _cupsSNMPOpen() - Open a SNMP socket.
* _cupsSNMPRead() - Read and parse a SNMP response.
* _cupsSNMPSetDebug() - Enable/disable debug logging to stderr.
+ * _cupsSNMPStringToOID() - Convert a numeric OID string to an OID array.
* _cupsSNMPWalk() - Enumerate a group of OIDs.
* _cupsSNMPWrite() - Send an SNMP query packet.
* asn1_debug() - Decode an ASN1-encoded message.
}
+/*
+ * '_cupsSNMPOIDToString()' - Convert an OID to a string.
+ *
+ * @since CUPS 1.4@
+ */
+
+
+char * /* O - New string or @code NULL@ on error */
+_cupsSNMPOIDToString(const int *src, /* I - OID */
+ char *dst, /* I - String buffer */
+ size_t dstsize) /* I - Size of string buffer */
+{
+ char *dstptr, /* Pointer into string buffer */
+ *dstend; /* End of string buffer */
+
+
+ DEBUG_printf(("_cupsSNMPOIDToString(src=%p, dst=%p, dstsize=" CUPS_LLFMT ")\n",
+ src, dst, CUPS_LLCAST dstsize));
+
+ /*
+ * Range check input...
+ */
+
+ if (!src || !dst || dstsize < 4)
+ return (NULL);
+
+ /*
+ * Loop through the OID array and build a string...
+ */
+
+ for (dstptr = dst, dstend = dstptr + dstsize - 1;
+ *src >= 0 && dstptr < dstend;
+ src ++, dstptr += strlen(dstptr))
+ snprintf(dstptr, dstend - dstptr + 1, ".%d", *src);
+
+ if (*src >= 0)
+ return (NULL);
+ else
+ return (dst);
+}
+
+
/*
* '_cupsSNMPOpen()' - Open a SNMP socket.
*
}
+/*
+ * '_cupsSNMPStringToOID()' - Convert a numeric OID string to an OID array.
+ *
+ * This function converts a string of the form ".N.N.N.N.N" to the
+ * corresponding OID array terminated by -1.
+ *
+ * @code NULL@ is returned if the array is not large enough or the string is
+ * not a valid OID number.
+ *
+ * @since CUPS 1.4@
+ */
+
+int * /* O - Pointer to OID array or @code NULL@ on error */
+_cupsSNMPStringToOID(const char *src, /* I - OID string */
+ int *dst, /* I - OID array */
+ int dstsize)/* I - Number of integers in OID array */
+{
+ int *dstptr, /* Pointer into OID array */
+ *dstend; /* End of OID array */
+
+
+ DEBUG_printf(("_cupsSNMPStringToOID(src=\"%s\", dst=%p, dstsize=%d)\n",
+ src, dst, dstsize));
+
+ /*
+ * Range check input...
+ */
+
+ if (!src || !dst || dstsize < 2)
+ return (NULL);
+
+ /*
+ * Skip leading "."...
+ */
+
+ if (*src == '.')
+ src ++;
+
+ /*
+ * Loop to the end of the string...
+ */
+
+ for (dstend = dst + dstsize - 1, dstptr = dst, *dstptr = 0;
+ *src && dstptr < dstend;
+ src ++)
+ {
+ if (*src == '.')
+ {
+ dstptr ++;
+ *dstptr = 0;
+ }
+ else if (isdigit(*src & 255))
+ *dstptr = *dstptr * 10 + *src - '0';
+ else
+ break;
+ }
+
+ if (*src)
+ return (NULL);
+
+ /*
+ * Terminate the end of the OID array and return...
+ */
+
+ dstptr[1] = -1;
+
+ return (dst);
+}
+
+
/*
* '_cupsSNMPWalk()' - Enumerate a group of OIDs.
*
--- /dev/null
+/*
+ * "$Id$"
+ *
+ * Option test program for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 2008 by Apple Inc.
+ *
+ * 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:
+ *
+ * main() - Test option processing functions.
+ */
+
+/*
+ * Include necessary headers...
+ */
+
+#include "string.h"
+#include "cups.h"
+
+
+/*
+ * 'main()' - Test option processing functions.
+ */
+
+int /* O - Exit status */
+main(int argc, /* I - Number of command-line arguments */
+ char *argv[]) /* I - Command-line arguments */
+{
+ int status = 0, /* Exit status */
+ num_options; /* Number of options */
+ cups_option_t *options; /* Options */
+ const char *value; /* Value of an option */
+
+
+ if (argc == 1)
+ {
+ /*
+ * cupsParseOptions()
+ */
+
+ fputs("cupsParseOptions: ", stdout);
+
+ num_options = cupsParseOptions("foo=1234 "
+ "bar=\"One Fish\",\"Two Fish\",\"Red Fish\","
+ "\"Blue Fish\" "
+ "baz={param1=1 param2=2} "
+ "foobar=FOO\\ BAR "
+ "barfoo=barfoo "
+ "barfoo=\"\'BAR FOO\'\"", 0, &options);
+
+ if (num_options != 5)
+ {
+ printf("FAIL (num_options=%d, expected 5)\n", num_options);
+ status ++;
+ }
+ else if ((value = cupsGetOption("foo", num_options, options)) == NULL ||
+ strcmp(value, "1234"))
+ {
+ printf("FAIL (foo=\"%s\", expected \"1234\")\n", value);
+ status ++;
+ }
+ else if ((value = cupsGetOption("bar", num_options, options)) == NULL ||
+ strcmp(value, "One Fish,Two Fish,Red Fish,Blue Fish"))
+ {
+ printf("FAIL (bar=\"%s\", expected \"One Fish,Two Fish,Red Fish,Blue "
+ "Fish\")\n", value);
+ status ++;
+ }
+ else if ((value = cupsGetOption("baz", num_options, options)) == NULL ||
+ strcmp(value, "{param1=1 param2=2}"))
+ {
+ printf("FAIL (baz=\"%s\", expected \"{param1=1 param2=2}\")\n", value);
+ status ++;
+ }
+ else if ((value = cupsGetOption("foobar", num_options, options)) == NULL ||
+ strcmp(value, "FOO BAR"))
+ {
+ printf("FAIL (foobar=\"%s\", expected \"FOO BAR\")\n", value);
+ status ++;
+ }
+ else if ((value = cupsGetOption("barfoo", num_options, options)) == NULL ||
+ strcmp(value, "\'BAR FOO\'"))
+ {
+ printf("FAIL (barfoo=\"%s\", expected \"\'BAR FOO\'\")\n", value);
+ status ++;
+ }
+ else
+ puts("PASS");
+ }
+ else
+ {
+ int i; /* Looping var */
+ cups_option_t *option; /* Current option */
+
+
+ num_options = cupsParseOptions(argv[1], 0, &options);
+
+ for (i = 0, option = options; i < num_options; i ++, option ++)
+ printf("options[%d].name=\"%s\", value=\"%s\"\n", i, option->name,
+ option->value);
+ }
+
+ exit (status);
+}
+
+
+/*
+ * End of "$Id$".
+ */
*/
static void print_packet(cups_snmp_t *packet, void *data);
-static int *scan_oid(const char *s, int *oid, int oidsize);
static int show_oid(int fd, const char *community,
http_addr_t *addr, const char *s, int walk);
static void usage(void);
if (!oid)
{
if (!show_oid(fd, community, &(host->addr),
- walk ? "1.3.6.1.2.1.43" :
- "1.3.6.1.2.1.43.10.2.1.4.1.1", walk))
+ walk ? ".1.3.6.1.2.1.43" :
+ ".1.3.6.1.2.1.43.10.2.1.4.1.1", walk))
return (1);
}
void *data) /* I - User data pointer (not used) */
{
int i; /* Looping var */
+ char temp[1024]; /* Temporary OID string */
(void)data;
- printf("%d", packet->object_name[0]);
- for (i = 1; packet->object_name[i] >= 0; i ++)
- printf(".%d", packet->object_name[i]);
- fputs(" = ", stdout);
+ printf("%s = ", _cupsSNMPOIDToString(packet->object_name, temp, sizeof(temp)));
switch (packet->object_type)
{
break;
case CUPS_ASN1_OID :
- printf("OID %d", packet->object_value.oid[0]);
- for (i = 1; packet->object_value.oid[i] >= 0; i ++)
- printf(".%d", packet->object_value.oid[i]);
- putchar('\n');
+ printf("OID %s\n", _cupsSNMPOIDToString(packet->object_value.oid,
+ temp, sizeof(temp)));
break;
case CUPS_ASN1_HEX_STRING :
}
-/*
- * 'scan_oid()' - Scan an OID value.
- */
-
-static int * /* O - OID or NULL on error */
-scan_oid(const char *s, /* I - OID string */
- int *oid, /* I - OID array */
- int oidsize) /* I - Size of OID array in integers */
-{
- int i; /* Index into OID array */
- char *ptr; /* Pointer into string */
-
-
- for (ptr = (char *)s, i = 0, oidsize --; ptr && *ptr && i < oidsize; i ++)
- {
- if (!isdigit(*ptr & 255))
- return (NULL);
-
- oid[i] = strtol(ptr, &ptr, 10);
- if (ptr && *ptr == '.')
- ptr ++;
- }
-
- if (i >= oidsize)
- return (NULL);
-
- oid[i] = -1;
-
- return (oid);
-}
-
-
/*
* 'show_oid()' - Show the specified OID.
*/
int i; /* Looping var */
int oid[CUPS_SNMP_MAX_OID]; /* OID */
cups_snmp_t packet; /* SNMP packet */
+ char temp[1024]; /* Temporary OID string */
- if (!scan_oid(s, oid, sizeof(oid) / sizeof(oid[0])))
+ if (!_cupsSNMPStringToOID(s, oid, sizeof(oid) / sizeof(oid[0])))
{
puts("testsnmp: Bad OID");
return (0);
if (walk)
{
- printf("_cupsSNMPWalk(%d", oid[0]);
- for (i = 1; oid[i] >= 0; i ++)
- printf(".%d", oid[i]);
- puts("):");
+ printf("_cupsSNMPWalk(%s): ", _cupsSNMPOIDToString(oid, temp, sizeof(temp)));
if (_cupsSNMPWalk(fd, addr, CUPS_SNMP_VERSION_1, community, oid, 5.0,
print_packet, NULL) < 0)
}
else
{
- printf("_cupsSNMPWrite(%d", oid[0]);
- for (i = 1; oid[i] >= 0; i ++)
- printf(".%d", oid[i]);
- fputs("): ", stdout);
+ printf("_cupsSNMPWrite(%s): ", _cupsSNMPOIDToString(oid, temp, sizeof(temp)));
if (!_cupsSNMPWrite(fd, addr, CUPS_SNMP_VERSION_1, community,
CUPS_ASN1_GET_REQUEST, 1, oid))
<li><a href="#EXITCODES">Exit Codes</a></li>
<li><a href="#ENVIRONMENT">Environment Variables</a></li>
<li><a href="#MESSAGES">Communicating with the Scheduler</a></li>
-<li><a href="#COMMUNICATING">Communicating with the Backend</a></li>
+<li><a href="#COMMUNICATING_BACKEND">Communicating with the Backend</a></li>
+<li><a href="#COMMUNICATING_FILTER">Communicating with Filters</a></li>
<li><a href="#SNMP">Doing SNMP Queries with Network Printers</a></li>
</ul></li>
<li><a href="#FUNCTIONS">Functions</a><ul class="code">
<li><a href="#cupsBackendDeviceURI" title="Get the device URI for a backend.">cupsBackendDeviceURI</a></li>
<li><a href="#cupsSideChannelDoRequest" title="Send a side-channel command to a backend and wait for a response.">cupsSideChannelDoRequest</a></li>
<li><a href="#cupsSideChannelRead" title="Read a side-channel message.">cupsSideChannelRead</a></li>
+<li><a href="#cupsSideChannelSNMPGet" title="Query a SNMP OID's value.">cupsSideChannelSNMPGet</a></li>
+<li><a href="#cupsSideChannelSNMPWalk" title="Query multiple SNMP OID values.">cupsSideChannelSNMPWalk</a></li>
<li><a href="#cupsSideChannelWrite" title="Write a side-channel message.">cupsSideChannelWrite</a></li>
</ul>
<li><a href="#TYPES">Data Types</a><ul class="code">
<li><a href="#cups_sc_command_t" title="Request command codes">cups_sc_command_t</a></li>
<li><a href="#cups_sc_state_t" title="Printer state bits">cups_sc_state_t</a></li>
<li><a href="#cups_sc_status_t" title="Response status codes">cups_sc_status_t</a></li>
+ <li><a href="#cups_sc_walk_func_t" title="SNMP walk callback">cups_sc_walk_func_t</a></li>
</ul></li>
<li><a href="#ENUMERATIONS">Constants</a><ul class="code">
<li><a href="#cups_backend_e" title="Backend exit codes">cups_backend_e</a></li>
</ul></li>
</ul>
<!--
- "$Id: api-filter.shtml 7288 2008-02-06 01:39:05Z mike $"
+ "$Id: api-filter.shtml 7502 2008-04-28 21:30:12Z mike $"
Filter and backend programming introduction for the Common UNIX Printing
System (CUPS).
#-copies to the job-media-sheets-completed attribute. The second
form sets the job-media-sheets-completed attribute to #-pages.</dd>
+ <dt>PPD: keyword=value [keyword=value ...]</dt>
+ <dd>Changes or adds keywords to the printer's PPD file. Typically
+ this is used to update installable options or default media settings
+ based on the printer configuration.</dd>
+
<dt>STATE: printer-state-reason [printer-state-reason ...]</dt>
<dt>STATE: + printer-state-reason [printer-state-reason ...]</dt>
<dt>STATE: - printer-state-reason [printer-state-reason ...]</dt>
<p>Messages without one of these prefixes are treated as if they began with
the "DEBUG:" prefix string.</p>
-<h3><a name="COMMUNICATING">Communicating with the Backend</a></h3>
+<h3><a name="COMMUNICATING_BACKEND">Communicating with the Backend</a></h3>
<p>Filters can communicate with the backend via the
<a href="#cupsBackChannelRead"><code>cupsBackChannelRead</code></a> and
data[0] = '\0';
</pre>
+<h3><a name="COMMUNICATING_FILTER">Communicating with Filters</a></h3>
+
<p>Backends communicate with filters using the reciprocal functions
<a href="#cupsBackChannelWrite"><code>cupsBackChannelWrite</code></a>,
<a href="#cupsSideChannelRead"><code>cupsSideChannelRead</code></a>, and
indefinitely for commands using a <code>timeout</code> of -1.0 (probably in a
separate thread for that purpose), or use <code>select</code> or
<code>poll</code> on the <code>CUPS_SC_FD</code> file descriptor (4) to handle
-input and output on several file descriptors at the same time. Backends can pass
-<code>NULL</code> for the <code>data</code> and <code>datalen</code> parameters
-since none of the commands sent by upstream filters contain any data at this
-time.</p>
+input and output on several file descriptors at the same time.</p>
<p>Once a command is processed, the backend uses the
<a href="#cupsSideChannelWrite"><code>cupsSideChannelWrite</code></a> function
<a href="#cups_sc_command_t">cups_sc_command_t</a> command;
<a href="#cups_sc_status_t">cups_sc_status_t</a> status;
+char data[2048];
+int datalen = sizeof(data);
/* Poll for a command... */
-if (!<a href="#cupsSideChannelRead">cupsSideChannelRead</a>(&command, &status, NULL, NULL, 0.0))
+if (!<a href="#cupsSideChannelRead">cupsSideChannelRead</a>(&command, &status, data, &datalen, 0.0))
{
- char data[2048];
- int datalen;
-
switch (command)
{
- /* handle supported commands, file data/datalen/status with values as needed */
+ /* handle supported commands, fill data/datalen/status with values as needed */
default :
status = CUPS_SC_STATUS_NOT_IMPLEMENTED;
<h3><a name="SNMP">Doing SNMP Queries with Network Printers</a></h3>
-<p>Re-write for side-channel-based SNMP queries.</p>
-
-<!--
<p>The Simple Network Management Protocol (SNMP) allows you to get the current
status, page counter, and supply levels from most network printers. Every
piece of information is associated with an Object Identifier (OID), and
every printer has a <em>community</em> name associated with it. OIDs can be
queried directly or by "walking" over a range of OIDs with a common prefix.</p>
-<p>The CUPS SNMP functions provide a simple API for querying network printers.
-Queries are made using a datagram socket that is created using
-<a href="#cupsSNMPOpen"><code>cupsSNMPOpen</code></a> and destroyed using
-<a href="#cupsSNMPClose"><code>cupsSNMPClose</code></a>:</p>
-
-<pre class="example">
-#include <cups/snmp.h>
-
-int snmp = <a href="#cupsSNMPOpen">cupsSNMPOpen</a>(AF_INET);
-
-/* do some queries */
-
-<a href="#cupsSNMPClose">cupsSNMPClose</a>(snmp);
-</pre>
+<p>The two CUPS SNMP functions provide a simple API for querying network
+printers through the side-channel interface. Each accepts a string containing
+an OID like ".1.3.6.1.2.1.43.10.2.1.4.1.1" (the standard page counter OID)
+along with a timeout for the query.</p>
-<p>OIDs are simple C arrays of integers, terminated by a value of -1. For
-example, the page counter OID .1.3.6.1.2.1.43.10.2.1.4.1.1 would be:</p>
+<p>The <a href="#cupsSideChannelSNMPGet"><code>cupsSideChannelSNMPGet</code></a>
+function queries a single OID and returns the value as a string in a buffer
+you supply:</p>
<pre class="example">
-int page_counter_oid[] = { 1, 3, 6, 1, 2, 1, 43, 10, 2, 1, 4, 1, 1, -1 };
-</pre>
-
-<p>You send a query using
-<a href="#cupsSNMPWrite"><code>cupsSNMPWrite</code></a> and read the value back
-using <a href="#cupsSNMPRead"><code>cupsSNMPRead</code></a>. The value is read
-into a structure called <a href="#cups_snmp_t"><code>cups_snmp_t</code></a>:</p>
-
-<pre class="example">
-#include <cups/snmp.h>
+#include <cups/sidechannel.h>
-int page_counter_oid[] = { 1, 3, 6, 1, 2, 1, 43, 10, 2, 1, 4, 1, 1, -1 };
-http_addrlist_t *host = httpAddrGetList("myprinter", AF_UNSPEC, "161");
-int snmp = <a href="#cupsSNMPOpen">cupsSNMPOpen</a>(host->addr.addr.sa_family);
-<a href="#cups_snmp_t">cups_snmp_t</a> packet;
+char data[512];
+int datalen = sizeof(data);
-<a href="#cupsSNMPWrite">cupsSNMPWrite</a>(snmp, &(host->addr), CUPS_SNMP_VERSION_1,
- <a href="#cupsSNMPDefaultCommunity">cupsSNMPDefaultCommunity</a>(), CUPS_ASN1_GET_REQUEST, 1,
- page_counter_oid);
-if (<a href="#cupsSNMPRead">cupsSNMPRead</a>(snmp, &packet, 5000))
+if (<a href="#cupsSideChannelSNMPGet">cupsSideChannelSNMPGet</a>(".1.3.6.1.2.1.43.10.2.1.4.1.1", data, &datalen, 5.0)
+ == CUPS_SC_STATUS_OK)
{
/* Do something with the value */
- printf("Page counter is: %d\n", packet.object_value.integer);
+ printf("Page counter is: %s\n", data);
}
</pre>
-<p>The <a href="#cupsSNMPWalk"><code>cupsSNMPWalk</code></a> function allows you
-to query a whole group of OIDs, calling a function of your choice for each OID
-that is found:</p>
+<p>The
+<a href="#cupsSideChannelSNMPWalk"><code>cupsSideChannelSNMPWalk</code></a>
+function allows you to query a whole group of OIDs, calling a function of your
+choice for each OID that is found:</p>
<pre class="example">
-#include <cups/snmp.h>
+#include <cups/sidechannel.h>
void
-my_callback(<a href="#cups_snmp_t">cups_snmp_t</a> *packet, void *data)
+my_callback(const char *oid, const char *data, int datalen, void *context)
{
/* Do something with the value */
+ printf("%s=%s\n", oid, data);
}
-int printer_mib_oid[] = { 1, 3, 6, 1, 2, 1, 43, -1 };
-http_addrlist_t *host = httpAddrGetList("myprinter", AF_UNSPEC, "161");
-int snmp = <a href="#cupsSNMPOpen">cupsSNMPOpen</a>(host->addr.addr.sa_family);
+...
+
void *my_data;
-<a href="#cupsSNMPWalk">cupsSNMPWalk</a>(snmp, &(host->addr), CUPS_SNMP_VERSION_1,
- <a href="#cupsSNMPDefaultCommunity">cupsSNMPDefaultCommunity</a>(), printer_mib_oid, my_callback, my_data);
+<a href="#cupsSideChannelSNMPWalk">cupsSNMPSideChannelWalk</a>(".1.3.6.1.2.1.43", 5.0, my_callback, my_data);
</pre>
---><h2 class="title"><a name="FUNCTIONS">Functions</a></h2>
+<h2 class="title"><a name="FUNCTIONS">Functions</a></h2>
<h3 class="function"><span class="info"> CUPS 1.2 </span><a name="cupsBackChannelRead">cupsBackChannelRead</a></h3>
<p class="description">Read data from the backchannel.</p>
<p class="code">
pointed to by the "data" parameter. cupsSideChannelDoRequest() will
update the value to contain the number of data bytes in the buffer.
+</p>
+<h3 class="function"><span class="info"> CUPS 1.4 </span><a name="cupsSideChannelSNMPGet">cupsSideChannelSNMPGet</a></h3>
+<p class="description">Query a SNMP OID's value.</p>
+<p class="code">
+<a href="#cups_sc_status_t">cups_sc_status_t</a> cupsSideChannelSNMPGet (<br>
+ const char *oid,<br>
+ char *data,<br>
+ int *datalen,<br>
+ double timeout<br>
+);</p>
+<h4 class="parameters">Parameters</h4>
+<dl>
+<dt>oid</dt>
+<dd class="description">OID to query</dd>
+<dt>data</dt>
+<dd class="description">Buffer for OID value</dd>
+<dt>datalen</dt>
+<dd class="description">Size of OID buffer on entry, size of value on return</dd>
+<dt>timeout</dt>
+<dd class="description">Timeout in seconds</dd>
+</dl>
+<h4 class="returnvalue">Return Value</h4>
+<p class="description">Query status</p>
+<h4 class="discussion">Discussion</h4>
+<p class="discussion">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.<br>
+<br>
+"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.<br>
+<br>
+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</code> is returned by backends that do not
+support SNMP queries. <code>CUPS_SC_STATUS_NO_RESPONSE</code> is returned when
+the printer does not respond to the SNMP query.
+
+</p>
+<h3 class="function"><span class="info"> CUPS 1.4 </span><a name="cupsSideChannelSNMPWalk">cupsSideChannelSNMPWalk</a></h3>
+<p class="description">Query multiple SNMP OID values.</p>
+<p class="code">
+<a href="#cups_sc_status_t">cups_sc_status_t</a> cupsSideChannelSNMPWalk (<br>
+ const char *oid,<br>
+ double timeout,<br>
+ <a href="#cups_sc_walk_func_t">cups_sc_walk_func_t</a> cb,<br>
+ void *context<br>
+);</p>
+<h4 class="parameters">Parameters</h4>
+<dl>
+<dt>oid</dt>
+<dd class="description">First numeric OID to query</dd>
+<dt>timeout</dt>
+<dd class="description">Timeout for each query in seconds</dd>
+<dt>cb</dt>
+<dd class="description">Function to call with each value</dd>
+<dt>context</dt>
+<dd class="description">Application-defined pointer to send to callback</dd>
+</dl>
+<h4 class="returnvalue">Return Value</h4>
+<p class="description">Status of first query of <code>CUPS_SC_STATUS_OK</code> on success</p>
+<h4 class="discussion">Discussion</h4>
+<p class="discussion">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.<br>
+<br>
+"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.<br>
+<br>
+"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.<br>
+<br>
+"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 <a href="#cupsSideChannelSNMPGet"><code>cupsSideChannelSNMPGet</code></a>.
+
+<code>CUPS_SC_STATUS_NOT_IMPLEMENTED</code> is returned by backends that do not
+support SNMP queries. <code>CUPS_SC_STATUS_NO_RESPONSE</code> is returned when
+the printer does not respond to the first SNMP query.
+
</p>
<h3 class="function"><span class="info"> CUPS 1.3 </span><a name="cupsSideChannelWrite">cupsSideChannelWrite</a></h3>
<p class="description">Write a side-channel message.</p>
<p class="code">
typedef enum <a href="#cups_sc_status_e">cups_sc_status_e</a> cups_sc_status_t;
</p>
+<h3 class="typedef"><a name="cups_sc_walk_func_t">cups_sc_walk_func_t</a></h3>
+<p class="description">SNMP walk callback</p>
+<p class="code">
+typedef void (*cups_sc_walk_func_t)(const char *oid, const char *data, int datalen, void *context);
+</p>
<h2 class="title"><a name="ENUMERATIONS">Constants</a></h2>
<h3 class="enumeration"><a name="cups_backend_e">cups_backend_e</a></h3>
<p class="description">Backend exit codes</p>
<dd class="description">Return the IEEE-1284 device ID</dd>
<dt>CUPS_SC_CMD_GET_STATE </dt>
<dd class="description">Return the device state</dd>
+<dt>CUPS_SC_CMD_SNMP_GET <span class="info"> CUPS 1.4 </span></dt>
+<dd class="description">Query an SNMP OID </dd>
+<dt>CUPS_SC_CMD_SNMP_GET_NEXT <span class="info"> CUPS 1.4 </span></dt>
+<dd class="description">Query the next SNMP OID </dd>
<dt>CUPS_SC_CMD_SOFT_RESET </dt>
<dd class="description">Do a soft reset</dd>
</dl>
break;
}
}
+ else if (!strcasecmp(line, "Attribute") && value)
+ {
+ for (valueptr = value; *valueptr && !isspace(*valueptr & 255); valueptr ++);
+
+ if (!*valueptr)
+ cupsdLogMessage(CUPSD_LOG_ERROR,
+ "Syntax error on line %d of printers.conf.", linenum);
+ else
+ {
+ for (; *valueptr && isspace(*valueptr & 255); *valueptr++ = '\0');
+
+ cupsdSetPrinterAttr(p, value, valueptr);
+
+ if (!strncmp(value, "marker-", 7))
+ p->marker_time = time(NULL);
+ }
+ }
else
{
/*
struct tm *curdate; /* Current date */
cups_option_t *option; /* Current option */
const char *ptr; /* Pointer into info/location */
+ ipp_attribute_t *marker; /* Current marker attribute */
/*
i --, option ++)
cupsFilePrintf(fp, "Option %s %s\n", option->name, option->value);
+ if ((marker = ippFindAttribute(printer->attrs, "marker-colors",
+ IPP_TAG_NAME)) != NULL)
+ {
+ cupsFilePrintf(fp, "Attribute %s %s", marker->name,
+ marker->values[0].string.text);
+ for (i = 1; i < marker->num_values; i ++)
+ cupsFilePrintf(fp, ",%s", marker->values[i].string.text);
+ cupsFilePuts(fp, "\n");
+ }
+
+ if ((marker = ippFindAttribute(printer->attrs, "marker-levels",
+ IPP_TAG_INTEGER)) != NULL)
+ {
+ cupsFilePrintf(fp, "Attribute %s %d", marker->name,
+ marker->values[0].integer);
+ for (i = 1; i < marker->num_values; i ++)
+ cupsFilePrintf(fp, ",%d", marker->values[i].integer);
+ cupsFilePuts(fp, "\n");
+ }
+
+ if ((marker = ippFindAttribute(printer->attrs, "marker-names",
+ IPP_TAG_NAME)) != NULL)
+ {
+ cupsFilePrintf(fp, "Attribute %s %s", marker->name,
+ marker->values[0].string.text);
+ for (i = 1; i < marker->num_values; i ++)
+ cupsFilePrintf(fp, ",%s", marker->values[i].string.text);
+ cupsFilePuts(fp, "\n");
+ }
+
+ if ((marker = ippFindAttribute(printer->attrs, "marker-types",
+ IPP_TAG_KEYWORD)) != NULL)
+ {
+ cupsFilePrintf(fp, "Attribute %s %s", marker->name,
+ marker->values[0].string.text);
+ for (i = 1; i < marker->num_values; i ++)
+ cupsFilePrintf(fp, ",%s", marker->values[i].string.text);
+ cupsFilePuts(fp, "\n");
+ }
+
cupsFilePuts(fp, "</Printer>\n");
#ifdef __sgi
value = ptr;
}
}
+
+ cupsdMarkDirty(CUPSD_DIRTY_PRINTERS);
}
p->type |= CUPS_PRINTER_COMMANDS;
}
+ else if (!(p->type & CUPS_PRINTER_COMMANDS))
+ {
+ /*
+ * See if this is a PostScript device without a command filter...
+ */
+
+ for (i = 0; i < ppd->num_filters; i ++)
+ if (!strncasecmp(ppd->filters[i],
+ "application/vnd.cups-postscript", 31))
+ break;
+
+ if (i < ppd->num_filters)
+ {
+ /*
+ * Add the generic PostScript command filter...
+ */
+
+ add_printer_filter(p, p->filetype,
+ "application/vnd.cups-command 0 commandtops");
+ p->type |= CUPS_PRINTER_COMMANDS;
+ }
+ }
if (p->type & CUPS_PRINTER_COMMANDS)
{